API

Build with the API and MCP

Make product images, video, voiceovers, music and finished ads from your own code or from an AI agent. One balance pays for the studio, the REST API and the MCP server.

Introduction

Everything you make in the studio you can make from code: product images, video clips, voiceovers, music and complete ads with review steps. Every job has a free price check first, and a job that fails is refunded automatically.

Requests and answers are JSON, except uploads, which are multipart form data. Every path starts with /api/v1, the current version.

Base URLhttps://staglight.ai/api/v1
AuthBearer <key>
FormatJSON
OpenAPI/api/v1/openapi.json

Get an API key in under a minute

Sign up, confirm your email, then open API keys in the studio and create a key. It is shown once, so copy it right away. You can give it a daily credit cap.

Get an API key

Authentication

Create a key in the studio and give it a daily credit cap. The key is shown once. Keys start with sl_live_.

Send it on every request as Authorization: Bearer <key>. X-API-Key: <key> also works. The examples read it from STAGLIGHT_API_KEY.

Terminal
export STAGLIGHT_API_KEY="sl_live_..."

Never put a key in front-end code, a repo or a chat. If one leaks, revoke it in the studio.

Lost your API key?

Keys are stored only as a hash, so nobody can show you an existing key again, not even our support. Replace it yourself: sign in, open API keys, create a new key, then revoke the old one.

Your keys work side by side (up to 20), so you can switch without downtime: create the new key, put it in your app, then revoke the old one. A revoked key stops working at once.

Credits

1 credit = US$0.01. Every job has a price before it runs, and that price is charged when the job is created.

  • Failed jobs are refunded automatically, in full.
  • Canceling a job before it starts refunds it.
  • Purchased credits never expire.
  • No subscription and no API surcharge: the API and MCP use the same prices as the studio.
  • A key with a daily cap stops at the cap with 402 daily_limit_reached.

Check the balance with GET /api/v1/me, or with its history at GET /api/v1/credits. Buy credits in the studio.

Rate limits

Limits are counted per API key over a sliding minute (per account for signed-in browser sessions). All calls in a row share one count.

LimitCounted perApplies to
30 per minuteAPI keyCalls that create: POST /api/v1/characters, DELETE /api/v1/characters/{character_id}, PATCH /api/v1/characters/{character_id}, POST /api/v1/characters/{character_id}/sheet, POST /api/v1/formats/{format_id}/series, DELETE /api/v1/formats/{format_id}/series/{series_id}, PATCH /api/v1/formats/{format_id}/series/{series_id}, POST /api/v1/generate/image, POST /api/v1/generate/video, POST /api/v1/jobs, POST /api/v1/products, POST /api/v1/runs, POST /api/v1/runs/from-analysis, POST /api/v1/runs/propose, POST /api/v1/runs/{run_id}/approve, POST /api/v1/runs/{run_id}/redo, POST /api/v1/runs/{run_id}/scenes, POST /api/v1/scripts, POST /api/v1/scripts/{job_id}/make, PUT /api/v1/studio/library, POST /api/v1/studio/{studio_id}/helpers/{name}, POST /api/v1/studio/{studio_id}/helpers/{name}/cancel, POST /api/v1/studio/{studio_id}/runs, POST /api/v1/studio/{studio_id}/series, DELETE /api/v1/studio/{studio_id}/series/{series_id}, PATCH /api/v1/studio/{studio_id}/series/{series_id}, DELETE /api/v1/studio/{studio_id}/user-cards/{card_id}, POST /api/v1/uploads, POST /api/v1/uploads/from-library, POST /api/v1/uploads/from-url
120 per minuteAPI keyEvery other call with a key (41 endpoints)
60 per minuteIP addressCalls without a valid key
  • Over the limit you get 429 rate_limited with retry_after and a Retry-After header.
  • Long polls (wait) hold the request up to 50 s.
  • Uploads: images up to 20 MB, video and audio up to 1024 MB.

The MCP server counts the same way: paid tools 30 per minute, free tools 120 per minute.

Conventions

The same rules hold on every endpoint.

Errors

Every error has the same shape. code is stable; message is for people.

JSON
{
  "error": {
    "code": "insufficient_credits",
    "message": "This needs 63 credits and the balance is 10. Buy credits to continue.",
    "needed": 63,
    "balance": 10
  }
}
StatusCodeWhat to do
400invalid_idempotency_keyUse 1 to 255 visible ASCII characters, for example a UUID.
401unauthorizedMissing, wrong or revoked key.
402insufficient_creditsComes with needed and balance. Nothing was charged. Buy credits, then retry.
402daily_limit_reachedThis key reached its daily cap. Raise the cap in the studio or wait.
403csrf_failedBrowser sessions only. API keys never see it.
404not_foundWrong id, or it belongs to another account.
409price_above_maxThe price is above your max_credits. Nothing was charged.
409idempotency_mismatchThis key was used with a different body. Use a new key.
409idempotency_in_progressThe first request with this key is still running. Retry in a second.
413file_too_largeImages up to 20 MB, video and audio up to 1024 MB.
415unsupported_mediaUse JPG, PNG, WEBP, GIF, MP4, MOV, WEBM, MP3, WAV or M4A.
422invalid_requestCheck field and message.
422content_not_allowedThe request asks for content we don't make. See the terms.
429rate_limitedWait retry_after seconds (also in Retry-After).
503service_unavailableGeneration is paused for a few minutes. Nothing was charged.

