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

# Rate Limiting

> Understand Terminal49 API rate limits, HTTP 429 responses, and how to design clients that handle throttling with retries and exponential backoff.

## Overview

Terminal49 API implements rate limiting to ensure fair usage and maintain service quality for all users.

The default API limit is 100 requests per minute per API key/account on a rolling 60-second window. Some high-volume or expensive endpoints use their own bucket.

## Rate limit details

All limits apply per API key/account on a rolling 60-second window.

| Bucket                  | Endpoint                                  | Limit                            |
| ----------------------- | ----------------------------------------- | -------------------------------- |
| Default API requests    | All endpoints without a dedicated bucket  | 100 requests per minute          |
| Create Tracking Request | `POST /v2/tracking_requests`              | 100 tracking requests per minute |
| Infer Tracking Number   | `POST /v2/tracking_requests/infer_number` | 200 requests per minute          |
| Refresh Container       | `PATCH /v2/containers/{id}/refresh`       | 10 requests per minute           |

## Rate limit response

When you exceed the rate limit, the API will return:

**HTTP Status Code**: `429 Too Many Requests`

**Response Headers**:

* `Retry-After`: Number of seconds to wait before making another request against the same rate-limit bucket

`Retry-After` is the only rate-limit header the API documents. Base your 429 handling on it.

**Response Body**:

```json theme={null}
{
  "errors": [
    {
      "status": "429",
      "title": "Too Many Requests",
      "detail": "Your account has exceeded its API rate limit. Please reduce request frequency or contact support to increase your limit. Consider using webhooks for real-time updates instead of polling."
    }
  ]
}
```

## Best practices

### 1. Use webhooks instead of polling

The most effective way to avoid rate limits is to use **webhooks** for real-time updates instead of repeatedly polling the API:

* Configure webhooks to receive push notifications when shipment data changes
* Eliminates the need for frequent polling
* Provides instant updates without consuming your rate limit
* See the [Webhooks](/api-docs/in-depth-guides/webhooks) section for setup instructions

### 2. Implement exponential backoff

If you receive a `429` response:

1. Check the `Retry-After` header and, when present, wait at least that many seconds
2. If `Retry-After` is missing, back off exponentially (for example 1s, 2s, 4s) and add jitter so retries don't synchronize
3. Cap the number of retries and surface an error once the cap is reached
4. Don't retry immediately, as this will consume your limit further

### 3. Batch your requests

* Use list endpoints with filtering instead of multiple individual requests
* Leverage the [`include` parameter](/api-docs/in-depth-guides/including-resources) to fetch related resources in a single request
* Cache responses when appropriate to reduce redundant calls

### 4. Monitor your usage

* Track your request patterns
* Identify and optimize high-frequency operations
* Consider spreading requests over time rather than bursting

## Need a higher limit?

If your use case requires a higher rate limit:

1. **Evaluate webhook usage first** - Most polling use cases can be replaced with webhooks
2. **Contact support** at [support@terminal49.com](mailto:support@terminal49.com)
3. **Provide details** about your use case and expected request volume
4. **Our team will work with you** to find an appropriate solution

## Example: handling rate limits

Here's an example of how to properly handle rate limit responses in Python. It honors `Retry-After` when the server provides it and falls back to exponential backoff with jitter otherwise:

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

def make_request_with_retry(url, headers, max_retries=5):
    """
    Make an API request with automatic retry on rate limit.

    Honors the Retry-After header when present; otherwise falls back
    to exponential backoff with jitter.

    Args:
        url: The API endpoint URL
        headers: Request headers including Authorization
        max_retries: Maximum number of retry attempts

    Returns:
        Response object if successful

    Raises:
        Exception: If max retries exceeded
    """
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code != 429:
            # Return response for any other status code
            return response

        retry_after = response.headers.get('Retry-After')
        if retry_after is not None:
            # Honor the server's instruction
            wait_seconds = int(retry_after)
        else:
            # Exponential backoff with jitter: ~1s, 2s, 4s, 8s, ...
            wait_seconds = (2 ** attempt) + random.uniform(0, 1)

        print(f"Rate limited. Waiting {wait_seconds:.1f} seconds "
              f"(attempt {attempt + 1} of {max_retries})...")
        time.sleep(wait_seconds)

    raise Exception("Max retries exceeded")

# Example usage
headers = {
    'Authorization': 'Token YOUR_API_KEY'
}
response = make_request_with_retry(
    'https://api.terminal49.com/v2/shipments',
    headers
)
```

## Tips for high-volume applications

If you're building a high-volume application:

* **Design for webhooks from the start**: Don't rely on polling for data updates
* **Implement request queuing**: Spread your requests evenly across the rate limit window
* **Use pagination efficiently**: Fetch larger pages less frequently rather than small pages frequently
* **Cache aggressively**: Store and reuse data that doesn't change frequently
* **Honor `Retry-After`**: When a `429` response includes `Retry-After`, wait at least that many seconds before retrying against the same bucket.
