Limits and quotas¶
This page documents the caps the service actually enforces on upload activity and on analysis submissions, and what a partner integration must send to get a correct analysis result. Every cap below is scoped to your API key: your key's own usage never affects another partner's headroom, and none of these caps is shared globally across all partners. Each cap is stated with its exact enforced figure and the error you will see if you exceed it, so you can size your integration before you send a single request.
Caps at a glance¶
| Cap | Enforced value | Scope | On breach |
|---|---|---|---|
| Upload-spot rate | 10 spots per rolling 15-minute window | Per API key | 429 spots_per_window_exceeded |
| Unclaimed bytes | 1 GiB, aggregate across all your unclaimed uploads | Per API key | 429 unclaimed_bytes_exceeded |
| Upload spot lifetime | 15 minutes (900 seconds) from mint | Per upload spot | 410 upload_spot_expired |
| Upload handle reuse | Single-use; the same exercise_id is idempotent |
Per upload handle | 409 upload_already_claimed |
| Analysis budget | Operator-set per key: a whole number of analyses, or unlimited | Per API key | 429 usage_limit_exceeded |
Full error envelope shapes and message text for every code above are documented in the error catalog.
Per-key analysis limit¶
Your key may carry an analysis budget: a whole number of analyses it is allowed to run over its lifetime. Whether a key has a budget, and how large it is, is set by the operator per key; many keys have no budget at all and are unlimited.
An analysis counts against the budget once it has run: a job that reached
completed (including a zero-repetition result) or that failed while
being analysed each count as one. A job that never ran the engine does
not count, so a submission you cancel while it is still queued returns
its budget.
When the budget is used up, the next submission on that key is refused
with a 429 usage_limit_exceeded response and no job is created, so a
refused submission never counts against the budget. The budget is a
lifetime total, not a rolling window, so it does not refill by waiting;
it frees up only when you cancel one of the key's still-queued jobs, or
when the operator raises the limit.
You can read where your key stands at any time from the usage object on
GET /v1/identity: limit is the
budget (or null when the key is unlimited), used is how much has run,
and remaining is what is left. remaining is already net of jobs you
have submitted that are still in flight, so it is exactly the figure your
next submission is checked against: pace yourself on remaining and you
will not hit usage_limit_exceeded by surprise.
Upload spot rate limit¶
You may mint up to 10 upload spots within a rolling 15-minute window,
per API key. Minting an eleventh spot before the window clears is
refused with a 429 spots_per_window_exceeded response.
The rolling window is the same 15 minutes as the spot lifetime, so capacity does not free up all at once at a fixed reset time. As each of your spots crosses the 15-minute mark, it stops counting against your window usage and a new mint becomes available. Plan your upload cadence around a continuous rolling allowance rather than a periodic reset.
Unclaimed-bytes cap¶
The total size of bytes you have uploaded and left unclaimed by a
submitted job may not exceed 1 GiB, per API key. This is an aggregate
cap across every upload spot you currently hold, not a limit on any
single file. Uploading a file that would push your held total over
the cap is refused with a 429 unclaimed_bytes_exceeded response.
Once you submit a job for an uploaded video, its bytes stop counting
toward this cap.
Upload spot lifetime¶
An upload spot is valid for 15 minutes (900 seconds) from the moment
it is minted. Using a spot to upload a video, or submitting a job
against it, after that window has passed fails with a 410
upload_spot_expired response. Mint a new spot and upload again if
this happens.
Single-use upload handles¶
An upload handle can be claimed by exactly one job. Submitting a job against a handle that has already been claimed behaves differently depending on what you send:
- Submitting the same
exercise_idagain is idempotent: you get back a201response with the originaljob_id, not an error. It is safe to retry a submission with identical parameters. - Submitting a different
exercise_idagainst an already-claimed handle is refused with a409upload_already_claimedresponse, because the handle can only ever be tied to one exercise.
If you need to submit a second job, mint a new upload spot rather than reusing a claimed handle.
No enforced size or duration ceiling¶
The service enforces no per-file size ceiling and no duration bound on the video you upload. The 1 GiB figure above is a per-key aggregate across unclaimed uploads, not a per-file size limit: a single large video can consume most of your available headroom, but the service does not fail an individual file for being large or long on its own. Design your integration around the unclaimed-bytes and spot-rate caps above, since those are the only enforced storage-abuse bounds today.
Configuration and scope¶
The upload and storage caps on this page are the values enforced by the current production deployment. They are controlled by operator-side configuration and default to the values shown here; a future deployment could change them. They are not a per-partner negotiated limit, and there is no request path to raise them for an individual key: every key is held to the same production defaults.
The per-key analysis budget is the exception: it is set by the operator per individual key, so different keys can carry different budgets (or none). There is still no partner-facing request path to change your own budget; ask the operator to adjust it.
Video input requirements¶
For a correct analysis result, the file you upload must be a genuine video with a video track. The service does not check this today. It accepts whatever bytes are written to the signed upload URL and does not inspect their content, container, or codec before handing them to the analysis pipeline.
Sending a file that is not a real video does not produce an error. Instead, the analysis model runs on whatever it receives and returns a confident but wrong result: it fabricates an analysis rather than failing outright. Nothing in the response tells you the input was bad, because from the service's point of view nothing went wrong.
Validating that a file is a genuine video with a video track before you upload it is your responsibility as the integrator. Treat it as a precondition your own client enforces, not a guarantee the API enforces on your behalf.
Bad video input yields a wrong result, not an error
A non-video upload is analyzed anyway and comes back as a confident, incorrect result rather than a failure you can detect from the response alone. Validate your input before you upload.