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:
curl https://mixpanel.com/api/query/insights \
-u SERVICE_ACCOUNT_USERNAME:SERVICE_ACCOUNT_SECRET \
-d project_id=YOUR_PROJECT_ID \
-d bookmark_id= 12345
Rate Limits
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.
Available Endpoints
Insights
Query saved Insights reports to get event counts and trends.
View Insights Documentation →
Funnels
Retrieve funnel analysis data including conversion rates and drop-off points.
View Funnels Documentation →
Retention
Query retention data for cohort and frequency analysis.
View Retention Documentation →
Cohorts
List cohorts and retrieve cohort membership information.
View Cohorts Documentation →
Common Parameters
Most Query API endpoints accept these common parameters:
The workspace ID (optional, for workspace-specific queries)
Start date in YYYY-MM-DD format
End date in YYYY-MM-DD format
All Query API responses follow a consistent structure:
{
"computed_at" : "2024-01-15T10:30:00+00:00" ,
"status" : "ok" ,
"results" : {
// Endpoint-specific data
}
}
Best Practices
Since the Query API has low rate limits, cache results on your side: 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 ))
Query saved reports (Insights, Funnels) rather than building queries from scratch. This ensures consistency with the UI and better performance.
Query during off-peak hours
For large exports or heavy queries, schedule them during off-peak hours to minimize impact on rate limits.
Track your query usage to avoid hitting rate limits: 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())
Error Handling
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 }
)
Finding Report IDs
Insights Report ID (bookmark_id)
Open your Insights report in Mixpanel
Look at the URL:
https://mixpanel.com/project/{PROJECT_ID}/view/{WORKSPACE_ID}/app/boards#id=12345&editor-card-id="report-{BOOKMARK_ID}"
The BOOKMARK_ID is the number after report-
Funnel ID
Open your Funnel report
Use the List Funnels endpoint to get all funnel IDs:
curl https://mixpanel.com/api/query/funnels/list \
-u USERNAME:SECRET \
-d project_id=YOUR_PROJECT_ID
Workspace ID
Open any report in your workspace
Check the URL for the workspace ID:
https://mixpanel.com/project/{PROJECT_ID}/view/{WORKSPACE_ID}/...
Common Use Cases
Custom Dashboard
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()
Automated Reports
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 )
Next Steps
Insights Query event trends and metrics
Funnels Analyze conversion funnels
Retention Measure user retention
Cohorts Work with user cohorts