> For the complete documentation index, see [llms.txt](https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/reference/rate-limits.md).

# Rate Limits

The rate limits on the Heeler API and MCP server — application budgets, response headers, per-endpoint limits, cooldowns, and clean backoff.

Heeler limits how fast a client can call the API and MCP server, in two places:

<table><thead><tr><th width="200">Layer</th><th width="190">Scoped to</th><th>What it protects</th></tr></thead><tbody><tr><td><strong>Application</strong></td><td>API key, verified MCP principal, or anonymous MCP network</td><td>A per-minute budget for programmatic access, plus tighter limits on a handful of expensive endpoints.</td></tr><tr><td><strong>Edge</strong></td><td>Source IP address</td><td>A web application firewall in front of the API, guarding against volumetric abuse before a request reaches Heeler.</td></tr></tbody></table>

The two behave differently when you cross them, and the difference matters when you're writing a client — so read [Handling limits in your client](#handling-limits-in-your-client) before you ship one.

## API-key limits

Every request authenticated with an API key (`Authorization: Bearer h4r-…`) counts against a bulk budget of **2,000 requests per minute**, shared across the API endpoints for that key. API-key-authenticated calls to `/mcp` use this same API-key budget. The default can be raised per tenant.

The window is fixed, not rolling: it opens on your first request and closes 60 seconds later, at which point the budget resets in full. `X-RateLimit-Reset` tells you exactly when that happens.

{% hint style="info" %}
The limit is **per key, not per user or per organization**. Issuing a second key gives that integration its own budget — which is a good reason to create one key per integration rather than sharing one everywhere. See [API Keys](/mrecEO40m5D6bt7Pq5pE/administer-and-monitor/manage-access/api-keys.md).
{% endhint %}

Ordinary browser application requests are not subject to this application budget. A browser session used to authenticate `/mcp` is programmatic MCP traffic and uses the verified-principal budget below.

The CLI's authentication, configuration, and telemetry calls are charged to a **separate control-plane budget of 600 requests per minute**, so bulk scanning traffic cannot exhaust the calls that authorize and configure a run.

## MCP limits

MCP traffic is limited by the identity Heeler has verified, rather than by a raw token string:

<table><thead><tr><th width="250">Caller</th><th width="170">Default limit</th><th>Bucket identity</th></tr></thead><tbody><tr><td>OAuth-authenticated MCP</td><td><strong>600 per minute</strong></td><td>Tenant + OAuth client + user</td></tr><tr><td>Browser-session MCP</td><td><strong>600 per minute</strong></td><td>Tenant + signed-in user</td></tr><tr><td>API-key-authenticated MCP</td><td><strong>2,000 per minute</strong></td><td>The API key's normal budget</td></tr><tr><td>Unauthenticated or unverified MCP</td><td><strong>60 per minute</strong></td><td>Source network — exact IPv4 address or IPv6 <code>/64</code></td></tr></tbody></table>

The authenticated MCP limit can be raised per tenant. Rotating a credential for the same verified principal does not create a fresh bucket; the budget follows the principal, not the token value.

## Rate limit headers

Every request evaluated by the application limiter comes back with its current budget, so you never have to discover the limit by hitting it. This includes API-key and MCP traffic:

<table><thead><tr><th width="240">Header</th><th>Meaning</th></tr></thead><tbody><tr><td><code>X-RateLimit-Limit</code></td><td>Requests allowed in the window.</td></tr><tr><td><code>X-RateLimit-Remaining</code></td><td>Requests left in the current window.</td></tr><tr><td><code>X-RateLimit-Reset</code></td><td>Seconds until the window resets and the budget refills.</td></tr><tr><td><code>Retry-After</code></td><td>Seconds to wait before retrying. Present on <code>429</code> responses only.</td></tr></tbody></table>

A normal response:

```
HTTP/1.1 200 OK
X-RateLimit-Limit: 2000
X-RateLimit-Remaining: 1997
X-RateLimit-Reset: 43
```

