How APIMart Handles Image Generation Tasks in awesome-gpt-image-2

APIMart serves as the backend provider for image generation in awesome-gpt-image-2, orchestrating tasks through a three-layer architecture that separates shared utilities, server-side API handling, and client-side task management.

The awesome-gpt-image-2 repository by freestylefly integrates APIMart to power GPT-image-2 model generation through a robust TypeScript/JavaScript implementation. This architecture enables both platform-mediated and direct personal API key workflows while handling task lifecycle management, caching, and comprehensive error translation.

Three-Layer Architecture

The integration divides responsibilities across shared helpers, server middleware, and browser clients to ensure consistent behavior across environments.

Shared Utilities Layer

The foundation resides in shared/apimart.js, which exports pure functions usable in both Node.js and browser contexts. This module defines constants, payload builders, response normalizers, and pricing utilities that remain agnostic to the execution environment.

Server-Side API Layer

Located in api/_lib/apimart.js and consumed by api/generate-image.js, this layer manages secure communication with APIMart. It reads the APIMART_API_KEY environment variable, submits generation requests to https://api.apimart.ai/v1/images/generations, and stores task IDs in Supabase. The server returns 202 Accepted responses to clients and translates upstream errors into semantic public codes like UPSTREAM_BUSY or SERVER_NOT_CONFIGURED.

Client-Side Wrapper

The browser implementation in src/apimartClient.js offers dual operation modes: personal key (direct APIMart calls) and platform key (proxied through the server API). It persists API keys and task states in localStorage using keys gpt-image-2-apimart-key:v1, gpt-image-2-pending-tests:v1, and gpt-image-2-generated-tests:v1.

The Generation Workflow

Understanding how APIMart handles image generation tasks requires examining the request lifecycle from submission to completion.

Configuration and Initialization

The server determines APIMart availability through getApimartConfig() in api/_lib/apimart.js, which checks for the presence of process.env.APIMART_API_KEY. This boolean flag dictates whether the provider appears as "configured" in health checks.

Submitting Generation Tasks

Platform Mode (Server-Proxied):

  1. The client POSTs to /api/generate-image with a payload containing caseId, prompt, and language.
  2. The handler validates the prompt and reserves a usage slot in Supabase.
  3. It invokes submitApimartGeneration(), which calls buildApimartGenerationPayload() to construct the request body.
  4. The function POSTs to APIMart's /v1/images/generations endpoint.
  5. extractApimartTaskId() normalizes the response, extracting the task identifier for storage in the generation_reservations table.

Personal Mode (Direct Browser):

Users with their own APIMart credentials bypass the server entirely. The submitPersonalGeneration(prompt, apiKey, language) function in src/apimartClient.js constructs and submits the identical payload directly from the browser to the APIMart API.

Task Status Polling

Both modes utilize normalizeApimartTask() to convert APIMart's response format into a unified shape: { taskId, status, progress, image, expiresAt, cost, ... }.

  • Platform polling uses fetchPlatformTask(taskId, accessToken, language), which queries /api/generation/status and internally forwards to getApimartTask().
  • Personal polling uses fetchPersonalTask(taskId, apiKey, language) to call GET https://api.apimart.ai/v1/tasks/<taskId> directly.

The pollApimartTask(fetchTask, options) utility manages the polling lifecycle, repeatedly invoking the fetch function until isTerminalApimartStatus(status) returns true (indicating completed or failed status). It respects Retry-After headers when encountering APIMART_RATE_LIMITED errors and supports cancellation via AbortSignal.

Error Handling and Rate Limiting

The implementation provides granular error translation through the apimartErrorCode() helper, which maps HTTP status codes to specific semantic identifiers:

  • APIMART_API_KEY_INVALID for authentication failures
  • APIMART_BALANCE_REQUIRED for insufficient credits
  • APIMART_RATE_LIMITED for throttling scenarios

Server-side errors are wrapped with upstreamError(), while client-side errors use responseError(). The publicErrorCode() function further maps these internal codes to public-facing messages suitable for API consumers, translating to appropriate HTTP status codes such as 503 for upstream busy states or 500 for internal failures.

