# Limits and behaviour

> The numbers and rules worth knowing before you go to production.

| | |
|---|---|
| Requests | 1000 a minute per account. Part signing (`GET …/uploads/{uploadId}/parts/{n}`) has its own 10,000 a minute. [Rate limit](#rate-limit). |
| Failed authentication | 60 a minute per credential. [Rate limit](#failed-authentication). |
| Project name | 255 characters. |
| Project description | 2000 characters. |
| Page size | `perPage` 1 to 100, default 25; `page` from 1. |
| Request body | 2 MB, JSON or YAML. [Request bodies](#request-bodies). |
| File size | 5 TB. |
| File name | A path of up to 900 bytes, 255 bytes a segment, with a media extension ([list](/vod-express/uploads/overview#accepted-media)). No `..`, `.` or empty segments, no backslashes or control characters. |
| Upload parts | 16 MiB each (more for files over about 160 GB), at most 10,000. |
| Unfinished uploads | Discarded after 3 days. |
| Download links | Valid for 1 hour; an output feed's media links redirect to one valid for 6 hours. |
| Tokens | 30 minutes ([Environments](/vod-express/environments)). |
| Idle project | Stopped 30 minutes after its last file finished. A project that reads a feed is never stopped for being idle. |
| Feeds | The source feed is read every 30 seconds; its URL is up to 2048 characters. An output feed is rewritten after every file and every 30 seconds. [Feeds](/vod-express/concepts/inputs#feeds). |
| Failed service | Stopped after reading `failed` for 2 hours; `POST …/start` relaunches it. |
| Webhooks | 10 per account; each delivery retried for 24 hours. [Webhooks](/vod-express/guides/webhooks#limits). |
| Speaking rate | 20–200 %; dynamic between 80 and 160 % by default. |

## Rate limit

The limit is per account: every key and every user of your organization share one budget, and
another customer's traffic never uses it. A call SyncWords support makes on your account's
behalf counts against it too. Every answer of a counted call says where you stand:

| Header | On | Meaning |
|---|---|---|
| `X-RateLimit-Limit` | Every answer | Requests your account is allowed a minute: `1000`. |
| `X-RateLimit-Remaining` | Every answer | Requests your account has left in the current minute. |
| `Retry-After` | `429` | Seconds until requests are accepted again. |

The minute starts at the first request, not on the clock. Past the limit, a call answers `429`
with `code: "too_many_requests"`; wait `Retry-After` seconds and repeat it. A polling loop that
calls once a minute per project, as in [Monitoring](/vod-express/guides/monitoring#a-good-polling-loop), stays
far below the limit.

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0

{"message": ["Too Many Attempts."], "code": "too_many_requests"}
```

Part signing is not counted against it, so a large upload does not use up the limit. It has a
budget of its own instead, 10,000 parts a minute per account (a whole 5 TB file is at most 10,000
parts), answers `429` with `Retry-After` past it, and carries no rate-limit headers.

### Failed authentication

A request whose token is expired or unknown answers `401`, and counts against that token: after
60 refusals of one token in a minute, requests carrying it are answered `429` (with `Retry-After`)
without looking it up again, until the minute is over. `POST /token/create` counts a refused key
pair the same way. Other credentials are unaffected, so one misconfigured client never locks out
the others, and successful requests never count. A client that gets `401` should fix its token,
or its key pair, not retry.

Creating a token is not counted against the 1000 requests a minute.

## Request bodies

A body is JSON (`Content-Type: application/json`) or YAML (`application/yaml`,
`application/x-yaml` or `text/yaml`; a `charset` parameter is fine). Any other type answers `415`
with `code: "unsupported_media_type"`, so `curl -d` needs `-H 'Content-Type: application/json'`.
A body over 2 MB answers `413` with `code: "payload_too_large"`; completing an upload of the
full 10,000 parts sends about 700 KB.

Answers are JSON unless `Accept` prefers YAML, by its quality values: `Accept: application/yaml`
and `application/yaml, */*;q=0.1` get YAML, `*/*` gets JSON. The config endpoints and error
bodies follow it.

## Browsers

Every origin may call the API. A browser can read `Retry-After`, `X-RateLimit-Limit`,
`X-RateLimit-Remaining` and `Idempotency-Replayed`, and caches a preflight for two hours.

## Behaviour to design around

**A project stopped by hand does not process uploads**

A project that stopped itself after its last file starts again when a file lands, unless its
`startOnNewFiles` is off. Files uploaded to a project stopped with `POST …/stop`, or created
with `autoStart: false`, wait (unless an API upload in parts completes). Call
`POST /projects/{id}/start` after every upload session; it does nothing to a running project.
[Starting again on a new file](/vod-express/concepts/projects#starting-again-on-a-new-file).

**Changing the config does not reprocess finished files**

The new config applies to files processed from then on. Retry the files you want redone.

**Deleting a project keeps its files**

The input and output folders stay in storage after `DELETE /projects/{id}`.

**Outputs are named from the input without its extension**

`a.mov` and `a.mkv` write the same output. API uploads refuse the second; AWS CLI uploads do
not.

**A few files are processed at a time**

The service works on several files at once and takes the next one as soon as one finishes. A
file interrupted by a stop is processed again from the start on the next run.

**The build is pinned per project**

A project keeps its `buildVersion` until you change it, so a new build never alters a running
integration. New projects get your account's default.

**Unknown fields are refused**

A misspelt option is a `422`, not a silently ignored field.