Read these rather than hard-coding a documented default. The effective number can vary by tenant and request class; the headers describe the budget this request was charged against.

{% hint style="info" %}
These headers describe the **application budget that evaluated the request**. A `429` from one of the [per-endpoint limits](#per-endpoint-limits) below is a different bucket, so always use `Retry-After` to decide when to retry.
{% endhint %}

## When you exceed it

You get `429 Too Many Requests`:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 2000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 12
```

```json
{ "detail": "Rate limit exceeded. Please try again later." }
```

Rejected requests **don't consume budget**, so retrying early costs you nothing beyond the wasted call — but it also won't get you served any sooner. Wait for `Retry-After`.

## Per-endpoint limits

A few endpoints do enough work per call to carry their own, tighter limit. These are **scoped to the calling IP address**, not to your API key — so everyone behind a shared egress IP (a corporate NAT, a CI runner pool) draws on the same bucket.

<table><thead><tr><th width="330">Endpoint</th><th width="150">Limit</th><th>Why</th></tr></thead><tbody><tr><td><code>PATCH /api/reset_password</code></td><td>3 per hour</td><td>Slows down probing for which addresses have accounts.</td></tr><tr><td><code>POST /api/upgrade-guidance/</code></td><td>30 per minute</td><td>Reads cached guidance, generating it in the background on a miss.</td></tr><tr><td><code>POST /api/upgrade-guidance/generate</code></td><td>5 per minute</td><td>Generates upgrade guidance synchronously.</td></tr><tr><td><code>POST /api/upgrade-guidance/invalidate</code></td><td>5 per minute</td><td>Discards cached guidance so it regenerates.</td></tr><tr><td><code>POST /api/workflows/ai/suggest</code></td><td>10 per minute</td><td>Turns a natural-language prompt into a workflow.</td></tr><tr><td><code>POST /api/guardrails/ai/suggest</code></td><td>10 per minute</td><td>Turns a natural-language prompt into a guardrail.</td></tr><tr><td><code>POST /api/credentials/</code></td><td>100 per 30 minutes</td><td>Creates a connection credential.</td></tr><tr><td><code>POST /api/credentials/preflight/…</code></td><td>20 per 5 minutes</td><td>Each preflight opens a live connection to your cloud provider. Applies to the AWS organization, AWS account, CodeArtifact, GCP organization, and Azure organization preflights individually.</td></tr></tbody></table>

All of these return `429` with `Retry-After`.

## Cooldowns and quotas

Some actions are limited by what they cost rather than by how fast you call them. They also answer with `429` and `Retry-After`, but the window is tied to the resource, not to a request rate:

<table><thead><tr><th width="230">Action</th><th width="190">Limit</th><th>Scope</th></tr></thead><tbody><tr><td>Re-check a secret's validity</td><td>Once every 15 minutes</td><td>Per secret. Each check refreshes a repository cache and probes the provider live.</td></tr><tr><td>Manual secret re-checks</td><td>60 per hour</td><td>Per organization, across all secrets. Scheduled re-checks run on their own budget and never consume this one.</td></tr><tr><td>Start an export</td><td>30 per hour</td><td>Per user. Creating an export and running one on demand draw on the same budget; scheduled runs of a recurring export don't.</td></tr><tr><td>Change a repository's scan branch</td><td>Once every 24 hours</td><td>Per repository. Each change triggers a full re-scan, so the cooldown stops repeated re-scans of the same repository.</td></tr></tbody></table>

## Edge protection

A web application firewall sits in front of the API and rate limits by **source IP** — currently **2,000 requests per 5 minutes**, with a separate, higher allowance of **6,000 requests per 5 minutes** for `POST /api/agent_files/scan`, which is designed for server-to-server volume.

{% hint style="warning" %}
**An edge block looks different from an application limit.** It returns `403 Forbidden`, not `429`, with no `Retry-After` and no Heeler JSON body. If a burst of traffic starts returning `403` where the same credentials worked a moment ago, that's the edge limit — not an expired or revoked key. Back off; don't rotate credentials.
{% endhint %}

Because this layer counts by IP, all traffic leaving your network through one address shares the budget — CI runners, scripts, and browser sessions together. If you're planning a high-volume integration from a single egress IP, talk to your Heeler account team first.

## Scan capacity

`POST /api/agent_files/scan` can also push back when scanning capacity is saturated, which is a queueing signal rather than a rate limit:

<table><thead><tr><th width="120">Response</th><th>Meaning</th></tr></thead><tbody><tr><td><code>503</code> with <code>Retry-After</code></td><td>Capacity is saturated. Wait the stated number of seconds and retry — the request was never processed.</td></tr><tr><td><code>503</code> without <code>Retry-After</code></td><td>The scanner is temporarily unavailable. There's no useful wait to quote; retry with your own backoff.</td></tr></tbody></table>

`heelercli scan-agent-file` already handles this for you: it honours `Retry-After` and retries up to three times within 60 seconds. See the [Agent Files API](/mrecEO40m5D6bt7Pq5pE/reference/agent-files-api.md) and the [CLI Command Reference](/mrecEO40m5D6bt7Pq5pE/reference/cli-command-reference.md).

## How the CLI reduces request pressure

Use **heelercli 1.0.23 or later** for parallel or monorepo scans. It reduces and absorbs request pressure in three ways:

* Vulnerability and `ci` scans submit SBOM assessments in server-advertised batches (10 SBOMs by default) instead of making one request per code root. The platform can tune the batch size per tenant, and the CLI safely falls back to individual assessment on an older platform or for an entry the batch could not assess.
* Read-only platform calls honour `Retry-After` and use bounded, cancellable backoff for `429` responses. If the retry budget is exhausted, the error includes the endpoint and the limit headers needed to diagnose the request class that was throttled.
* The default `ci` run collects license data once and reuses it for dependency policy, avoiding a second set of license lookups.

These controls make concurrent scans much less likely to exhaust a budget, but they do not make retries unbounded. Keep parallelism reasonable and treat a terminal rate-limit error as a signal to reduce concurrency or ask Heeler to review the tenant-specific limit.

## Handling limits in your client

1. **Honour `Retry-After` on both `429` and `503`.** It's the only number that accounts for where you actually are in the window.
2. **Read `X-RateLimit-Remaining` and slow down before you hit zero.** Pacing beats retrying.
3. **Treat a `403` during a burst as a rate limit, not an auth failure.** Back off and retry; rotating a key won't help and creates work for you.
4. **Give each integration its own API key.** The per-key budget isn't shared, so one noisy job can't starve another.
5. **Keep concurrency modest and paginate rather than parallelising.** Most large reads are cheaper as one paged sequence than as many simultaneous requests.
6. **Export in bulk instead of polling.** If you're pulling large result sets, an [export](/mrecEO40m5D6bt7Pq5pE/operate/dashboards/filtering-and-exports.md) costs one request instead of hundreds.

## Related

* [API Keys](/mrecEO40m5D6bt7Pq5pE/administer-and-monitor/manage-access/api-keys.md) — creating, scoping, and rotating the keys these limits apply to.
* [Agent Files API](/mrecEO40m5D6bt7Pq5pE/reference/agent-files-api.md) — the scan endpoint's own status codes and size limits.
* [CLI Command Reference](/mrecEO40m5D6bt7Pq5pE/reference/cli-command-reference.md) — how the CLI handles backoff for you.
* [MCP Tools and Prompts](/mrecEO40m5D6bt7Pq5pE/reference/mcp-tools-and-prompts.md) — the MCP tool catalog, scopes, and structured error behavior.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.heeler.com/mrecEO40m5D6bt7Pq5pE/reference/rate-limits.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