Retries and idempotency

Send an Idempotency-Key header (a UUID) on every POST that spends credits.

  • Same key and same body within 24 h: you get the first response again, with no new charge, and the header Idempotent-Replayed: true.
  • Same key with a different body: 409 idempotency_mismatch.
  • First request still running: 409 idempotency_in_progress. Retry in a second.
  • A request that ended in an error is forgotten, so you can retry it with the same key.

After a timeout, don't resend blindly. Look the job up first: GET /api/v1/jobs?idempotency_key=<key>.

Cap the price with max_credits in the body. Combined with the key, a retry can never charge twice or charge more than you allowed.

Pagination

Lists take limit (default 20, at most 100) and answer with next. Pass it as before to get the next page. On the last page next is null.

Lists that page this way: GET /api/v1/characters, GET /api/v1/credits, GET /api/v1/jobs, GET /api/v1/media, GET /api/v1/products, GET /api/v1/runs, GET /api/v1/studio/{studio_id}/runs.

Your first job

Generation is asynchronous. These examples use curl and read your key from STAGLIGHT_API_KEY.

Upload a product photo

Terminal
curl -s -X POST https://staglight.ai/api/v1/uploads \
  -H "Authorization: Bearer $STAGLIGHT_API_KEY" \
  -F "file=@product.jpg"

The answer is a media object. Use its id (med_...) in job params. Images are re-encoded, which removes EXIF and GPS data. For a public link use POST /api/v1/uploads/from-url.

Get the price (free)

Terminal
curl -s -X POST https://staglight.ai/api/v1/estimate \
  -H "Authorization: Bearer $STAGLIGHT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"op": "image.nano-banana-pro", "params": {"prompt": "The red sneaker on white paper backdrop, soft morning light", "reference_images": ["med_19a2b3c4d5e6f7a8b9c0d1"], "aspect_ratio": "4:5"}}'

Same op and params as the job. You get credits, usd, eta_seconds, your balance and enough_credits. Nothing is charged. GET /api/v1/models lists every op with its fields.

Create the job

Terminal
curl -s -X POST https://staglight.ai/api/v1/jobs \
  -H "Authorization: Bearer $STAGLIGHT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"op": "image.nano-banana-pro", "params": {"prompt": "The red sneaker on white paper backdrop, soft morning light", "reference_images": ["med_19a2b3c4d5e6f7a8b9c0d1"], "aspect_ratio": "4:5"}, "max_credits": 40}'

The answer is 202 with the job and a Location header. The credits are charged now. If the price is above max_credits you get 409 price_above_max and nothing is charged.

Poll until it finishes

Terminal
curl -s "https://staglight.ai/api/v1/jobs/job_19a2b3c4d5e6f7a8b9c0d1?wait=30" \
  -H "Authorization: Bearer $STAGLIGHT_API_KEY"

wait=30 holds the request until the job finishes (up to 50 s). Statuses: queued, running, succeeded, failed (refunded), canceled (refunded).

JSON
{
  "id": "job_19a2b3c4d5e6f7a8b9c0d1",
  "op": "image.nano-banana-pro",
  "status": "succeeded",
  "credits": 23,
  "outputs": [
    {
      "id": "med_19a2b3c4d5e6f7a8b9c0d2",
      "kind": "image",
      "mime": "image/png",
      "width": 1664,
      "height": 2080,
      "url": "https://staglight.ai/media/med_19a2b3c4d5e6f7a8b9c0d2/image.png?exp=...&sig=...",
      "download_url": "https://staglight.ai/media/med_19a2b3c4d5e6f7a8b9c0d2/image.png?exp=...&sig=...&dl=1"
    }
  ],
  "error": null
}

Download the result

Terminal
curl -L -o product-ad.png "<outputs[0].download_url>"

Links are signed and last about 24 h, with no auth header needed. Get the job again for fresh links. Files are kept 30 days.

Complete ads (formats)

A format run makes a finished ad in steps: script, keyframes, clips, voice, music and the final edit. It pauses at review steps so you can approve or redo a take. GET /api/v1/formats lists them with prices per tier.

Terminal
curl -s -X POST https://staglight.ai/api/v1/runs \
  -H "Authorization: Bearer $STAGLIGHT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"format": "product_hero", "product_id": "prd_19a2b3c4d5e6f7a8b9c0d1", "options": {"tier": "draft", "aspect": "9:16", "lang": "en"}}'

Poll GET /api/v1/runs/<id>?wait=30. When status is awaiting_approval, approve with POST /api/v1/runs/<id>/approve or make a new take with /redo. Only the plan step is charged at the start; each approval charges the steps it starts.

Webhooks

There are no webhooks yet. Poll with wait=30: it returns as soon as the job finishes.

Next

This page as Markdown · llms.txt