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

# Rate Limits & Quotas

> Beta quota pools, per-minute rate limits, response headers, and 429 handling

Signa enforces two independent controls per organization:

* **Monthly quotas** cap how many requests you can make against each pool over a billing period. Quotas reset on your billing anchor day (usually the first of the month).
* **Rate limits** cap how many requests per minute you can make against each endpoint type. Rate limits slide in real time and are separate from monthly quotas: a single request counts against both.

All API keys under the same organization share one quota and one rate-limit budget per pool.

## Monthly Quota Pools (Beta)

Endpoints are grouped into pools. Each pool has its own monthly allowance. During beta, every organization is on the same plan with the following limits:

| Pool           | Beta monthly quota                           | Rate limit | Endpoints                                                                                                                                                                                                                                                                                                                                                                                      |
| -------------- | -------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **search**     | 100,000                                      | 1,000/min  | `GET`/`POST /v1/trademarks`, `GET /v1/trademarks/suggest`, `GET /v1/suggest`, `GET /v1/owners`, `GET /v1/attorneys`, `GET /v1/firms`, `GET /v1/entities`, `GET /v1/entities/{id}/trademarks`, `POST /v1/classifications/suggest`                                                                                                                                                               |
| **read**       | 500,000                                      | 10,000/min | `GET /v1/trademarks/{id}` and its sub-resources (`history`, `changes`, `related`, `proceedings`, `coverage`, `source`), `POST /v1/trademarks/batch`, `GET /v1/owners/{id}`, `GET /v1/attorneys/{id}`, `GET /v1/firms/{id}`, `GET /v1/entities/{id}`, `GET /v1/entities/{id}/family`, `GET /v1/proceedings`, `GET /v1/proceedings/{id}`, `GET /v1/events`, portfolio and saved-search endpoints |
| **screening**  | 1,000                                        | 100/min    | `GET /v1/screening`                                                                                                                                                                                                                                                                                                                                                                            |
| **check**      | 500,000                                      | 1,000/min  | `POST /v1/goods-services/suggest`, `POST /v1/deadlines/compute`, `POST /v1/oppositions/compute`, `POST /v1/reconcile`                                                                                                                                                                                                                                                                          |
| **monitoring** | Unmetered (per-watch resource quota instead) | 100/min    | `/v1/watches/*`, `/v1/alerts/*`, `/v1/webhooks/*` (except test and delivery reads, which are utility)                                                                                                                                                                                                                                                                                          |
| **reference**  | Unmetered                                    | 1,000/min  | `GET /v1/offices`, `GET /v1/jurisdictions`, `GET /v1/classifications`, `GET /v1/design-codes`, `GET /v1/deadline-rules`, `GET /v1/opposition-rules`                                                                                                                                                                                                                                            |
| **utility**    | Unmetered                                    | 1,000/min  | `GET /v1/organization/*`                                                                                                                                                                                                                                                                                                                                                                       |

The MCP server (`/mcp`) is metered separately at 1,000 requests/min per organization; see [MCP tools & permissions](/ai/mcp-tools).

<Note>
  The [health check endpoints](/api-reference/health) and `GET /v1/openapi.json` are unauthenticated and are not subject to any per-organization quota or rate limit.
</Note>

### Pool rules of thumb

* **List and search endpoints** (returning many results, ranked or filtered) are **search**.
* **Detail endpoints** (returning one resource, or a batch of known IDs) are **read**.
* **Static taxonomies** (offices, classifications, design codes) are **reference** and never count against a monthly quota.
* **Dashboard routes** (usage, plan, API key management) are **utility** and never count against a monthly quota. Health and docs endpoints are unauthenticated and bypass rate limiting and quota entirely.

To see your current usage per pool, call [Get Usage](/api-reference/administration/get-usage). Your plan limits are also included in every response under `by_endpoint_type[*].limit`.

***

## Rate Limits

