Polling Strategies
Efficient techniques for checking task status
When using async mode, you need to poll the status endpoint to get results. This guide covers efficient polling techniques.
The simplest approach: poll at fixed intervals.
async function basicPoll(model, taskId, apiKey) {
const interval = 3000; // 3 seconds
const maxAttempts = 60;
for (let i = 0; i < maxAttempts; i++) {
const { data } = await checkStatus(model, taskId, apiKey);
if (data.state === 'success' || data.state === 'failed') {
return data;
}
await sleep(interval);
}
throw new Error('Timeout');
}
Pros: Simple to implement
Cons: Not optimal for varying generation times
Adjust intervals based on model type and elapsed time.
function getPollingConfig(model) {
const configs = {
// Fast image models
'nano-banana': { initial: 5000, interval: 3000, max: 120000 },
'flux-2': { initial: 10000, interval: 5000, max: 180000 },
// Video models (slower)
'sora-2': { initial: 60000, interval: 15000, max: 600000 },
'veo-3-1': { initial: 45000, interval: 10000, max: 480000 },
// Audio models
'suno': { initial: 30000, interval: 10000, max: 300000 },
};
return configs[model] || { initial: 10000, interval: 5000, max: 300000 };
}
async function adaptivePoll(model, taskId, apiKey) {
const config = getPollingConfig(model);
let elapsed = 0;
// Initial wait
await sleep(config.initial);
elapsed += config.initial;
while (elapsed < config.max) {
const { data } = await checkStatus(model, taskId, apiKey);
if (data.state === 'success' || data.state === 'failed') {
return data;
}
await sleep(config.interval);
elapsed += config.interval;
}
throw new Error('Timeout');
}
Start with frequent polls, then gradually slow down.
async function exponentialBackoffPoll(model, taskId, apiKey) {
let interval = 2000; // Start at 2s
const multiplier = 1.5;
const maxInterval = 30000;
const maxTime = 600000; // 10 minutes
let elapsed = 0;
while (elapsed < maxTime) {
const { data } = await checkStatus(model, taskId, apiKey);
if (data.state === 'success' || data.state === 'failed') {
return data;
}
await sleep(interval);
elapsed += interval;
interval = Math.min(interval * multiplier, maxInterval);
}
throw new Error('Timeout');
}
Best for: Unknown or variable processing times
Poll multiple tasks efficiently.
class TaskPoller {
constructor(apiKey) {
this.apiKey = apiKey;
this.tasks = new Map();
}
add(model, taskId, callback) {
this.tasks.set(taskId, { model, callback, attempts: 0 });
}
async start() {
while (this.tasks.size > 0) {
const checks = Array.from(this.tasks.entries()).map(
async ([taskId, { model, callback, attempts }]) => {
const { data } = await checkStatus(model, taskId, this.apiKey);
if (data.state === 'success') {
callback(null, JSON.parse(data.resultJson).resultUrls);
this.tasks.delete(taskId);
} else if (data.state === 'failed') {
callback(new Error(data.failMsg));
this.tasks.delete(taskId);
} else if (attempts > 60) {
callback(new Error('Timeout'));
this.tasks.delete(taskId);
} else {
this.tasks.set(taskId, { model, callback, attempts: attempts + 1 });
}
}
);
await Promise.all(checks);
if (this.tasks.size > 0) {
await sleep(3000);
}
}
}
}
// Usage
const poller = new TaskPoller(apiKey);
poller.add('nano-banana', taskId1, (err, urls) => {
if (err) console.error(err);
else console.log('Task 1:', urls);
});
poller.add('flux-2', taskId2, (err, urls) => {
if (err) console.error(err);
else console.log('Task 2:', urls);
});
await poller.start();
For real-time updates without polling, consider using webhooks instead:
// Instead of polling, use callback mode
const response = await fetch(`${API_URL}/generateTask/${model}`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${apiKey}` },
body: JSON.stringify({
request_type: 'callback',
callback_url: 'https://your-server.com/webhook',
input: { prompt: '...' },
}),
});
// Your webhook receives results automatically
See Webhooks for setup details.
Handle network failures during polling:
async function resilientPoll(model, taskId, apiKey) {
const maxNetworkRetries = 3;
let interval = 3000;
let elapsed = 0;
const maxTime = 300000;
while (elapsed < maxTime) {
let networkRetries = 0;
while (networkRetries < maxNetworkRetries) {
try {
const { data } = await checkStatus(model, taskId, apiKey);
if (data.state === 'success') {
return JSON.parse(data.resultJson).resultUrls;
}
if (data.state === 'failed') {
throw new Error(`${data.failCode}: ${data.failMsg}`);
}
break; // Success, exit retry loop
} catch (error) {
if (error.message.includes('network') || error.code === 'ECONNRESET') {
networkRetries++;
await sleep(1000 * networkRetries);
continue;
}
throw error; // Non-network error, rethrow
}
}
await sleep(interval);
elapsed += interval;
}
throw new Error('Timeout');
}
| Technique | Use Case | Efficiency |
|---|
| Fixed interval | Simple scripts | Low |
| Adaptive | Production apps | Medium |
| Exponential backoff | Unknown durations | High |
| Concurrent | Multiple tasks | High |
| Webhooks | Real-time needs | Highest |
For production applications, we recommend using webhooks instead of polling when possible.