Api18.dev

How it works: jobs & polling

Every generation runs asynchronously. You create a job, track its status, and download the file once it's ready.

The lifecycle#

1. You POST to the model's endpoint and get a job with status and id. 2. While it isn't done, you call GET /v1/jobs/{id} every few seconds. 3. When status becomes completed, the data array holds the output URLs.

Possible statuses: processing completed or failed. Cost is charged when the job is accepted; failed jobs are refunded automatically.

Response shape#

Both the POST and the GET return the same job object:

{
  "id": "abc123…",
  "object": "generation",
  "model": "<model-id>",
  "output_kind": "video" | "image",
  "status": "processing" | "completed" | "failed",
  "created_at": "ISO-8601",
  "completed_at": "ISO-8601 | null",
  "expires_at": "ISO-8601",
  "retention_days": 7,
  "error": "string | null",
  "cost_usd": 0.30,
  "input": { /* echo of your request */ },
  "timings": { "inference_ms": 1234 },
  "urls": { "get": "https://api18.dev/v1/jobs/{id}" },
  "data": [ { "url": "https://api18.dev/v1/files/{id}" } ]
}

Polling loop#

let job = /* response from the POST */;
while (job.status !== "completed" && job.status !== "failed") {
  await new Promise((r) => setTimeout(r, 3000)); // 3s
  job = await fetch("https://api18.dev/v1/jobs/" + job.id, {
    headers: { Authorization: `Bearer ${process.env.API18_KEY}` },
  }).then((r) => r.json());
}
if (job.status === "failed") throw new Error(job.error);
const urls = job.data.map((d) => d.url);

Downloading the files#

Each item in data points to /v1/files/{id}, which 302-redirects to the file. For multiple outputs, use ?n=1, ?n=2, etc.

Output URLs expire (see retention_days). Download and store the files if you need to keep them long-term.

See full examples in SDKs.

Jobs & polling — Api18.dev Docs