> ## Documentation Index
> Fetch the complete documentation index at: https://cue.vibeset.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors & rate limits

> Status codes, error shape, the rate limit, and what an Idempotency-Key really does.

## Error shape

A `/v1` error returns a JSON body with an `error` object carrying a stable `type`, a human-readable
`message`, and the `request_id` for that call:

```json theme={null}
{
  "error": {
    "type": "invalid_request",
    "message": "Provide either 'video_id' or 'video_url'.",
    "request_id": "a1b2c3d4e5f6..."
  }
}
```

Handled `/v1` responses also carry that id as an `X-Request-Id` header, and Cue echoes an inbound
`X-Request-Id` if you send one, so you can supply your own.

<Warning>
  An unhandled `500` carries neither. The header is stamped by middleware that runs after the
  handler returns, and an unhandled exception never gets that far, so a `500` comes back bare with
  no `error` body and no request id. That is the one failure you would most want to report. When
  you hit one, send us the timestamp, the endpoint, and your account id instead. Responses outside
  `/v1`, including `/mcp`, carry no `X-Request-Id` at all.
</Warning>

## Status codes

| Code  | Meaning                                                                                                                                                         | What to do                                                              |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `202` | Accepted, the work runs off the request path.                                                                                                                   | Poll the `poll` URL in the body.                                        |
| `400` | The request was malformed, or `video_url` is not a public https URL we can fetch.                                                                               | Read `error.message`.                                                   |
| `401` | Missing, invalid, or revoked key.                                                                                                                               | Send a valid key as `Authorization: Bearer` or `x-api-key`.             |
| `402` | No active payment method, credits spent or expired, or the monthly spend cap reached.                                                                           | The message says which. Add a card, or raise the cap.                   |
| `403` | The video or job belongs to another account, or your account is suspended.                                                                                      | Use a video your account ingested.                                      |
| `404` | No such `video_id` or `job_id`.                                                                                                                                 | Ingest the video first, or check the id.                                |
| `422` | Request fields are invalid, the video failed to process, a beats job failed or will never complete, or an `Idempotency-Key` was reused for a different request. | Read `error.message`. A dead beats job was not billed, so resubmit it.  |
| `429` | Rate limit exceeded.                                                                                                                                            | Back off, see below.                                                    |
| `500` | Something broke on our side.                                                                                                                                    | Retry. See the warning above, an unhandled `500` carries no request id. |
| `502` | The match failed, or an async loop or sync job failed.                                                                                                          | Retry. Contact us if it persists.                                       |
| `503` | A dependency is temporarily unavailable: key validation, your credit balance, or the `Idempotency-Key` check.                                                   | Retry. A credit-balance `503` sends `Retry-After: 5`.                   |

## An oversized or non-video source fails later, not on the request

`POST /v1/videos` and `POST /v1/match` with a `video_url` validate the URL shape and then return
`202` immediately. The fetch happens in a worker, so size and content-type are checked long after
your request succeeded. There is no `413` or `415` on the request.

Poll `GET /v1/videos/{video_id}` for the outcome:

```json theme={null}
{ "video_id": "vid_123", "status": "failed", "reason": "source_too_large: Video exceeds the size limit." }
```

`reason` is prefixed with a machine-branchable token you can switch on: `source_too_large` past
600 MB, `source_not_a_video`, `source_url_rejected`, `source_not_found`, `source_error` for any
other problem reaching or reading the source (an empty body, a transfer that was truncated
mid-download), or `processing_error` for anything on our side. Treat an unrecognised token as
`processing_error`.

A source counts as a video when its `Content-Type` contains `video`, or its URL path ends `.mp4`,
`.mov`, `.webm` or `.m4v`.

## Rate limit

Each key is limited to **60 requests per minute** on a rolling 60-second window. Buckets are per
key, so two keys is two budgets. Need more?
[Get in touch](mailto:sales@vibeset.ai) and we will raise it.

Exceeding the limit gets you a `429` telling you how long to wait:

```
Retry-After: 12
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 12
```

Any `/v1` response that reached the rate check also carries `X-RateLimit-Limit`,
`X-RateLimit-Remaining` and `X-RateLimit-Reset`, so you can pace yourself before you hit the wall.
A response refused earlier than that check, such as a `401` on a bad key or a `402` from the card
or credit gate, carries none of them.

## What an Idempotency-Key does, endpoint by endpoint

The header does two different things depending on where you send it, and only one of them is what
most people expect.

On `POST /v1/beats` it is a true replay. A key is honoured for 24 hours, scoped to your account.
Resending the same request with the same key returns the original `job_id` with
`idempotent_replay: true`, and nothing runs or bills a second time. Reusing that key for a
different request is a `422` rather than a silent alias. The fingerprint ignores the URL's query
string, so re-signing a presigned link between attempts is fine.

On `POST /v1/match`, `/v1/align`, `/v1/loop` and `/v1/underscore` it prevents a second **charge**
only. Nothing short-circuits the handler, so the match, alignment or loop is recomputed in full.
Use it to make a retry safe for your bill, not to save the work.

<Warning>
  On a cold `/v1/align`, `/v1/loop` or `/v1/match`, the one that returns `202` with a `job_id`, the
  header does not reach the billing path at all. Those jobs are metered under a server-generated
  key when they complete. Resending while the first job is still running starts a second job and
  bills a second time at the higher cold rate, whatever key you send. If a cold call times out on
  you, poll the job URL you already have before you resend the request.
</Warning>