Each pool has a per-minute limit that applies regardless of HTTP method. This is the number you'll see in the `RateLimit-Policy` header and the one you'll normally hit.

One notable exception: `POST /v1/trademarks/batch` bills against the **read** pool (10,000/min), but because it's a `POST` request its effective limit is **1,000/min**, the same ceiling that applies to other mutating requests. If you're bulk-hydrating IDs, pace batch calls accordingly.

***

## Rate Limit Headers

API responses include IETF-standard rate limit headers so you can monitor your usage in real time. One exception: the CDN-cached reference-data routes (`/v1/offices`, `/v1/jurisdictions`, `/v1/classifications`, `/v1/design-codes`) omit the `RateLimit-*` headers, since they carry per-organization values that must not leak into a shared cache hit.

```
HTTP/1.1 200 OK
RateLimit-Policy: 1000;w=60
RateLimit: remaining=994, reset=30
```

| Header             | Format                           | Description                                                                                                                                                                     |
| ------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `RateLimit-Policy` | `{limit};w={windowSec}`          | The limit in effect for this request. May list more than one comma-separated policy when more than one applies to the same request; `RateLimit` always reports the binding one. |
| `RateLimit`        | `remaining={N}, reset={seconds}` | Remaining requests and seconds until reset for the policy closest to being exhausted.                                                                                           |
| `Retry-After`      | `{seconds}`                      | Only present on `429` responses. Number of seconds to wait before retrying.                                                                                                     |

<Tip>
  Monitor the `remaining` value proactively. If it drops below 10% of your limit, slow down requests rather than hitting 429 errors.
</Tip>

***

## Daily Sub-Caps

In addition to the monthly quota, each metered pool has a daily sub-cap set at **10% of the monthly limit**. Its purpose is to prevent a single client from exhausting an entire month's allowance in minutes.

| Pool      | Beta monthly | Beta daily |
| --------- | ------------ | ---------- |
| search    | 100,000      | 10,000     |
| read      | 500,000      | 50,000     |
| screening | 1,000        | 100        |
| check     | 500,000      | 50,000     |

The daily counter resets at **UTC midnight**.

### Daily quota headers

Every metered response includes three additional headers alongside the rate-limit headers:

| Header                    | Format             | Description                                       |
| ------------------------- | ------------------ | ------------------------------------------------- |
| `X-Quota-Daily-Limit`     | integer            | Total daily units allowed for this pool.          |
| `X-Quota-Daily-Remaining` | integer            | Remaining daily units.                            |
| `X-Quota-Daily-Reset`     | ISO 8601 timestamp | Next UTC midnight, when the daily counter resets. |

`X-Quota-Limit`, `X-Quota-Remaining`, and `X-Quota-Reset` report the monthly counter using the same shape. These headers count quota units (per day or per month), not requests per minute, so don't confuse them with `RateLimit-*`. They're omitted on unmetered pools (reference, utility) and on `404` responses, since neither spends quota.

### 429 with `quota_scope`

If a request exceeds either cap, you receive a `429` with `error.type = "quota_exceeded"`. The `error.quota_scope` field tells you which counter was breached:

```json theme={null}
{
  "error": {
    "type": "quota_exceeded",
    "title": "Daily quota exceeded",
    "detail": "You have used 10000 of 10000 units today. Quota resets at 2026-05-13T00:00:00.000Z.",
    "quota_scope": "daily",
    "quota_limit": 10000,
    "quota_used": 10000,
    "quota_resets_at": "2026-05-13T00:00:00.000Z"
  },
  "request_id": "req_..."
}
```

`quota_scope` is `"monthly"` or `"daily"`. `error.type` stays `"quota_exceeded"` regardless; switch on `quota_scope` if you need to distinguish.

***

## 429 Response

When you exceed your rate limit, the API returns a `429 Too Many Requests` status with details about when you can retry:

```
HTTP/1.1 429 Too Many Requests
RateLimit-Policy: 1000;w=60
RateLimit: remaining=0, reset=12
Retry-After: 12
```

