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

# Errors and limits

> What each error means, and how many requests a key may make.

## Errors

Every REST error under `/v1` is an [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem, sent as `application/problem+json`. Branch on its `code`; `title` and `detail` are for people. The `instance` is the request id, taken from Railway's `x-railway-request-id` when present or generated for the request.

```json theme={null}
{
  "type": "about:blank",
  "title": "Not Found",
  "status": 404,
  "detail": "Nothing here that this API key can read.",
  "code": "not_found",
  "instance": "request-id"
}
```

| Status | `code` | Means | What to do |
| - | - | - | - |
| 400 | `invalid_cursor` | The cursor is invalid or belongs to another query. | Start paging again with the intended filters. |
| 401 | `invalid_token` | The key is missing, unknown, revoked or expired. The response carries `WWW-Authenticate: Bearer`. | Create a new key. |
| 404 | `not_found` | The path or the brand does not exist, or the key cannot read the brand. | Check the id against `GET /v1/brands`. |
| 409 | `workspace_archived` | The key's organization, or workspace, is archived. | Nothing to retry; the organization reads nothing until it is restored. |
| 410 | `scan_version_unsupported` | The shared report was measured on a score formula Heralded no longer serves. The problem carries `domain` and `rescan_url`. | Start a new Snapshot at `rescan_url`. |
| 410 | `report_retired` | The shared report was retired with Heralded's pre-launch data. The problem carries `domain` and `rescan_url`. | Start a new Snapshot at `rescan_url`. |
| 422 | `invalid_request` | A parameter is invalid. `errors` names each one. | Fix the parameters. |
| 429 | `rate_limited` | The key made more than 120 requests in a minute. | Wait for `Retry-After` seconds, then retry. |
| 500 | `internal_server_error` | Heralded failed. | Retry later. |

A 422 lists each invalid parameter with its location, message and type:

```json theme={null}
{
  "type": "about:blank",
  "title": "Unprocessable Content",
  "status": 422,
  "detail": "The request's parameters are invalid; `errors` names each one.",
  "code": "invalid_request",
  "errors": [
    { "loc": ["query", "limit"], "msg": "Input should be greater than or equal to 1", "type": "greater_than_equal" }
  ]
}
```

## Errors over MCP

The MCP server follows MCP's own rules rather than RFC 9457:

* A failure inside a tool call, such as a bad argument, a brand the key cannot read or an archived organization, is a tool result with `isError` set and the reason as text.
* An unknown method or tool is a JSON-RPC error.
* A missing, revoked or expired key is an HTTP 401, and a key past its limit an HTTP 429 with `Retry-After`.

## Rate limit

Each API key or assistant grant may make 120 requests a minute, counted across REST and MCP. The request past the limit answers 429 with `Retry-After: 60`. The limit is per credential, so two keys do not share it.

The anonymous [leaderboard HTTP reads](/concepts/leaderboards#read-through-the-api-or-an-assistant) have a separate per-IP bucket. A configured site key selects a larger bucket without changing which measurements are public. Their `Retry-After` is the number of seconds until that bucket resets.

Responses to authenticated requests carry advisory headers:

```http theme={null}
RateLimit-Policy: "default";q=120;w=60
RateLimit: "default";r=119;t=40
```

`q` is the quota, `w` is the window in seconds, `r` is the requests remaining and `t` is the seconds until the window resets. Concurrent requests can consume the remaining quota before your next call. A 429 body carries the same numbers in `rate_limit`:

```json theme={null}
{
  "limit": 120,
  "window": 60,
  "remaining": 0,
  "reset": 40
}
```

## Keeping reads consistent

Computed reads return `meta.query`, the normalized period, filters and paging parameters, and `meta.data_as_of`, the id and completion time of the newest scan included. `data_as_of` is null before a scan is available. The existing `period` descriptor stays alongside `meta`.

To keep a page's requests on the same scans, take the first response's `meta.data_as_of.id` and pass it as `as_of` on the other computed reads. For example:

```http theme={null}
GET /v1/brands/{brand_id}/scores?period=4w&as_of={scan_id}
GET /v1/brands/{brand_id}/sources?period=4w&as_of={scan_id}
```

The reads exclude scans completed after the pin. An unknown scan, an unfinished scan or a scan belonging to another brand answers `404 not_found`. The default uses the latest scan. The pin selects scan facts; current settings, page inventory and action states remain live.

MCP tools accept the same `as_of` and cursor arguments on the applicable reads. Their structured content includes the same `meta` and `next_cursor` fields.

## Paging

Prompts, answers, sources, AI visits, actions, changes, prompt suggestions, scores, competitors and the organization portfolio take `limit` and `offset`. `limit` defaults to 20 and goes up to 100, except for scores where it defaults to 10. They also accept an opaque `cursor` and return `next_cursor`.

Scores page the Engines table with `include=engines`, or the engine pane's competitors with `include=engine`. Competitors page the leaderboard with `with_page=true`. Both return `next_cursor` at the top of the response, null when the read has no page or the page is the last. Start with your filters and `limit`, then pass each `next_cursor` as `cursor` with the same filters and `limit`. Stop when `next_cursor` is null. When you supply a cursor, its offset takes precedence over `offset`. A cursor pins the scans selected by the first request, so a newer scan does not move answers between pages. Prompt cursors keep the first page's prompt-set version and row order, including prompts you archive while paging. Action, AI-visit and change cursors also exclude actions, pages and changes created after the first page's population is read. Keep the cursor unchanged; changing its query or an explicit `as_of` answers `400 invalid_cursor`.

The cited-page read pages its citing prompts with `prompts_limit` and `prompts_offset`. It accepts `as_of` for its scan facts.

Changes and Discover recommendations accept `as_of` and return query metadata.

[Versioning](/versioning) covers what can change within v1.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.