---
title: "Rate Limits"
url: "https://developer.agentsync.io/api-rate-limits"
image: "https://developer.agentsync.io/_og/d/c_Ocean.takumi,title_Rate+Limits,props_eyJ0aGVtZSI6eyJtb2RlIjoibGlnaHQiLCJjb2xvcnMiOnsicHJpbWFyeSI6IiMxODdFRkYifX19,p_Ii9hcGktcmF0ZS1saW1pdHMi,s_JZu-KQ_iji-gfF14.png"
---

# Rate Limits

| Surface                                                          | Limit                   |
| :--------------------------------------------------------------- | :---------------------- |
| API endpoints — ProducerSync, Contracting, Hierarchies, Identity | 300 requests per minute |
| Token endpoint                                                   | 200 requests per minute |

**Limits are counted per credential** — that is, per `client_id`, not per user, per server, or per IP address.

The practical consequence is that everything authenticating with the same `client_id` draws from one shared budget. If you run your sync across several parallel workers, or across several machines, they are not each allowed 300 requests per minute — together they get 300. Size your concurrency accordingly, and pace against the `ratelimit-remaining` header rather than assuming a per-worker allowance.

> Rate limits are subject to change to ensure fair and high quality usage for all customers.

## [Response Headers](#response-headers)

Every response carries your current status — pace against these rather than probing for the edge.

| Header              | Meaning                                                |
| :------------------ | :----------------------------------------------------- |
| ratelimit-limit     | Maximum requests allowed in the current window         |
| ratelimit-remaining | Requests still available in the current window         |
| ratelimit-reset     | When the limit resets                                  |
| retry-after         | Present only on 429 — how long to wait before retrying |

```text
ratelimit-limit: 300
ratelimit-remaining: 299
ratelimit-reset: 17
```

## [Handling a 429](#handling-a-429)

```text
HTTP/1.1 429 Too Many Requests
{ "message": "API rate limit exceeded" }
```

Honor `retry-after` when present. Otherwise back off exponentially, and add randomness so retries from parallel workers don't synchronize into a thundering herd.

```python
import time
import random
import requests

def request_with_backoff(url, headers, max_retries=5):
    """Make a GET request with exponential backoff on 429 responses."""
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code != 429:
            response.raise_for_status()
            return response

        # Honor the retry-after header if present, otherwise use exponential backoff
        retry_after = response.headers.get("retry-after")
        if retry_after:
            wait = float(retry_after)
        else:
            wait = (2 ** attempt) + random.uniform(0, 1)

        if attempt < max_retries - 1:
            time.sleep(wait)

    raise Exception(f"Request failed after {max_retries} attempts due to rate limiting")
```

## [Staying Well Inside the Limits](#staying-well-inside-the-limits)

-   **Reuse tokens.** They're valid for 60 minutes. Fetching a new token per request is the most common way integrations hit the token endpoint limit — see [Token Expiration & Reuse](https://developer.agentsync.io/api-authentication#token-expiration--reuse).
-   **Prefer paged bulk reads** over per-record polling.
-   **Use `updated_since` as a high-water mark** so each sync run fetches only what changed, rather than re-reading whole collections. See [Filter, Search & Sort](https://developer.agentsync.io/api-sorting-and-filtering).