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

# Rate Limits

> Understanding API rate limits and best practices

# Rate Limits

Mixpanel enforces rate limits on API endpoints to ensure system stability and fair usage across all customers.

## Rate Limits by API

### Ingestion API

<ResponseField name="Import Events" type="/import">
  **Rate Limit:** No hard limit, but 429 errors may be returned during high load

  **Best Practice:** Implement exponential backoff on 429 errors
</ResponseField>

<ResponseField name="Track Events" type="/track">
  **Rate Limit:** No hard limit for reasonable usage

  **Recommendation:** Batch events when possible (up to 2000 events per request)
</ResponseField>

### Query API

<ResponseField name="Query Endpoints" type="All query endpoints">
  **Rate Limit:** 60 queries per hour, 5 concurrent queries maximum

  **Per Second:** 3 queries per second

  **Status Code:** 429 Too Many Requests
</ResponseField>

Applies to:

* `/api/query/insights`
* `/api/query/funnels`
* `/api/query/retention`
* `/api/query/segmentation`
* `/api/query/cohorts`
* `/api/query/engage`

### Export API

<ResponseField name="Raw Export" type="/export">
  **Rate Limit:** 60 queries per hour, 3 queries per second, 100 concurrent queries maximum

  **Status Code:** 429 Too Many Requests
</ResponseField>

### Management APIs

<ResponseField name="Annotations, Schemas, Service Accounts" type="Management endpoints">
  **Rate Limit:** Standard HTTP rate limiting applies

  **Recommendation:** Space out requests, avoid burst traffic
</ResponseField>

## Request Limits

### Ingestion Payload Limits

| Endpoint  | Max Payload Size | Max Events per Request |
| --------- | ---------------- | ---------------------- |
| `/import` | 2 MB             | 2000 events            |
| `/track`  | 2 MB             | 2000 events            |
| `/engage` | 2 MB             | 2000 profile updates   |
| `/groups` | 2 MB             | 2000 group updates     |

### Query Result Limits

| Endpoint                       | Default Limit | Max Limit                         |
| ------------------------------ | ------------- | --------------------------------- |
| `/export`                      | All events    | 100,000 events (with limit param) |
| `/engage`                      | 1000 profiles | 10,000 profiles                   |
| `/segmentation` (on parameter) | 60 values     | 10,000 values                     |

## Handling Rate Limits

### Detecting Rate Limit Errors

When you exceed rate limits, Mixpanel returns a `429 Too Many Requests` response:

<CodeGroup>
  ```json Response Example theme={null}
  {
    "code": 429,
    "error": "Project exceeded rate limits. Please retry the request with exponential backoff.",
    "status": "Too Many Requests"
  }
  ```
</CodeGroup>

### Implementing Exponential Backoff

<CodeGroup>
  ```python Python theme={null}
  import time
  import requests

  def import_with_backoff(events, max_retries=5):
      base_delay = 1  # Start with 1 second
      
      for attempt in range(max_retries):
          response = requests.post(
              'https://api.mixpanel.com/import',
              auth=('SERVICE_ACCOUNT', 'SECRET'),
              params={'project_id': 'PROJECT_ID', 'strict': '1'},
              json=events
          )
          
          if response.status_code == 200:
              return response.json()
          elif response.status_code == 429:
              if attempt < max_retries - 1:
                  delay = base_delay * (2 ** attempt)  # Exponential backoff
                  print(f"Rate limited. Retrying in {delay} seconds...")
                  time.sleep(delay)
              else:
                  raise Exception("Max retries exceeded")
          else:
              response.raise_for_status()
  ```

  ```javascript JavaScript theme={null}
  async function importWithBackoff(events, maxRetries = 5) {
    const baseDelay = 1000; // Start with 1 second
    
    for (let attempt = 0; attempt < maxRetries; attempt++) {
      const response = await fetch('https://api.mixpanel.com/import?project_id=PROJECT_ID&strict=1', {
        method: 'POST',
        headers: {
          'Authorization': 'Basic ' + btoa('SERVICE_ACCOUNT:SECRET'),
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(events)
      });
      
      if (response.status === 200) {
        return await response.json();
      } else if (response.status === 429) {
        if (attempt < maxRetries - 1) {
          const delay = baseDelay * Math.pow(2, attempt);
          console.log(`Rate limited. Retrying in ${delay}ms...`);
          await new Promise(resolve => setTimeout(resolve, delay));
        } else {
          throw new Error('Max retries exceeded');
        }
      } else {
        throw new Error(`HTTP ${response.status}: ${await response.text()}`);
      }
    }
  }
  ```
</CodeGroup>

## Best Practices

<AccordionGroup>
  <Accordion title="Batch Your Requests">
    Instead of sending individual events, batch them into arrays of up to 2000 events per request. This significantly reduces the number of API calls.

    ```python theme={null}
    # Good: Batched
    events = [event1, event2, event3, ...] # Up to 2000
    response = requests.post(url, json=events)

    # Bad: Individual
    for event in events:
        response = requests.post(url, json=[event])
    ```
  </Accordion>

  <Accordion title="Implement Retry Logic">
    Always implement exponential backoff retry logic for 429 errors. Start with a 1-second delay and double it with each retry.
  </Accordion>

  <Accordion title="Cache Query Results">
    If you're querying the same data frequently, cache the results on your side instead of making repeated API calls.
  </Accordion>

  <Accordion title="Use Webhooks for Real-time Data">
    For real-time updates, consider using Mixpanel's webhook features instead of polling the Query API.
  </Accordion>

  <Accordion title="Compress Payloads">
    Use gzip compression for large payloads to reduce transfer time and stay within size limits:

    ```python theme={null}
    import gzip
    import json

    compressed = gzip.compress(json.dumps(events).encode())
    response = requests.post(
        url,
        data=compressed,
        headers={'Content-Encoding': 'gzip'}
    )
    ```
  </Accordion>

  <Accordion title="Monitor Your Usage">
    Track your API usage to identify patterns and optimize before hitting rate limits.
  </Accordion>
</AccordionGroup>

## Rate Limit Headers

Mixpanel may include rate limit information in response headers (implementation varies by endpoint):

```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1609459200
```

<Note>
  Not all endpoints currently return rate limit headers. Use 429 status codes as the primary indicator.
</Note>

## Increasing Rate Limits

If you have a legitimate need for higher rate limits:

1. Contact Mixpanel Support
2. Describe your use case
3. Provide expected traffic patterns
4. Enterprise customers may have higher default limits

<Warning>
  Rate limit increases are evaluated on a case-by-case basis and are not guaranteed.
</Warning>

## Common Scenarios

### High-Volume Event Ingestion

**Problem:** Sending millions of events per day

**Solution:**

* Batch events in groups of 2000
* Use multiple threads/workers with proper backoff
* Consider using Data Pipelines for bulk imports

### Frequent Dashboard Updates

**Problem:** Polling Query API every minute for dashboard data

**Solution:**

* Cache results and refresh less frequently
* Use longer time windows to reduce query complexity
* Consider using Mixpanel's embedding features

### Bulk Data Export

**Problem:** Exporting large date ranges frequently

**Solution:**

* Use Data Pipelines for scheduled exports
* Limit export date ranges to reduce query time
* Export during off-peak hours
