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

# Rate limits

> Per-key request windows, and the 429 response when a window is exceeded.

Each API key has five request windows. The Embedded API, the Platform API, and legacy `POST /v0.1/search/talents` all use them. MCP tool calls count against the same windows.

## Default limits

A new API key is created with these limits:

| Window | Maximum requests |
| ------ | ---------------- |
| Second | 1                |
| Minute | 30               |
| Hour   | 200              |
| Day    | 1,000            |
| Month  | 15,000           |

The limiter reads the five integers stored on that key. Each key has its own counters. An OAuth access token uses the counters of the API key it belongs to.

Each window is a fixed UTC bucket:

* **Second, minute, hour, and day** follow the UTC clock. A day is a UTC day.
* **Month** is a block of 30 days counted from the Unix epoch, not a calendar month.

Windows are checked in this order: second, minute, hour, day, month. The first window over its maximum rejects the request.

A window stored as `0` rejects every request. In that case `details.limit` and `details.current` are both `0`.

## Where it applies

After the API key is accepted, every external HTTP route consumes one request on these windows. That includes talent search, qualified search, LinkedIn contact enrichment, sourcings, candidates, and sequences.

MCP does not consume these windows when the client connects. Each MCP tool call does. See [MCP](#mcp).

## When a window is exceeded

The route responds with **429** and `error.code` `rate_limit_exceeded`. `details.current` counts the rejected request.

```json theme={null}
{
  "success": false,
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded: too many requests per minute",
    "debugTrackingCode": "...",
    "details": {
      "window": "minute",
      "limit": 30,
      "current": 31
    }
  }
}
```

`details.window` is `second`, `minute`, `hour`, `day`, or `month`. The `message` is `Rate limit exceeded: too many requests per {window}`.

## Requests before a valid key

Before the key is checked, the client IP is limited to **10 attempts in 5 minutes**. The next attempt is rejected for **10 minutes**.

A valid key clears that IP counter, so only attempts that fail authentication accumulate. The response is also **429** `rate_limit_exceeded`:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests, please try again later",
    "debugTrackingCode": "..."
  }
}
```

The IP is the first address in `x-forwarded-for`, then `x-real-ip`, then the CloudFront viewer address.

## MCP

Each tool call takes one request from the same per-key windows. When a window is exceeded, the tool result is an error. The text starts with `Rate limit exceeded` and includes the reason, for example `Rate limit exceeded: too many requests per minute`.

Authenticated requests to the MCP endpoint also have a separate protocol cap, on the same API key:

| Window | Maximum protocol requests |
| ------ | ------------------------- |
| Second | 20                        |
| Minute | 600                       |

Exceeding that cap returns **429** and this JSON-RPC error:

```json theme={null}
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32000,
    "message": "MCP connector is receiving too many protocol requests. Please wait a moment and retry."
  },
  "id": null
}
```

The [IP limit](#requests-before-a-valid-key) still applies when connecting. A 429 from that check is returned as a JSON-RPC error (`code` `-32000`) whose message includes `Too many requests, please try again later`.
