Bản lưu ngày / Snapshot of 2026-10-18 · Xem bản mới nhất / See the latest version
WanduraDocsBeta
BetaThese docs are in beta and change often. Something wrong, unclear or missing? Tell us.
Image and video API (v2)

Queue and congestion

Markdown
On this page

TL;DR — Submit answers tell you how many of your runs are running and waiting, and your limit. GET /v2/queue shows the same numbers live. GET /v2/queue/log gives them per minute or per hour, GET /v2/queue/refusals lists refused submits, and GET /v2/queue/episodes lists congestion episodes. Subscribe to queue.congestion_started and queue.congestion_ended to switch to your fallback early.

You only ever see your own product's numbers. Test keys and live keys count apart.

Words

Word Meaning
Concurrency slots How many runs of your product run at once. The default is 20; Wandura raises it per product on request.
Queue Priority runs that were accepted and wait for a slot. It holds as many runs as you have slots.
Refusal A submit answered 429 because your product was at its limits. The reason is product_busy (slots and queue full) or rate_limited (over the per-minute run limit, answered gateway_rate_limited). No run was made and nothing was charged.
Congestion episode A stretch of time your product was in one kind of congestion: refusing, near_full or slow. It has a start and an end.

A per-end-user daily cap (end_user_cap_reached) is not a refusal: it is about one person, not your product.

Headers on every submit

Answers of POST /v2/styles/{style}, POST /v2/runs and POST /v2/jobs carry three headers: accepted, replayed and refused submits alike.

Header Meaning
X-Concurrency-Limit Your concurrency slots. Your queue holds as many again.
X-Concurrency-Running Runs of your product using a slot right now.
X-Concurrency-Waiting Priority runs of your product waiting for a slot.
HTTP/1.1 202
X-Concurrency-Limit: 20
X-Concurrency-Running: 20
X-Concurrency-Waiting: 6

When Running and Waiting both reach Limit, a new priority run is refused: 429 product_busy (Jobs API: PRODUCT_BUSY) with Retry-After: 5. Relax runs are never refused for this; they wait.

  • A submit refused before it reaches your limits (a bad body, an unknown style, a bad key) has no such headers.
  • Treat the headers as optional: if the numbers cannot be read for a moment, the answer comes without them.

Live queue

GET /v2/queue returns your product's numbers right now, for the product and mode of your key.

curl https://api.wandura.ai/v2/queue -H "Authorization: Bearer $WANDURA_KEY"
{
  "object": "queue_state",
  "product": "prod_…",
  "livemode": true,
  "concurrency_limit": 20,
  "running": 20,
  "waiting": 6,
  "queue_limit": 20,
  "accepted_this_minute": 41,
  "per_minute_limit": 60,
  "refusals": { "last_15m": 0, "last_24h": 3 },
  "at": "2026-10-15T09:30:12.000Z"
}
Field Meaning
concurrency_limit Your concurrency slots.
running Runs using a slot now.
waiting Priority runs waiting for a slot now.
queue_limit How many priority runs may wait. The same number as your slots.
per_minute_limit Runs per minute for gateway keys and the Jobs API. Over it: 429 gateway_rate_limited (Jobs API: RATE_LIMITED).
accepted_this_minute Runs counted against that limit since the start of the current UTC minute.
refusals Refused submits in the last 15 minutes and the last 24 hours.

Queue log

GET /v2/queue/log returns your numbers over time, one bucket per minute or per hour, oldest first.

Parameter Meaning
granularity minute (default) or hour.
from Start, included. A time inside a bucket includes that bucket. Default: 24 hours before to.
to End, excluded. Default: now.
  • A range spans at most 7 days in minutes and 90 days in hours. Minutes are kept 7 days, hours 90 days.
  • Buckets with no activity are left out.
  • A minute shows up about a minute after it ends. The current hour fills as it goes.
  • A wrong value is 400 invalid_request, and the message says which.
curl "https://api.wandura.ai/v2/queue/log?granularity=minute&from=2026-10-15T09:00:00Z" \
  -H "Authorization: Bearer $WANDURA_KEY"
{
  "object": "list",
  "product": "prod_…",
  "livemode": true,
  "granularity": "minute",
  "from": "2026-10-15T09:00:00.000Z",
  "to": "2026-10-15T09:30:00.000Z",
  "data": [
    {
      "object": "queue_log_bucket",
      "bucket": "2026-10-15T09:29:00.000Z",
      "accepted": 38,
      "refusals": { "product_busy": 2, "rate_limited": 0 },
      "max_running": 20,
      "max_waiting": 20,
      "concurrency_limit": 20,
      "ended": 17,
      "median_wait_ms": 41000,
      "median_duration_ms": 96000
    }
  ]
}
Field Meaning
bucket Start of the minute or hour (UTC).
accepted Runs accepted.
refusals Refused submits, by reason.
max_running The most runs using a slot at once.
max_waiting The most priority runs waiting for a slot at once.
concurrency_limit Your concurrency slots.
ended Runs that ended after running. A run canceled while queued is not counted.
median_wait_ms Median wait from submit to start, for the priority runs that started in the bucket.
median_duration_ms Median run time, start to end (the wait not included), for the runs that ended in the bucket.

Refusals

GET /v2/queue/refusals lists your refused submits, newest first. They are kept 30 days.

