VidLabDocsDashboard
QuickstartMarkdown

Submit a generation

POST /v1/generations answers at once with an id and the status queued. The work runs in the background: poll the id, then download the file. This is the whole flow.

KEY=$(uuidgen)

ID=$(curl -s -X POST https://vidlab.ai/v1/generations \
  -H "Authorization: Bearer $VIDLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $KEY" \
  -d '{
    "model": "seedance-2.5",
    "input": {
      "prompt": "A slow push-in on a perfume bottle on wet black stone",
      "resolution": "720p",
      "duration": 5,
      "aspect_ratio": "9:16"
    },
    "max_cost_usd": 1.50
  }' | jq -r .data.id)

# Poll until it finishes. A failed one is free; to retry, make a new key.
while true; do
  STATUS=$(curl -s https://vidlab.ai/v1/generations/$ID \
    -H "Authorization: Bearer $VIDLAB_API_KEY" | jq -r .data.status)
  echo "$STATUS"
  case "$STATUS" in succeeded|failed) break ;; esac
  sleep 5
done

curl -sL https://vidlab.ai/v1/generations/$ID/content \
  -H "Authorization: Bearer $VIDLAB_API_KEY" -o out.mp4

The answer is 202 with the queued generation:

json
{
  "data": {
    "id": "gen_4Fq9xT2mLr7Kd1sV8bN3pZ0c",
    "object": "generation",
    "status": "queued",
    "model": "seedance-2.5",
    "variant": "720p",
    "units": 5,
    "price_usd": 1.05,
    "outputs": [],
    "created_at": "2026-11-01T18:04:11.000Z",
    "status_url": "/v1/generations/gen_4Fq9xT2mLr7Kd1sV8bN3pZ0c",
    "result_url": "/v1/jobs/gen_4Fq9xT2mLr7Kd1sV8bN3pZ0c/result"
  }
}
FieldTypeRequiredWhat it is
modelstringYesA model id from GET /v1/models.
inputobjectYesThe model's inputs, see Input fields. A field the model does not list is refused, never ignored.
max_cost_usdnumberNoThe most you will pay. A route dearer than this is skipped; if none is cheaper the call answers 422 max_cost_exceeded and nothing is held. Quote the exact request first and pass the quote.
max_wait_snumberNoThe longest you will wait, in seconds. A route expected to take longer is skipped, and no new route is tried once the time has passed. If no route fits, the call answers 422 max_wait_exceeded and nothing is held.
routestringNoauto (default) picks the cheapest healthy route and fails over on its own. route-1, route-2 or route-3 starts from that place in the ranking.
HeaderRequiredWhat it is
AuthorizationYesBearer vidlab_sk_...
Idempotency-KeyRecommended8 to 100 characters of letters, digits and _ : . -; a UUID works. The same key returns the same generation and never holds or charges twice. A failed generation is final: to retry, send a new key. See Rate limits.

What happens when you submit

  1. The body is checked against the model's options and priced. A problem answers 400 or 422 at once and nothing is held.
  2. The price is reserved on your balance. Without room for it the call answers 429 api_wallet_exhausted.
  3. The generation is stored as queued and the answer is 202.

The answer carries x-wallet-remaining-cents (what you can still spend after the reservation) and x-wallet-resets-at. These routes answer the same as POST /v1/generations: POST /v1/video-router/generate and POST /v1/image-router/generate.