You need custom event data that Mixpanel's UI doesn't surface. JQL (Jelly Query Language) lets you query your event stream and user profiles directly with filters and aggregations. Here's how to set it up and start extracting data.
Get Credentials and Authenticate
JQL queries run against Mixpanel's Data Export API, which requires your project token and API secret.
Copy your project credentials
In the Mixpanel dashboard, go to Settings > Project Details. Copy your Project Token and API Secret—you'll need both for authentication.
// Store credentials in environment variables
const projectToken = process.env.MIXPANEL_PROJECT_TOKEN;
const apiSecret = process.env.MIXPANEL_API_SECRET;Set up HTTP Basic Auth
JQL requests use HTTP Basic Auth where the username is your Project Token and password is your API Secret. Configure this in your HTTP client.
const axios = require('axios');
const httpAuth = {
username: projectToken,
password: apiSecret
};
// Use this auth object in all API requests
const headers = {
'Content-Type': 'application/json'
};Write and Execute Your First Query
JQL queries select columns in a data block and filter with optional where clauses.
Structure a basic event query
Start with a simple query that selects specific event properties. The data block defines which columns to return; the where clause filters by event name.
const jqlQuery = `
data {
event_name,
properties.$distinct_id,
properties.$browser,
time
}
where event == "Purchase"
limit 100
`;
console.log(jqlQuery);POST the query to the API
Send your JQL query to https://data.mixpanel.com/api/2.0/jql with the query string in the data property of the request body.
const response = await axios.post(
'https://data.mixpanel.com/api/2.0/jql',
{ data: jqlQuery },
{
auth: {
username: projectToken,
password: apiSecret
}
}
);
const events = response.data;
console.log(`Fetched ${events.length} events`);Process the results
The API returns a JSON array of event objects. Each object contains the properties you specified in your data block.
events.forEach(event => {
console.log(`User: ${event.properties.$distinct_id}`);
console.log(`Browser: ${event.properties.$browser}`);
console.log(`Time: ${event.time}`);
});
// Filter results in memory if needed
const highValueUsers = events.filter(e => e.properties.amount > 100);limit to test. JQL can return large datasets fast—test before querying millions of rows.Filter and Aggregate Data
Use where clauses with conditions and group by for summary statistics.
Add multiple filter conditions
Combine conditions with and / or. Filter by property values, date ranges, or specific user segments.
const jqlQuery = `
data {
event_name,
properties.$distinct_id,
properties.amount,
properties.plan_type,
time
}
where event == "Purchase"
and properties.amount > 50
and properties.plan_type in ["pro", "enterprise"]
and time >= "2026-01-01"
limit 500
`;
const response = await axios.post(
'https://data.mixpanel.com/api/2.0/jql',
{ data: jqlQuery },
{ auth: { username: projectToken, password: apiSecret } }
);Use aggregation functions for summaries
Return aggregated metrics (count, sum, average) by using functions in your data block. Pair with group by to segment results.
const jqlQuery = `
data {
plan = properties.plan_type,
event_count = count(),
total_revenue = sum(properties.amount),
avg_order_value = avg(properties.amount)
}
where event == "Purchase"
group by properties.plan_type
`;
const response = await axios.post(
'https://data.mixpanel.com/api/2.0/jql',
{ data: jqlQuery },
{ auth: { username: projectToken, password: apiSecret } }
);
response.data.forEach(row => {
console.log(`${row.plan}: ${row.event_count} purchases, $${row.total_revenue} revenue`);
});time >= now() - 30d for the last 30 days or time >= now() - 1h for the last hour.Common Pitfalls
- Forgetting that event and property names are case-sensitive.
event == "Purchase"andevent == "purchase"return different results. - Confusing event properties (
properties.amount) with user profile properties. Use the correct syntax for each context. - Running queries with no
limitor a very high limit on large datasets. Start withlimit 100and increase incrementally. - Not checking your actual event schema before writing queries. Non-existent properties return null, not errors.
Wrapping Up
You now have a working setup for querying Mixpanel events directly via JQL. Use this for custom reports, data exports, and analysis beyond the dashboard. If you want to track this automatically across tools, Product Analyst can help.