/APIXO Docs

Polling Strategies

Efficient techniques for checking task status

Polling Strategies

When using async mode, you need to poll the status endpoint to get results. This guide covers efficient polling techniques.

Basic Polling

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

Adaptive Polling

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');
}

Exponential Backoff

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

Concurrent Polling

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();

WebSocket Alternative

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.

Error Recovery

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');
}

Best Practices Summary

TechniqueUse CaseEfficiency
Fixed intervalSimple scriptsLow
AdaptiveProduction appsMedium
Exponential backoffUnknown durationsHigh
ConcurrentMultiple tasksHigh
WebhooksReal-time needsHighest

For production applications, we recommend using webhooks instead of polling when possible.

On this page