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

# Rate limits

> What the limits are, how to read them, and how to stay under them.

Rate limits are per workspace, not per key. Issuing more keys does not buy more throughput.

| Endpoint class                          | Default limit |
| --------------------------------------- | ------------- |
| Reads — `GET` detail, timeseries, lists | 600 / minute  |
| Searches — `POST /*/search`             | 120 / minute  |
| Resolution — `/resolve`, `/enrich`      | 60 / minute   |
| Job creation — `POST /jobs`             | 20 / minute   |
| Management — lists, trackers, webhooks  | 120 / minute  |

Read your workspace's actual limit from `GET /v1/me` rather than hard-coding these.

## Reading the headers

Every response carries the current state:

```
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 84
X-RateLimit-Reset: 1789459320
```

<ResponseField name="X-RateLimit-Remaining" type="integer">
  Requests left in the window. Slowing down as this approaches zero is cheaper than being
  throttled.
</ResponseField>

<ResponseField name="X-RateLimit-Reset" type="integer">
  Unix timestamp when the window resets.
</ResponseField>

When you exceed a limit you get `429` with a `Retry-After` header in seconds. Honor it — backing
off on your own schedule keeps you limited longer.

```python theme={null}
if resp.status_code == 429:
    time.sleep(float(resp.headers.get("Retry-After", 1)))
```

## Staying under

<AccordionGroup>
  <Accordion title="Page with limit=100" icon="layer-group">
    Requesting 100 rows per page instead of the default 25 cuts your request count by four for the
    same data, at no extra credit cost — [credits are charged per row](/docs/platform/credits).
  </Accordion>

  <Accordion title="Batch resolution and enrichment" icon="boxes-stacked">
    `/resolve` and `/enrich` take 100 inputs per call. One hundred single-input calls will exhaust
    a 60/minute budget immediately; one batched call uses a single request.
  </Accordion>

  <Accordion title="Use jobs for bulk work" icon="clock">
    A job is one request regardless of how much work it does. Anything above a few thousand rows
    belongs in a [job](/docs/workflows/jobs) rather than a paging loop.
  </Accordion>

  <Accordion title="Replace polling with webhooks" icon="webhook">
    Polling job status every five seconds burns a read budget to learn nothing. Subscribe to
    `job.completed` instead.
  </Accordion>

  <Accordion title="Cache what does not move" icon="database">
    `GET /channels` and `GET /categories` change on the order of weeks. Refresh them on a schedule,
    not per request.
  </Accordion>
</AccordionGroup>

## Concurrency

Alongside the per-minute limits, a workspace may have at most **20 requests in flight** at once.
Exceeding it returns `429` with `Retry-After: 1`.

A bounded worker pool is the fix, not a retry loop:

```python theme={null}
from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=10) as pool:   # comfortably under 20
    results = list(pool.map(enrich_batch, batches))
```

## If you need more

Limits are raised for workspaces with a demonstrated pattern. Before asking, check that the volume
is real rather than avoidable — the list above removes most of it. If it is real, contact
[support@unif.dev](mailto:support@unif.dev) with your workspace ID and the sustained rate you need.

<Note>
  Rate limits and [credits](/docs/platform/credits) are independent. Staying under the rate limit
  does not mean you have credits, and having credits does not raise your rate limit.
</Note>