```json theme={null}
{
  "error": {
    "type": "rate_limited",
    "title": "Rate limit exceeded",
    "status": 429,
    "detail": "Rate limit exceeded. Retry after 12 seconds.",
    "retryable": true,
    "retry_after": 12
  },
  "request_id": "req_abc123"
}
```

The `Retry-After` response header and the `retry_after` field in the body both contain the number of seconds to wait.

<Warning>
  Ignoring `429` responses and continuing to send requests will not help: those requests are also rejected. In extreme cases, sustained limit violations may result in a temporary block of your API key.
</Warning>

***

## Monitoring Usage

Check your current billing period usage and rate limit status with [Get Usage](/api-reference/administration/get-usage):

```bash theme={null}
GET /v1/organization/usage
Authorization: Bearer sig_YOUR_KEY
```

```json theme={null}
{
  "object": "usage",
  "billing_period": {
    "start": "2026-04-01T00:00:00Z",
    "end": "2026-04-30T23:59:59Z"
  },
  "by_endpoint_type": {
    "search": { "used": 1204, "limit": 100000 },
    "read": { "used": 8932, "limit": 500000 },
    "screening": { "used": 3, "limit": 1000 },
    "check": { "used": 40, "limit": 500000 }
  },
  "rate_limits": {
    "search": 1000,
    "read": 10000,
    "screening": 100,
    "check": 1000,
    "monitoring": 100,
    "utility": 1000,
    "reference": 1000
  },
  "request_id": "req_abc123"
}
```

`by_endpoint_type` reports `used` and `limit` for every metered pool: `search`, `read`, `screening`, and `check`. A `limit` of `null` means unlimited; `0` means the pool is not allowed on your plan.

`rate_limits` is the per-endpoint-type requests-per-minute map the limiter enforces, including the unmetered pools (`monitoring`, `utility`, `reference`) that still carry a per-minute ceiling even though they don't spend a monthly quota.

<Note>
  Avoid polling `/v1/organization/usage` in a tight loop. It is classified **utility**: unmetered against any monthly quota, but still subject to the utility per-minute rate limit (1,000/min on beta).
</Note>

***

## Handling 429 in Code

The Signa TypeScript SDK handles 429 responses automatically with built-in retry logic; see [SDK Error Handling](/sdk/typescript#error-handling). If you are implementing your own retry logic, wait for the `Retry-After` duration and retry with exponential backoff.

***

## Best Practices

<Accordion title="Use batch endpoints to reduce request count">
  A single batch request of 100 IDs counts as 1 request against your rate limit, compared to 100 individual GET requests. See [Batch Get Trademarks](/api-reference/trademarks/batch-trademarks).
</Accordion>

<Accordion title="Cache responses with ETags">
  Using `If-None-Match` headers with ETags avoids downloading unchanged response bodies, saving bandwidth and processing time. `304 Not Modified` responses still count against your rate limit, but they are significantly cheaper for both client and server. See the [caching guide](/guides/caching) for implementation patterns.
</Accordion>

<Accordion title="Use targeted lookups instead of frequent searches">
  If you are periodically checking for trademark status changes, use [Trademark History](/api-reference/trademarks/trademark-history) on specific marks rather than re-running broad searches.
</Accordion>

<Accordion title="Spread requests evenly">
  Bursting 500 requests in the first second of a window is more likely to trigger rate limiting than spreading them evenly across the minute. If you need to process a large batch, add a small delay (50-100ms) between requests.
</Accordion>

<Accordion title="Use separate API keys per concern">
  If your application has both a user-facing dashboard and a background sync job, create separate API keys for each. This prevents a background job from exhausting the rate limit that your dashboard users depend on.
</Accordion>

***

## Need Higher Limits?

If you are consistently hitting rate limits, reach out at [support@signa.so](mailto:support@signa.so) to discuss higher limits tailored to your workload.
