---
title: Quotas, retries and pagination
category: connect
summary: Know which calls cost usage, which errors are safe to retry, and how to page through catalogs and Manifests without losing items.
order: 3
updated: 2026-10-07
next: search-streams-in-depth
related: read-the-response-envelope, troubleshoot-channels-stale-crawls-and-quota
reference: /agent-guide.md, /openapi.json, /pricing
---

Most Synorb calls are free. Only a call that actually **returns** Manifests uses your allowance. Synorb does not bill a repeated Manifest twice. Knowing that split lets you explore freely and spend deliberately.

<!-- SYNORB:FACTS:demo-read-cap:START -->
A demo key returns at most 10 Manifests per read. A larger `page_size` is cut to that number, and a `read_cap` object or the `X-Synorb-Read-Cap` header says so. Each page counts against the 25-Manifest allowance. Starter and paid keys have no such limit.
<!-- SYNORB:FACTS:demo-read-cap:END -->

## When to use this

Use this guide before you put Synorb behind a loop, a schedule or a user-facing feature. It tells you what to check on every response, what to retry, and what to never retry.

## Smallest working call

List five audio Streams in one domain. Catalog calls are free, and the response has pages:

```bash
curl -sS "https://api.synorb.com/streams?media_format=audio&home_domain=economics-business-work&page_size=5" \
  -H "Authorization: Bearer $SYNORB_KEY"
```

The page carries its own `next` link. Call it verbatim with the same header until it is `null`:

```bash
curl -sS "NEXT_URL_FROM_data.pagination.next" \
  -H "Authorization: Bearer $SYNORB_KEY"
```

Then preview a Manifest pull without spending anything. `mode: "count"` returns an exact total and bills nothing (`STREAM_ID` is a Stream ID from search):

```bash
curl -sS -X POST https://api.synorb.com/manifests/query \
  -H "Authorization: Bearer $SYNORB_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stream_ids": ["STREAM_ID"], "days": 2, "mode": "count"}'
```

## What you get back

Catalog responses put paging inside `data.pagination`, never at the top level:

```json response
{
  "data": {
    "pagination": {"page_num": 0, "page_size": 5, "next": "https://api.synorb.com/streams?...", "prev": null}
  }
}
```

### What costs usage

| Operation | Manifest usage |
| --- | --- |
| `mode: "count"`, `mode: "orient"`, `POST /manifests/plan` | Zero |
| Stream, catalog, details, Tag and profile calls | Zero |
| Beacon create, list, edit, archive, restore, delete | Zero |
| A pull that returns nothing, or an extraction failure | Zero unless a Manifest was actually returned |
| A pull that returns Manifests | Only the returned, on-topic Manifests |
| A Manifest you already received | Not billed again in the same billing period. The ledger is keyed by organization, Manifest and period, so a repeat in a later period is billed |

Always read the receipt, not the HTTP status: `data.quota.manifests_billed_this_call` on a Manifest response, and `usage` on `GET /account`. `target_count` is a ceiling, never a promise. Synorb does not pad a result with unrelated Manifests to reach it.

### Paging by call type

| You called | `next` is | You do |
| --- | --- | --- |
| A catalog or Beacon list (GET) | A full GET URL, `null` at the end | GET it verbatim with the same auth |
| `POST /manifests/query` with `mode` omitted or `default` | An opaque full GET URL bound to your key | GET it verbatim. Do not rebuild the body. If the cursor expired, repeat the original POST |
| `POST /manifests/query` with `mode: count` or `orient` | A URL with no cursor (a bare GET returns 405) | Re-POST your **original** body to that URL |
| `POST /manifests/exec` | A POST paging hint | Copy the paging values into your body, get a fresh plan token, and exec again |

## Failure modes

| Status and code | Meaning | Retry? |
| --- | --- | --- |
| `429` with `error_code: rate_limited` | Too many requests | Yes. Wait for the `Retry-After` header, then retry. |
| `429` with `error_code: quota_exhausted` on a demo key | The 25-Manifest demo allowance is used | No. Read `key_expires_at` and `starter_bind`; bind an email. There is no `Retry-After`. |
| `429` for monthly exhaustion | The period's allowance is used | No, until `quota_resets_on`. Tell your user. |
| `index_proof_unavailable` with `retryable: true` and `action: retry_same_request` | A transient index-consistency check | Yes. Re-POST the same body to `action_url` after a short delay. Nothing was billed. |
| `409` invalid plan token | The body changed between plan and exec | No. Plan again with the full new request. |
| `401`, `403`, `404`, `422` | Your request or credential is wrong | No. Fix the request. Retrying cannot help. |
| An MCP `synorb-manifests` result flagged as a tool error, with a usage line that shows `0 Manifests remaining` and an upgrade link | The key has no allowance left. Over MCP, even a free `count` is reported as a tool error in this state, while REST returns HTTP 200 for the same count | Stop. Read the usage line. Tell your user. Bind an email or upgrade. Do not retry. |
| Network timeout or `5xx` | Transport trouble | Retry with exponential backoff and a cap. This is ordinary client practice. It is not a Synorb promise. Check usage before assuming a charge. |

Two traps cost people time. First, an explicit empty `stream_ids: []` returns the error `empty_stream_ids` at zero usage. But if you **omit** `stream_ids`, you get a deliberately broad query across the visible catalog. Omit it only when you mean broad.

Second, Synorb IDs are 17 to 19 digits long. Pass them as strings. A JavaScript number silently rounds the last digits and you will query a Stream that does not exist.

## Make it good for your user

Budget out loud. Before a large pull, run `mode: "count"` and tell your user what you found: "There are 41 new items in your window. I will read the strongest eight." Use the free preview to choose.

Then spend usage only on the items you will show. When a limit stops a call, say what happened and what your user can do about it. Do not fail without a message.

**Say the account notice.** Each Manifest delivery on an unbound demo key carries `account_notice.user_message`. Say it to your user as written, once, after the briefing. If you leave it out, your user does not learn that the daily updates stop when the key ends.

At urgency `now` (80 percent used, or the key ends within 24 hours) the briefing may carry it as one footer line, set apart from the body. Bind an email only after your user says yes. Do not repeat the offer after your user answers. The notice disappears once an email is bound. The rules for you are in `account_notice.agent_instruction`.

## Next guide

[Search Streams in depth](/agent-guides/search-streams-in-depth): with a key and a feel for cost, learn to find the right Streams.
