VidLabDocsDashboard
GuidesMarkdown

Rate limits

Limits apply to calls that start work. Reading the model list is limited per address; polling never counts against your plan.

Starting generations

Each POST /v1/generations counts against three limits at once:

AccountPer key, per minutePer account, per minutePer account, per day
No plan20402,500
Any paid plan6012010,000
Paid plan with $100 or more of top-up credit12024025,000
  • A top-up balance of $100 or more moves an account up one row, with or without a plan.
  • Minutes are fixed windows; the day resets at midnight UTC.
  • A retry that returns an existing generation (same Idempotency-Key) does not count.

Viral video library

Calls to the viral video library also count against the library's own limits, per account per minute: 20 searches (the trending feed included), 40 full-video reads and 30 saves.

What never counts

  • Polling and reading: GET /v1/generations/{id}, the /v1/jobs/... aliases, /content and GET /v1/generations.
  • Requests refused before they reach the limiter (bad input, unknown model).

One safety ceiling still applies to reads: 1,200 a minute per key (20 a second), across GET /v1/generations/{id}, the /v1/jobs/... aliases, /content, GET /v1/generations and GET /v1/balance. Past it, reads answer 429 read_rate_limited with Retry-After. Polling each generation every few seconds stays far below it; to watch many at once, read them together with GET /v1/generations.

Public endpoints

Counted per IP address, per minute:

EndpointsLimit
GET /v1/models, GET /v1/models/{id}, GET /v1/status, GET /v1/open/catalog, GET /v1/balance120
POST /v1/estimate, POST /v1/open/estimate60
POST /v1/generations/quote120

Past the limit they answer 429 rate_limited with Retry-After. Model list answers can be cached for 60 seconds (status for 30), so you rarely need to ask more often.

The 429 answer

A submit over its limit answers 429 with the limit that was hit, what was counted, and when it resets:

json
{
  "error": {
    "message": "Request limit reached for this API key (61/60 requests this minute on the Base plan). Resets at 2026-11-01T18:05:00.000Z. Job status/result polling never counts against rate limits.",
    "type": "rate_limit_error",
    "code": "request_per_minute"
  },
  "limits": { "per_minute": 60, "per_user_per_minute": 120, "per_day": 10000 },
  "observed": { "minute_count": 61, "user_minute_count": 61, "day_count": 812 },
  "plan": "base",
  "reset_at": "2026-11-01T18:05:00.000Z"
}

Wait the number of seconds in the Retry-After header, then retry with the same Idempotency-Key.

Idempotency

Send an Idempotency-Key header on every POST /v1/generations. If the answer is lost (a timeout, a dropped connection), send the exact same request again with the same key:

  • The first call's generation comes back, with its current status. Nothing is started, held or charged a second time.
  • A key belongs to your account and stays tied to the first generation it made. A later call with the same key returns that generation even if the body is different, so use a new key for new work.
  • Two calls with the same key at the same moment still make only one generation.
  • Format: 8 to 100 characters of letters, digits and _ : . -. A UUID works well. Anything else answers 400 invalid_idempotency_key.
curl -X POST https://vidlab.ai/v1/generations \
  -H "Authorization: Bearer $VIDLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a9e-3b4d-4e8f-9a0b-1c2d3e4f5a6b" \
  -d '{"model": "gpt-image-2", "input": {"prompt": "A red sneaker on white"}}'