> ## 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.

# Query API Overview

> Retrieve and analyze your Mixpanel data programmatically

# Query API

The Query API allows you to retrieve data from your Mixpanel reports programmatically. Query the same data you see in the Mixpanel UI to build custom dashboards, exports, and integrations.

## Base URL

```
https://mixpanel.com/api/query
```

For EU data residency: `https://eu.mixpanel.com/api/query`

For India data residency: `https://in.mixpanel.com/api/query`

## Authentication

Use Service Account credentials with HTTP Basic Auth:

<CodeGroup>
  ```bash cURL theme={null}
  curl https://mixpanel.com/api/query/insights \
    -u SERVICE_ACCOUNT_USERNAME:SERVICE_ACCOUNT_SECRET \
    -d project_id=YOUR_PROJECT_ID \
    -d bookmark_id=12345
  ```
</CodeGroup>

## Rate Limits

<Warning>
  The Query API has strict rate limits:

  * **60 queries per hour**
  * **5 concurrent queries** maximum
  * **3 queries per second**

  Exceeding these limits returns a `429 Too Many Requests` error.
</Warning>

## Available Endpoints

### Insights

Query saved Insights reports to get event counts and trends.

[View Insights Documentation →](/api-reference/query/insights)

### Funnels

Retrieve funnel analysis data including conversion rates and drop-off points.

[View Funnels Documentation →](/api-reference/query/funnels)

### Retention

Query retention data for cohort and frequency analysis.

[View Retention Documentation →](/api-reference/query/retention)

### Cohorts

List cohorts and retrieve cohort membership information.

[View Cohorts Documentation →](/api-reference/query/cohorts)

## Common Parameters

Most Query API endpoints accept these common parameters:

<ParamField query="project_id" type="integer" required>
  Your Mixpanel project ID
</ParamField>

<ParamField query="workspace_id" type="integer">
  The workspace ID (optional, for workspace-specific queries)
</ParamField>

<ParamField query="from_date" type="string">
  Start date in `YYYY-MM-DD` format
</ParamField>

<ParamField query="to_date" type="string">
  End date in `YYYY-MM-DD` format
</ParamField>

## Response Format

All Query API responses follow a consistent structure:

<CodeGroup>
  ```json Success Response theme={null}
  {
    "computed_at": "2024-01-15T10:30:00+00:00",
    "status": "ok",
    "results": {
      // Endpoint-specific data
    }
  }
  ```
</CodeGroup>

## Best Practices

<AccordionGroup>
  <Accordion title="Cache query results">
    Since the Query API has low rate limits, cache results on your side:

    ```python theme={null}
    import time
    from functools import lru_cache

    @lru_cache(maxsize=100)
    def get_insights_data(project_id, bookmark_id, cache_time):
        # cache_time changes every hour to refresh data
        return fetch_insights(project_id, bookmark_id)

    # Call with current hour as cache key
    data = get_insights_data(123, 456, int(time.time() / 3600))
    ```
  </Accordion>

  <Accordion title="Use saved reports">
    Query saved reports (Insights, Funnels) rather than building queries from scratch. This ensures consistency with the UI and better performance.
  </Accordion>

  <Accordion title="Query during off-peak hours">
    For large exports or heavy queries, schedule them during off-peak hours to minimize impact on rate limits.
  </Accordion>

  <Accordion title="Monitor your query quota">
    Track your query usage to avoid hitting rate limits:

    ```python theme={null}
    import time
    from collections import deque

    class QueryTracker:
        def __init__(self):
            self.queries = deque(maxlen=60)
        
        def can_query(self):
            now = time.time()
            # Remove queries older than 1 hour
            while self.queries and now - self.queries[0] > 3600:
                self.queries.popleft()
            return len(self.queries) < 60
        
        def record_query(self):
            self.queries.append(time.time())
    ```
  </Accordion>
</AccordionGroup>

## Error Handling

