Skip to main content

Jobs API

Poll async generation jobs, list job history, cancel pending jobs, and handle webhook callbacks.

Async generations. Video and music generation are always async. Long-form voice may also be async. Use this API to poll for results, or provide a webhook_url when submitting the job.

GET /v1/jobs/:id

Check the status of an async generation job. Poll this endpoint until status is "succeeded" or "failed".

Path parameters

ParameterTypeDescription
idstringJob ID returned when you submitted the generation (e.g. job_abc123).

Response fields

FieldTypeDescription
job_idstringJob ID.
statusstring"queued", "running", "succeeded", "partial", or "failed".
modelstringModel key used for generation.
modalitystring"video", "music", or "voice".
resultobjectPresent only when succeeded/partial. Contains url, duration_sec, width, height, cost (USD float), cost_micro (integer microdollars), currency ("USD").
asset_idstring | nullAsset ID for the saved output (when succeeded).
tagsarrayAsset tags as { key, value } pairs (when succeeded).
errorstringSanitized error message (only when failed).
created_atstringISO 8601 timestamp of job creation.
completed_atstringISO 8601 timestamp of completion (when succeeded or failed).

Status values

StatusMeaningCost charged?
queuedJob is waiting to start.No
runningJob is actively running.No
succeededGeneration finished. result.url and result.cost_micro available.Yes
partialGeneration partially completed. result available.Yes
failedGeneration failed. error available.No

Code examples

curl

curl https://api.fairstack.ai/v1/jobs/job_abc123 \
  -H "Authorization: Bearer $FAIRSTACK_API_KEY"

Python (polling)

from fairstack import FairStack

client = FairStack(api_key="fs_live_YOUR_KEY")

# Submit a video generation job
job = client.generate.video(
    model="wan-2-1-t2v",
    prompt="A cat walking on a beach at sunset"
)

# Option 1: Block until complete
result = job.wait()
print(result.url)

# Option 2: Poll manually
import time
while True:
    status = client.jobs.get(job.id)
    if status.status == "succeeded":
        print(status.result.url)
        break
    elif status.status == "failed":
        print(status.error)
        break
    time.sleep(2)

Node.js (polling)

import { FairStack } from "fairstack";

const client = new FairStack({ apiKey: "fs_live_YOUR_KEY" });

// Submit a video generation job
const job = await client.generate.video({
  model: "wan-2-1-t2v",
  prompt: "A cat walking on a beach at sunset",
});

// Option 1: Block until complete
const result = await job.wait();
console.log(result.url);

// Option 2: Poll manually
const poll = async (jobId: string) => {
  while (true) {
    const status = await client.jobs.get(jobId);
    if (status.status === "succeeded") return status;
    if (status.status === "failed") throw new Error(status.error);
    await new Promise((r) => setTimeout(r, 2000));
  }
};

Queued response

{
  "job_id": "job_abc123",
  "status": "queued",
  "model": "wan-2-1-t2v",
  "modality": "video",
  "created_at": "2026-03-10T14:30:00Z"
}

Completed response

{
  "job_id": "job_abc123",
  "status": "succeeded",
  "model": "wan-2-1-t2v",
  "modality": "video",
  "created_at": "2026-03-10T14:30:00Z",
  "completed_at": "2026-03-10T14:30:42Z",
  "result": {
    "url": "https://media.fairstack.ai/video/.../output.mp4",
    "duration_sec": 5,
    "width": 1280,
    "height": 720,
    "cost": 0.048,
    "cost_micro": 48000,
    "currency": "USD"
  },
  "asset_id": "asset_def456",
  "tags": []
}

Failed response

{
  "job_id": "job_abc123",
  "status": "failed",
  "model": "wan-2-1-t2v",
  "modality": "video",
  "created_at": "2026-03-10T14:30:00Z",
  "completed_at": "2026-03-10T14:30:15Z",
  "error": "Content moderation filter triggered"
}

Cost units. result.cost_micro is an integer in microdollars; result.cost is the same value as a USD float; result.currency is "USD". The conversion is 1 credit = 1 cent = 10,000 microdollars (1 USD = 1,000,000 microdollars). So cost_micro: 30000 = $0.03 = 3 credits, and the example above (48000) = $0.048 = 4.8 credits.

GET /v1/jobs

List your recent generation jobs with optional filters.

Query parameters

ParameterTypeDescription
statusstringFilter by status: "queued", "running", "succeeded", "failed".
modalitystringFilter by modality: "video", "music", "voice".
limitintegerMax results to return (1-100). Default: 20.
cursorstringPagination cursor from a previous response.
curl "https://api.fairstack.ai/v1/jobs?status=completed&limit=10" \
  -H "Authorization: Bearer $FAIRSTACK_API_KEY"
{
  "jobs": [
    { "job_id": "job_abc123", "status": "succeeded", "model": "wan-2-1-t2v", "modality": "video", "created_at": "..." },
    { "job_id": "job_def456", "status": "succeeded", "model": "suno-v4-5", "modality": "music", "created_at": "..." }
  ],
  "has_more": true,
  "cursor": "cursor_xyz"
}

POST /v1/jobs/:id/cancel

Cancel a queued or processing job. No charge for cancelled jobs.

curl -X POST https://api.fairstack.ai/v1/jobs/job_abc123/cancel \
  -H "Authorization: Bearer $FAIRSTACK_API_KEY"

Returns the job object with status: "cancelled". Jobs that have already completed or failed cannot be cancelled.

Webhooks

Instead of polling, provide a webhook_url when submitting a generation. FairStack will POST the result to your URL when the job completes or fails.

// POST to your webhook_url when the generation completes:
{
  "event": "generation.completed",
  "data": {
    "id": "gen_abc123",
    "type": "video",
    "status": "succeeded",
    "model": "wan-2-1-t2v",
    "output": { "url": "https://media.fairstack.ai/video/.../output.mp4", "width": 1280, "height": 720, "duration_sec": 5 },
    "cost": { "cost_micro": 48000, "cost_usd": 0.048, "currency": "USD" },
    "error": null,
    "created_at": "2026-03-10T14:30:00Z",
    "completed_at": "2026-03-10T14:30:42Z"
  },
  "created_at": "2026-03-10T14:30:42Z"
}

Webhook requests include an X-FairStack-Signature header for verification. Respond with a 2xx status. Failed deliveries are retried up to 3 times with exponential backoff.

Polling best practices

  • Start polling 2-3 seconds after submission.
  • Use a 2-5 second interval between polls.
  • Set a timeout (e.g. 5 minutes) to avoid infinite polling.
  • For production: prefer webhooks over polling to reduce API calls.
  • The SDK .wait() method handles polling with automatic backoff.

Next steps