> This page is for version v2 (default).
> For other versions, use one of these documentation indexes:
> - v2 (default): https://docs.paid.ai/v-2/llms.txt
> - v1: https://docs.paid.ai/v-1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.paid.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.paid.ai/_mcp/server.

# Rate limits

> How Paid rate limits API requests, how to recognize a 429, and how clients should back off.

Paid applies per-organization rate limits to protect the platform for every
customer. This page explains how limits are counted, how to recognize a
rate-limited request, and how your integration should respond to a `429`.

## How limits are counted

Limits use a token bucket per organization: a bucket holds a budget of
tokens (its burst capacity) and refills continuously at a sustained
per-second rate. Short spikes above the sustained rate succeed as long as
the bucket holds enough tokens.

Today the per-organization limit applies to signal ingestion
(`POST /api/v2/signals/bulk` and the legacy v1 usage endpoints). Ingestion
is signal-counted: a request carrying 500 signals consumes 500 tokens,
not 1. Other endpoints are not yet rate limited per organization; as
limits extend to them they will use this same framework, counting one
token per request. Live and test-mode organizations have separate buckets,
so load tests against a test organization never consume your production
budget.

Limits are per organization, not per API key. If your workload needs a
higher sustained rate, contact Paid support.

## The 429 response

When the bucket cannot cover a request, the API responds with
`429 Too Many Requests`, a `Retry-After` header (in seconds), and an error
body:

```json
{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "details": "Too many requests. Retry after 3s."
}
```

The `429` status, the `RATE_LIMIT_EXCEEDED` code, and the `Retry-After`
header are the complete contract: wait at least `Retry-After` seconds,
then retry.

A rate-limited signals request is rejected whole: no signals from that
request are ingested. Retry the whole batch after the `Retry-After`
interval; a stable `idempotencyKey` on each signal makes the retry safe
(see below).

## Two kinds of 429

The API returns `429` for two distinct limits. Discriminate on the body
`code`, not the status:

| `code`                       | What it means                                                    | Retry signal                                  |
| ---------------------------- | ---------------------------------------------------------------- | --------------------------------------------- |
| `RATE_LIMIT_EXCEEDED`        | The per-organization rate limit described on this page           | `Retry-After` header (seconds)                |
| `CONCURRENCY_LIMIT_EXCEEDED` | Too many analytics queries running at once for your organization | No `Retry-After`; retry once a query finishes |

Only the rate limiter's `429` carries `Retry-After`. A
`CONCURRENCY_LIMIT_EXCEEDED` response clears as soon as one of your
in-flight analytics queries completes.

## Back off correctly

* Respect `Retry-After`. Wait at least that many seconds before retrying the
  request.
* Use exponential backoff with jitter for repeated `429`s. Never retry in a
  tight loop; that keeps your bucket empty and delays your own recovery.
* Smooth your send rate rather than bursting: a steady stream at your
  working rate ingests everything a spiky one does, without rejections.
* Batch signals up to the 500-per-request maximum. Fewer, fuller requests
  reduce overhead, and the signal-counted cost is the same.
* Give every signal a stable `idempotencyKey`, and resend exactly the same
  key on a retry; never generate a fresh key per attempt. Keys are
  optional, and signals without one are not deduplicated, so retrying a
  keyless batch can ingest the same signals twice. Duplicate keys within
  an organization are skipped, which is what makes whole-batch retries
  safe.

## Related

* [First signals](/documentation/getting-started/first-signals)
* [Create signals in bulk](/api-reference/api-reference/signals/create-signals)