<CodeGroup>
  ```python Python theme={null}
  import requests
  import time
  from requests.auth import HTTPBasicAuth

  def query_with_retry(url, params, max_retries=3):
      """Query with exponential backoff for rate limits"""
      
      for attempt in range(max_retries):
          response = requests.get(
              url,
              auth=HTTPBasicAuth('USERNAME', 'SECRET'),
              params=params
          )
          
          if response.status_code == 200:
              return response.json()
          elif response.status_code == 429:
              if attempt < max_retries - 1:
                  delay = 2 ** attempt * 60  # 1min, 2min, 4min
                  print(f"Rate limited. Waiting {delay}s...")
                  time.sleep(delay)
              else:
                  raise Exception("Rate limit exceeded")
          else:
              response.raise_for_status()
      
      raise Exception("Max retries exceeded")

  # Usage
  data = query_with_retry(
      'https://mixpanel.com/api/query/insights',
      {'project_id': 123, 'bookmark_id': 456}
  )
  ```
</CodeGroup>

## Finding Report IDs

### Insights Report ID (bookmark\_id)

1. Open your Insights report in Mixpanel
2. Look at the URL:
   ```
   https://mixpanel.com/project/{PROJECT_ID}/view/{WORKSPACE_ID}/app/boards#id=12345&editor-card-id="report-{BOOKMARK_ID}"
   ```
3. The `BOOKMARK_ID` is the number after `report-`

### Funnel ID

1. Open your Funnel report
2. Use the List Funnels endpoint to get all funnel IDs:
   ```bash theme={null}
   curl https://mixpanel.com/api/query/funnels/list \
     -u USERNAME:SECRET \
     -d project_id=YOUR_PROJECT_ID
   ```

### Workspace ID

1. Open any report in your workspace
2. Check the URL for the workspace ID:
   ```
   https://mixpanel.com/project/{PROJECT_ID}/view/{WORKSPACE_ID}/...
   ```

## Common Use Cases

### Custom Dashboard

<CodeGroup>
  ```python Python theme={null}
  import requests
  from requests.auth import HTTPBasicAuth

  class MixpanelDashboard:
      def __init__(self, username, secret, project_id):
          self.auth = HTTPBasicAuth(username, secret)
          self.project_id = project_id
          self.base_url = 'https://mixpanel.com/api/query'
      
      def get_daily_signups(self):
          """Get signup counts from Insights report"""
          response = requests.get(
              f'{self.base_url}/insights',
              auth=self.auth,
              params={
                  'project_id': self.project_id,
                  'bookmark_id': 12345  # Your signup report ID
              }
          )
          return response.json()
      
      def get_signup_funnel(self):
          """Get conversion funnel data"""
          response = requests.get(
              f'{self.base_url}/funnels',
              auth=self.auth,
              params={
                  'project_id': self.project_id,
                  'funnel_id': 7890,
                  'from_date': '2024-01-01',
                  'to_date': '2024-01-31'
              }
          )
          return response.json()

  # Usage
  dashboard = MixpanelDashboard('username', 'secret', 123)
  signups = dashboard.get_daily_signups()
  funnel = dashboard.get_signup_funnel()
  ```
</CodeGroup>

### Automated Reports

<CodeGroup>
  ```python Python theme={null}
  import requests
  import schedule
  import time
  from requests.auth import HTTPBasicAuth

  def send_daily_report():
      """Fetch Mixpanel data and send report"""
      
      # Query Mixpanel
      response = requests.get(
          'https://mixpanel.com/api/query/insights',
          auth=HTTPBasicAuth('username', 'secret'),
          params={
              'project_id': 123,
              'bookmark_id': 456
          }
      )
      
      data = response.json()
      
      # Process and send report
      send_email_report(data)
      print(f"Report sent: {data['computed_at']}")

  # Schedule daily at 9 AM
  schedule.every().day.at("09:00").do(send_daily_report)

  while True:
      schedule.run_pending()
      time.sleep(60)
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Insights" icon="chart-line" href="/api-reference/query/insights">
    Query event trends and metrics
  </Card>

  <Card title="Funnels" icon="filter" href="/api-reference/query/funnels">
    Analyze conversion funnels
  </Card>

  <Card title="Retention" icon="users" href="/api-reference/query/retention">
    Measure user retention
  </Card>

  <Card title="Cohorts" icon="layer-group" href="/api-reference/query/cohorts">
    Work with user cohorts
  </Card>
</CardGroup>