Practical Implementation Examples

Submitting a Generation via Platform API

// POST /api/generate-image
await fetch('/api/generate-image', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ 
    caseId: 123, 
    prompt: 'A cyberpunk city at sunrise', 
    language: 'en' 
  })
});

The server handler in api/generate-image.js processes this request and internally calls submitApimartGeneration() (defined at line 44 in api/_lib/apimart.js) to communicate with APIMart.

Direct Generation with Personal API Key

import { submitPersonalGeneration, getStoredApimartKey } from './apimartClient.js';

const apiKey = getStoredApimartKey();  // reads from localStorage
const result = await submitPersonalGeneration(
  'A futuristic robot painting', 
  apiKey, 
  'en'
);
// result => { taskId: 'task_abc123…', status: 'submitted' }

Polling for Task Completion

import { pollApimartTask, fetchPersonalTask } from './apimartClient.js';

const apiKey = getStoredApimartKey();
const taskId = 'task_…';

await pollApimartTask(
  () => fetchPersonalTask(taskId, apiKey, 'en'),
  {
    onProgress: t => console.log('Progress', t.progress),
    maxAttempts: 100,
    intervalMs: 2000
  }
);
// Returns normalized task object when status becomes 'completed' or 'failed'

Retrieving Cached Results

import { getSavedGeneration } from './apimartClient.js';

const saved = getSavedGeneration(123); // caseId = 123
if (saved) {
  console.log('Cached image URL:', saved.image);
}

Server-Side Error Translation

import { publicErrorCode, publicErrorStatus } from './api/generate-image.js';

try {
  // ... generation logic
} catch (err) {
  const code = publicErrorCode(err);
  const status = publicErrorStatus(code);
  res.status(status).json({ ok: false, error: code });
}

Summary

  • APIMart integration in awesome-gpt-image-2 uses a three-tier architecture separating shared utilities (shared/apimart.js), server handlers (api/_lib/apimart.js, api/generate-image.js), and browser clients (src/apimartClient.js).
  • Dual operation modes support both platform-mediated requests (using server-stored API keys) and personal key workflows (direct browser-to-APIMart communication).
  • Task lifecycle management includes submission via submitApimartGeneration() or submitPersonalGeneration(), polling through pollApimartTask(), and normalization via normalizeApimartTask().
  • Robust error handling maps upstream APIMart errors to semantic codes like APIMART_RATE_LIMITED and APIMART_API_KEY_INVALID, with appropriate HTTP status code translation for API consumers.
  • Local persistence stores API keys and generation history in localStorage using version-prefixed keys to maintain state across sessions.

Frequently Asked Questions

What environment variables are required to configure APIMart in awesome-gpt-image-2?

The server-side integration requires APIMART_API_KEY to be set in the environment. The getApimartConfig() function in api/_lib/apimart.js checks for this variable to determine if the provider is properly configured before attempting any upstream requests.

How does the system handle APIMart rate limits?

When APIMart returns rate limit errors, the client detects APIMART_RATE_LIMITED codes and respects the Retry-After header. The pollApimartTask() utility automatically implements backoff strategies, while the server translates these into 503 status codes with appropriate retry guidance for API consumers.

Can users use their own APIMart API keys instead of the platform's?

Yes. The src/apimartClient.js module supports a personal key mode through submitPersonalGeneration() and fetchPersonalTask(), allowing users to store their API key in localStorage (under gpt-image-2-apimart-key:v1) and make direct requests to https://api.apimart.ai/v1/images/generations without routing through the platform's server infrastructure.

Where are generation tasks stored during processing?

Platform-generated tasks are persisted in Supabase within the generation_reservations table, allowing the system to track usage and task states across distributed workers. Client-side tasks and results are cached in localStorage using keys gpt-image-2-pending-tests:v1 and gpt-image-2-generated-tests:v1 for immediate retrieval without repeated API calls.

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 →