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

# Rate limits and quotas

> Burst limits, cycle quotas, and error handling

Two separate mechanisms limit API usage:

1. **Rate limits** — per-minute burst protection, keyed by **API key**. Hitting one means *slow down and retry*.
2. **Cycle quotas** — workspace-level `used`/`limit` meters (unlocks, searches, messages) that reset each cycle. Hitting one means *you're done until reset or upgrade*.

## Request rate limits (stacking)

Every authenticated request consumes the **global** limit. Some routes also consume a **route-class** limit.

| Class | Limit (per key / 60s) | Block duration | Routes |
| - | - | - | - |
| **global** | 120 | 60s | All `/api/v1/*` |
| **search** | 10 | 120s | `GET /search/candidates` |
| **sensitiveRead** | 30 | 60s | `GET /candidates/{talentCardId}` |
| **createJob** | 5 | 300s | `POST /jobs` |
| **inviteToApply** | 10 | 120s | `POST /invite-to-apply` |

### On rate limit exceeded

HTTP **429** with:

```json theme={null}
{
  "error_code": "rate_limit_exceeded",
  "error_description": "Rate limit exceeded for this search endpoint. Retry after 42 seconds."
}
```

The response includes a **`Retry-After`** header (seconds). Back off and retry — do not retry in a tight loop.

## Cycle quotas

Workspace-level running totals, reset each usage cycle. Defaults (production workspaces get custom grants):

| Meter | Default limit / cycle | Consumed by | Exhausted → `error_code` |
| - | - | - | - |
| `apiUnlocks` | 10 | `GET /candidates/{id}` on a **locked** candidate (auto-unlock) | `api_unlock_limit_reached` |
| `searches` | 100 | `GET /search/candidates` (each page request) | `api_search_limit_reached` |
| `messages` | 20 | `POST /invite-to-apply` only (not `POST /messages` follow-ups) | `api_message_limit_reached` |

Quota errors are also HTTP **429**, but retrying does not help — the meter resets at `cycleEndsOn`, or contact Cutshort for a higher grant.

## Checking usage

`GET /usage` returns every meter as a `used`/`limit` pair:

```json theme={null}
{
  "workspaceId": "…",
  "apiUnlocks": { "used": 3, "limit": 10 },
  "searches": { "used": 42, "limit": 100 },
  "messages": { "used": 5, "limit": 20 },
  "cycleEndsOn": "2026-08-15T00:00:00.000Z",
  "planExpiresOn": null
}
```

| Field | Meaning |
| - | - |
| `cycleEndsOn` | When usage meters reset |
| `planExpiresOn` | When the workspace subscription ends (`null` for free plans) |

## Other 429 responses

| `error_code` | Meaning |
| - | - |
| `rate_limit_exceeded` | Burst limit — retry after `Retry-After` seconds |
| `api_unlock_limit_reached` | Unlock quota exhausted for the cycle |
| `api_search_limit_reached` | Search quota exhausted for the cycle |
| `api_message_limit_reached` | Message quota exhausted for the cycle |
| `search_throttled` | Plan-level search throttle from upstream (not the Redis burst limit) |

## Best practices

* Run searches sequentially, not in parallel
* Refine filters instead of paginating deep — every page request costs one search
* Check `apiUnlocks` before batch candidate fetches
* Cache results client-side when iterating on the same role
* Check `Retry-After` on 429 and exponential-backoff retry (burst limits only)


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