How to Poll for Generation Task Status in GPT-Image2: A Complete Implementation Guide

Poll for generation task status in GPT-Image2 by using the pollApimartTask helper in src/apimartClient.js, which repeatedly queries the server-side endpoint until the APIMart task reaches a terminal state of completed or failed.

The freestylefly/awesome-gpt-image-2 repository implements a robust polling mechanism for monitoring asynchronous image generation jobs. When you submit a generation request, the APIMart service returns a taskId that represents a long-running operation requiring active status checks until the image is ready or fails.

Architecture of the GPT-Image2 Polling System

The polling implementation splits responsibilities between server-side API routes and a reusable client-side utility.

Server-Side Status Endpoint (api/generation/status.js)

The backend exposes a GET endpoint that validates the taskId parameter and retrieves the current state from APIMart. According to the source code in api/generation/status.js, this route:

  • Validates the incoming taskId
  • Calls getApimartTask (from api/_lib/apimart.js) to fetch the live status
  • Returns the raw task object if pending, or stores and returns the final result if completed

When the task is still processing, the endpoint returns intermediate metadata; when finished, it persists the result locally before responding.

Client-Side Polling Engine (src/apimartClient.js)

The client library provides pollApimartTask, a generic poller implemented in src/apimartClient.js (lines 15-42). This function:

  • Accepts a fetchTask callback that retrieves the latest status
  • Checks isTerminalApimartStatus(task.status) to detect completion (returns true for "completed" or "failed")
  • Respects APIMart rate limits by reading the Retry-After header or error.retryAfterMs
  • Supports cancellation via AbortSignal
  • Enforces default limits of 120 attempts or 10 minutes (maxElapsedMs)

Step-by-Step Polling Workflow

Follow this sequence to monitor generation tasks from submission to completion:

  1. Submit the generation request to /api/generate-image, which creates an APIMart task and returns a taskId
  2. Initialize the poller by invoking pollApimartTask with a fetchTask wrapper that calls /api/generation/status?taskId=${taskId}
  3. Handle progress updates through the onProgress callback, which receives the task object on every poll interval
  4. Detect terminal states automatically—the poller stops when status equals "completed" or "failed" and returns the final task object
  5. Abort if necessary by passing an AbortSignal from AbortController to cancel polling during component unmount or user cancellation

Code Implementation Examples

React Component Implementation

Use the useGenerationStatus hook pattern to integrate polling into your frontend:

import { pollApimartTask } from '../apimartClient';

function useGenerationStatus(taskId) {
  const [status, setStatus] = React.useState(null);
  const [error, setError] = React.useState(null);
  const controller = React.useRef(new AbortController());

  React.useEffect(() => {
    async function startPolling() {
      try {
        const finalTask = await pollApimartTask(
          async () => {
            const res = await fetch(`/api/generation/status?taskId=${taskId}`);
            if (!res.ok) throw new Error('STATUS_FETCH_FAILED');
            return res.json();
          },
          {
            signal: controller.current.signal,
            onProgress: (task) => setStatus(task),
            maxAttempts: 200,
            maxElapsedMs: 15 * 60 * 1000,
            intervalMs: 3000
          }
        );

        setStatus(finalTask);
      } catch (e) {
        if (e.code !== 'APIMART_POLL_ABORTED') setError(e);
      }
    }

    startPolling();
    return () => controller.current.abort();
  }, [taskId]);

  return { status, error };
}

The AbortController ensures polling stops automatically when the component unmounts, preventing memory leaks and unnecessary network requests.

Node.js CLI Implementation

For server-side scripts or command-line interfaces, import the client directly:

import { pollApimartTask } from './src/apimartClient.js';
import fetch from 'node-fetch';

const taskId = process.argv[2];

(async () => {
  const final = await pollApimartTask(
    async () => {
      const res = await fetch(`http://localhost:3000/api/generation/status?taskId=${taskId}`);
      return res.json();
    },
    { 
      onProgress: (t) => console.log('progress →', t.status),
      intervalMs: 2000 
    }
  );

  console.log('final task:', final);
})();

Manual Polling Alternative

If you cannot use the helper, implement basic polling with setInterval, though this lacks rate-limit handling:

function pollStatusManually(taskId, onUpdate) {
  const interval = setInterval(async () => {
    const { ok, status } = await fetch(`/api/generation/status?taskId=${taskId}`)
      .then(r => r.json());

    if (!ok) {
      clearInterval(interval);
      throw new Error('Failed to get status');
    }

    onUpdate({ status });

    if (['completed', 'failed'].includes(status)) {
      clearInterval(interval);
    }
  }, 2000);
}

Warning: This manual approach does not respect Retry-After headers or handle APIMART_RATE_LIMITED errors, making pollApimartTask the recommended solution for production use.

Handling Rate Limits and Timeouts

The pollApimartTask function implements sophisticated error handling for APIMart's rate limiting:

  • Rate-limit detection: When the API returns APIMART_RATE_LIMITED, the poller extracts the delay from error.retryAfterMs or falls back to the configured intervalMs
  • Timeout protection: Set maxElapsedMs to prevent infinite polling (default: 600,000ms or 10 minutes)
  • Attempt limits: Configure maxAttempts to cap total requests (default: 120)
  • Cancellation: Pass an AbortSignal to immediately terminate the polling loop; the function throws APIMART_POLL_ABORTED when aborted

Summary

  • pollApimartTask in src/apimartClient.js is the core utility for polling generation status in GPT-Image2
  • The server-side endpoint api/generation/status.js validates task IDs and retrieves live status from APIMart
  • Terminal states are completed and failed, detected via isTerminalApimartStatus
  • Always use AbortSignal in React components to cancel polling on unmount
  • Default polling limits are 120 attempts or 10 minutes, configurable via the options parameter
  • The poller automatically handles Retry-After headers when APIMart rate limits are encountered

Frequently Asked Questions

How do I cancel an active polling operation in GPT-Image2?

Create an AbortController and pass its signal property to the pollApimartTask options. When you call controller.abort(), the poller immediately stops and throws an error with code APIMART_POLL_ABORTED. In React, invoke abort() inside the useEffect cleanup function to prevent state updates after unmounting.

What are the default polling limits in pollApimartTask?

By default, pollApimartTask stops after 120 attempts or 10 minutes (600,000ms), whichever comes first. You can override these constraints by passing maxAttempts and maxElapsedMs in the options object. Exceeding either limit causes the function to throw a timeout error.

How does the system handle APIMart rate limiting?

When APIMart returns a rate-limit response, pollApimartTask checks for the Retry-After header or the retryAfterMs property in the error object. The poller waits for the specified duration before retrying, preventing request throttling or account suspension. If no retry delay is specified, it falls back to the standard intervalMs.

What is the difference between the server-side and client-side polling components?

The server-side component (api/generation/status.js) acts as a proxy that validates the taskId and fetches the current state from APIMart using getApimartTask. The client-side component (src/apimartClient.js) provides the pollApimartTask orchestration logic, managing the retry loop, terminal state detection, rate-limit handling, and cancellation signals that govern how frequently the frontend checks the server endpoint.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →