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

# Coinbase Advanced Trade API Rate Limits and Quotas

> Understand the request rate limits for the Coinbase Advanced Trade API and learn how to stay within quotas and handle 429 errors gracefully.

The Coinbase Advanced Trade API enforces rate limits to ensure fair access and platform stability for all users. Limits are applied per API key and vary depending on whether an endpoint is public or requires authentication. Understanding these limits — and building your integration to respect them — will prevent disruptions to your trading workflows and reduce the risk of temporary key restrictions.

## Rate Limit Tiers

Endpoints are divided into two tiers based on their sensitivity and resource cost. Your request budget is tracked separately for each tier.

### Public Endpoints (Market Data)

Public endpoints return market-wide data such as product listings, order book snapshots, candles, and ticker prices. Because they do not require authentication, they carry a higher rate limit to support a wide range of use cases including analytics, dashboards, and price feeds.

* **Limit:** 30 requests per second
* **Authentication required:** No
* **Examples:** `GET /api/v3/brokerage/products`, `GET /api/v3/brokerage/market/product-book`

### Private Endpoints (Orders and Accounts)

Private endpoints interact with your account data and can execute real trades. Because they carry financial risk and consume more server resources, they are subject to stricter limits. These limits are enforced per authenticated API key.

* **Limit:** 10 requests per second
* **Authentication required:** Yes (JWT Bearer token)
* **Examples:** `POST /api/v3/brokerage/orders`, `GET /api/v3/brokerage/accounts`, `GET /api/v3/brokerage/orders/historical/batch`

> [!NOTE]
>
> Rate limits are applied on a **per-API-key** basis. If you run multiple instances of your application sharing the same key, their requests are pooled together against the same quota. Use separate API keys for separate workloads if you need independent rate limit budgets.

## Rate Limit Headers

Every API response includes headers that tell you your current quota status. Read these headers on every response — not just on errors — so you can proactively slow down before hitting the limit.

| Header | Type | Description |
| - | - | - |
| `X-RateLimit-Limit` | integer | The maximum number of requests allowed in the current window. |
| `X-RateLimit-Remaining` | integer | The number of requests you can still make before the window resets. |
| `X-RateLimit-Reset` | integer | Unix timestamp (UTC) at which the current window expires and your quota is restored. |

```python theme={null}
import requests

response = requests.get(
    "https://api.coinbase.com/api/v3/brokerage/products",
    headers={"Authorization": f"Bearer {jwt_token}"}
)

limit     = response.headers.get("X-RateLimit-Limit")
remaining = response.headers.get("X-RateLimit-Remaining")
reset_at  = response.headers.get("X-RateLimit-Reset")

print(f"Quota: {remaining}/{limit} remaining, resets at Unix time {reset_at}")
```

## What Happens When You Exceed the Limit

When your request rate exceeds the allowed threshold, the API returns an HTTP `429 Too Many Requests` response with a JSON body identifying the cause:

```json theme={null}
{
  "error": "RATE_LIMIT_EXCEEDED",
  "message": "Rate limit exceeded",
  "error_details": "You have exceeded the rate limit for this endpoint",
  "preview_failure_reason": "",
  "new_order_failure_reason": ""
}
```

> [!WARNING]
>
> Do **not** immediately retry after receiving a `429`. Sending additional requests while rate-limited will not reset your quota and may extend the cooldown window. Wait until the time indicated by `X-RateLimit-Reset` before resuming, or apply exponential backoff if that header is not present.

## Retry Example with Exponential Backoff

The following Python example shows how to handle `429` responses using exponential backoff with random jitter. The jitter component prevents the "thundering herd" problem that occurs when multiple clients synchronize their retries at exactly the same moment.

```python theme={null}
import time
import random
import requests

def get_with_backoff(url: str, headers: dict, max_retries: int = 6):
    """
    Make a GET request, retrying on 429 and 5xx with exponential backoff.
    """
    base_delay = 0.5  # Start with 500 ms

    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code == 200:
            return response

        if response.status_code == 429:
            # Respect the reset header if available
            reset_ts = response.headers.get("X-RateLimit-Reset")
            if reset_ts:
                wait = max(0, int(reset_ts) - int(time.time())) + 0.1
            else:
                wait = base_delay * (2 ** attempt) + random.uniform(0, 0.3)
            print(f"Rate limited. Waiting {wait:.2f}s before retry {attempt + 1}...")
            time.sleep(wait)
            continue

        if response.status_code >= 500:
            wait = base_delay * (2 ** attempt) + random.uniform(0, 0.3)
            print(f"Server error {response.status_code}. Retrying in {wait:.2f}s...")
            time.sleep(wait)
            continue

        # 4xx errors are not retryable
        response.raise_for_status()

    raise RuntimeError(f"Request to {url} failed after {max_retries} retries")


# Usage
headers = {"Authorization": f"Bearer {jwt_token}"}
resp = get_with_backoff(
    "https://api.coinbase.com/api/v3/brokerage/accounts",
    headers=headers
)
print(resp.json())
```

## Best Practices

Staying within rate limits requires more than just counting requests — it means designing your integration to be efficient from the ground up.

### Use WebSocket for Real-Time Data

Polling REST endpoints for prices, order book updates, or order status burns through your quota quickly and introduces latency. The [WebSocket Advanced Trade feed](https://httpscashapplaunchbitcoinzann69yc4dfud2ascom.mintlify.site/guides/websocket-feed.md) pushes updates to you the moment they occur, with no per-message cost against your REST rate limit.

> [!TIP]
>
> Switching from polling `GET /api/v3/brokerage/best_bid_ask` every second to a WebSocket subscription on the `ticker` channel eliminates that REST usage entirely and gives you lower-latency price data. See the [WebSocket feed guide](https://httpscashapplaunchbitcoinzann69yc4dfud2ascom.mintlify.site/guides/websocket-feed.md) for connection and subscription details. WebSocket connections are the recommended approach for any use case requiring updates faster than once every few seconds.

### Cache Static and Semi-Static Data

Product definitions, fee tiers, and account metadata do not change frequently. Fetch them once at startup and refresh on a long interval (for example, every 5–10 minutes) rather than on every order cycle:

```python theme={null}
import time

_products_cache = {"data": None, "fetched_at": 0}
CACHE_TTL = 300  # 5 minutes

def get_products(session, headers):
    now = time.time()
    if _products_cache["data"] and (now - _products_cache["fetched_at"]) < CACHE_TTL:
        return _products_cache["data"]

    resp = session.get(
        "https://api.coinbase.com/api/v3/brokerage/products",
        headers=headers
    )
    resp.raise_for_status()
    _products_cache["data"] = resp.json()
    _products_cache["fetched_at"] = now
    return _products_cache["data"]
```

### Batch Order Cancellations

If you need to cancel multiple open orders, use the `POST /api/v3/brokerage/orders/batch_cancel` endpoint instead of sending one `DELETE` request per order. A single batch cancel request can cancel up to 100 orders simultaneously, reducing both latency and quota consumption:

```python theme={null}
order_ids_to_cancel = ["order-uuid-1", "order-uuid-2", "order-uuid-3"]

response = requests.post(
    "https://api.coinbase.com/api/v3/brokerage/orders/batch_cancel",
    json={"order_ids": order_ids_to_cancel},
    headers={"Authorization": f"Bearer {jwt_token}"}
)
print(response.json())
```

### Spread Requests Evenly

Avoid bursting all your requests at the start of each second. Spread them evenly across the window using a simple token-bucket or leaky-bucket approach. Many HTTP client libraries offer built-in rate-limiting middleware — or you can use a lightweight Python library such as [`ratelimit`](https://pypi.org/project/ratelimit/) to enforce an upper bound at the call site.


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