Parameter Meaning
limit 1 to 200. Default 50.
before The id of the last item of the previous page.
{
  "object": "list",
  "data": [
    {
      "object": "queue_refusal",
      "id": "5821",
      "at": "2026-10-15T09:29:41.204Z",
      "reason": "product_busy",
      "style": "sty_…",
      "end_user_id": "user_42",
      "retry_after_s": 5,
      "livemode": true
    }
  ],
  "has_more": false
}

reason is product_busy (slots and queue full) or rate_limited (the per-minute run limit). end_user_id is null when the call sent none. Use it to find which of your users were affected.

Congestion episodes

Wandura checks your product every minute. Each kind of congestion opens its own episode, so several can be open at once.

kind Starts when Ends when
refusing One or more refusals in the last 5 minutes. 10 minutes with no refusal.
near_full 80% or more of your concurrency slots are in use for 1 minute. Below 60% for 5 minutes.
slow At least 3 runs ended in the last 15 minutes, and their median run time is over 10 minutes. The median is back under 8 minutes, or nothing ended in 15 minutes and nothing runs or waits.
  • The same kind does not start again within 30 minutes of its end.
  • Live keys only: test keys never start an episode.
  • Run time is from start to end. The wait before it is not included.

GET /v2/queue/episodes lists your episodes, newest first. It takes the same limit and before as refusals.

{
  "object": "list",
  "data": [
    {
      "object": "congestion_episode",
      "id": "184",
      "product": "prod_…",
      "livemode": true,
      "kind": "refusing",
      "status": "ended",
      "started_at": "2026-10-15T09:31:00.000Z",
      "ended_at": "2026-10-15T09:52:00.000Z",
      "duration_s": 1260,
      "start": { "running": 20, "waiting": 20, "concurrency_limit": 20, "refusals_5m": 3, "refusals_10m": 3, "runs_ended_15m": 24, "median_duration_ms": 95000 },
      "end": { "running": 9, "waiting": 0, "concurrency_limit": 20, "refusals_5m": 0, "refusals_10m": 0, "runs_ended_15m": 30, "median_duration_ms": 88000 },
      "affected": { "refusals": 7, "runs_accepted": 212, "runs_ended": 205 }
    }
  ],
  "has_more": false
}
Field Meaning
status open or ended.
started_at, ended_at, duration_s When it started and ended. The last two are null while it is open.
start, end Your numbers at the start and at the end (end is null while open): running, waiting, concurrency_limit, refusals in the last 5 and 10 minutes, runs ended in the last 15 minutes and their median run time (null when none ended).
affected Once ended: refused submits (counted from 5 minutes before the start), runs accepted and runs ended during the episode. null while open.

Webhook events

Each episode is sent twice to your product's webhook:

  • queue.congestion_started when it starts.
  • queue.congestion_ended when it ends.

They are opt-in: your webhook gets them only when its event list names them. To turn them on, ask Wandura to add both events to your product's webhook. Your other events stay as they are.

data is the episode as it is when the event is made. There is no sequence, because the event belongs to no run. Signature, retries, Wandura-Event-Id and Wandura-Event-Type work as for run events: see Webhooks.

{
  "id": "evt_…",
  "object": "event",
  "type": "queue.congestion_started",
  "created_at": "2026-10-15T09:31:00.412Z",
  "data": {
    "object": "congestion_episode",
    "id": "184",
    "product": "prod_…",
    "livemode": true,
    "kind": "refusing",
    "status": "open",
    "started_at": "2026-10-15T09:31:00.000Z",
    "ended_at": null,
    "duration_s": null,
    "start": { "running": 20, "waiting": 20, "concurrency_limit": 20, "refusals_5m": 3, "refusals_10m": 3, "runs_ended_15m": 24, "median_duration_ms": 95000 },
    "end": null,
    "affected": null
  }
}
  • Match a start and its end on data.id. Rarely, an end can arrive before its start: trust data.status.
  • The events are the fast signal. For the full record, read GET /v2/queue/episodes.

What to do

  • Watch the headers. When X-Concurrency-Waiting climbs toward X-Concurrency-Limit, slow down or send new work to your fallback before you are refused.
  • Act on the events. On queue.congestion_started, switch to your fallback: at once for refusing, early for near_full, and for slow warn your users or use a faster path. On queue.congestion_ended of the same kind, switch back.
  • On 429 (product_busy, gateway_rate_limited; Jobs API PRODUCT_BUSY, RATE_LIMITED): no run was made, so sending the request to your fallback cannot run it twice. To retry with Wandura, wait Retry-After seconds (add a little jitter) and resend with the same Idempotency-Key.
  • On 503 service_unavailable (Jobs API SERVICE_UNAVAILABLE): nothing was created or charged. Retry after Retry-After with the same Idempotency-Key.
  • Poll calmly. While your product is congested, runs wait longer. Poll each run every 5–10 s, or better, use webhooks. Polling faster does not start a run sooner.
  • Need more room? The queue log and the refusals show when and how often you hit your limits. Send them to Wandura, and your product's slots or per-minute limit can be raised.

When Wandura itself has trouble, it is posted on wandura.instatus.com.

See also: Errors · Concepts: limits · Jobs API: limits.

Updated 15 Oct 2026 · Change history (1)

↑↓ selectEnter openEsc close

Send feedback

Your message goes straight to the Wandura team. It includes the address of this page.

0 / 4000