Reference
Limits and retention
Media sizes, request rates, field limits, supported inputs, and how long Story0 keeps each kind of data.
This page collects every limit in one place, with what happens when you reach it.
Media
| Limit | Value | What happens past it |
|---|---|---|
| Video length | 20 minutes (1,200 seconds) | The asset, or the job reading it, fails with unsupported_input. |
| File size | 2 GB (2,147,483,648 bytes) | POST /v1/uploads with a larger bytes, completing a larger upload, or fetching a larger URL fails with unsupported_input. |
| Minimum billable length | 30 seconds | Shorter videos are billed as 30 seconds. |
| URL download time | 10 minutes | The fetch fails with source_unavailable. Upload the file instead. |
| URL redirects | 5 | The fetch fails with source_unavailable. |
| Upload chunk size | Multiples of 256 KiB (262,144 bytes), except the last chunk | Use other sizes only for the final chunk. The SDKs round chunk sizes for you. |
| Upload session lifetime | 24 hours | Open a new session. |
Supported inputs
- Video files in common container formats and codecs, as long as the file has a video stream and a measurable duration. Story0 identifies the format from the file itself, whatever its extension or declared content type. A file it can't read fails with
media_unreadable; if it plays elsewhere, re-encoding it to H.264 in an MP4 container usually helps. - Upload content types: any
video/*type orapplication/octet-stream. - URLs: direct
httporhttpslinks to a video file on a public address, without credentials in the URL. Platform pages (YouTube, Instagram and TikTok links, for example) and web pages aren't fetched. See Uploading media. - Audio is detected and reported in
media.has_audio, but it isn't analyzed: results are based on what's visible in sampled frames.
Requests
| Limit | Value |
|---|---|
Read requests (GET) |
1,200 a minute per account |
Write requests (POST, PUT, DELETE) |
300 a minute per account |
| Active jobs | 20 queued or running per account, test and live combined |
Long-poll wait on GET /v1/jobs/{id} |
0 to 30 seconds |
Page size (limit) for assets, jobs and events |
1 to 100, default 20 |
| Webhook deliveries listed per endpoint | The 50 most recent |
GET /v1/usage window (days) |
1 to 90, default 30 |
| Enabled webhook endpoints | 10 per account |
| Active API keys | 25 per account |
Rate limits are counted in one-minute windows and tracked by each API server separately, so treat them as approximate. Going over either one returns 429 rate_limited with a Retry-After header giving the seconds until the window resets; the SDKs wait that long before retrying.
The active-job limit returns 429 concurrency_limit with Retry-After: 5. It counts jobs that are queued or running, so the way past it is to let jobs finish before you submit more.
Request fields
| Field | Limit |
|---|---|
instruction |
3 to 2,000 characters |
labels (partition) |
2 to 12 |
categories (screening) |
1 to 20 |
Label and category key, uncertainty.label |
1 to 40 characters: a lowercase letter, then lowercase letters, digits or underscores |
Label and category description |
1 to 500 characters |
sampling.fps |
0.1 to 4 |
| Frames inspected per segmentation | At most 600 |
range.start |
0 or more, and inside the video |
range.end |
More than 0 and more than range.start; clamped to the video's duration |
constraints.min_seconds |
More than 0, up to 600 |
constraints.max_seconds |
More than 0, up to 3,600 |
constraints.max_segments |
1 to 500 |
metadata |
Up to 20 pairs of strings, each value up to 500 characters |
billing.max_charge.amount |
A decimal string with up to 6 decimal places; currency must be USD |
Idempotency-Key header |
1 to 255 characters from A–Z a–z 0–9 _ . : - |
X-Request-Id header |
8 to 80 characters from A–Z a–z 0–9 _ - |
Upload filename |
Up to 255 characters |
Upload content_type |
Up to 100 characters |
Webhook endpoint description |
Up to 200 characters |
Feedback comment, actor, label |
Up to 2,000, 200 and 40 characters |
A request that breaks one of these limits is refused with 422 invalid_request, and the errors array points at the field. The 600-frame limit is the exception: it isn't validated up front, so choose sampling.fps so that the rate multiplied by the range length stays at or below 600. See Segmentation.
Retention
| What | Kept for | Afterwards |
|---|---|---|
| Asset media | 7 days from the asset's creation, or until a queued or running job that needs it finishes | The media is deleted and the asset returns 404 not_found. |
| Results | 30 days from creation | GET returns 410 result_expired. The job record remains. |
| Idempotency keys | 7 days | The same key starts a new job. |
| Upload sessions | 24 hours | Open a new session. |
| Estimates | Meant to be current for 1 hour (expires_at) |
Request a new estimate. |
| Webhook deliveries | Up to 18 attempts over about 17 hours | The delivery is marked failed; the event stays in GET /v1/events. |
Every asset and result states its own expiry in retention, so you never need to compute it. You can delete media sooner with DELETE /v1/assets/{id}; results stay available until their own expiry.
Timeouts and defaults
| Item | Value |
|---|---|
Hold for a request whose duration isn't known yet, without max_charge |
$2.00 |
| Time your webhook endpoint has to answer | 15 seconds |
| Webhook signature tolerance in the SDK helpers | 5 minutes |
| Automatic job retries before a job fails | Up to 5 attempts |
| SDK network timeout per request | 60 seconds |
| SDK automatic retries | 2 |
| SDK wait for a job | 10 minutes |
| SDK wait for an asset to become ready | 5 minutes |