JQL (JSON Query Language) is Mixpanel's query language for accessing and transforming event data without relying on pre-built reports. If you need to ask custom questions about user behavior—like "which premium users haven't performed a specific action in 30 days?" or "what's the distribution of plan types across regions?"—JQL lets you write those queries directly against your event stream.
What JQL Is and Why You Need It
JQL is a JavaScript-like syntax that Mixpanel uses to query, filter, and aggregate raw event data. It's the layer beneath Mixpanel's UI—when you build a report in Insights or Data Model, you're usually letting Mixpanel generate JQL behind the scenes.
Understand the Basic Structure
Every JQL query operates on data, which is an array of events. You filter, map, or aggregate using methods like filter(), groupBy(), and reduce(). Think of it as transforming raw events into the metric you actually care about. The query runs against events in your Mixpanel project and returns structured results.
// Filter events where users are on the 'premium' plan
data.filter(event => event.properties.plan == 'premium')
// Then group by country and sum revenue
.groupBy(['properties.country'], mixpanel.reducer.sum('properties.revenue'))Recognize When to Use JQL
Use JQL when Insights reports don't give you the exact slice you need. Common cases: filtering by multiple user properties at once, calculating custom ratios, or accessing raw event data for one-off analysis. If you're writing monthly dashboards or ad-hoc questions, JQL saves time versus building UI reports.
// Find users who viewed pricing but didn't sign up
data.filter(event => event.name == 'pricing_viewed')
.map(event => ({ user_id: event.user_id, timestamp: event.properties.timestamp }))
.reduce((acc, evt) => {
if (!acc[evt.user_id]) acc[evt.user_id] = 0;
acc[evt.user_id]++;
return acc;
}, {})Accessing JQL in Mixpanel
JQL queries are available through Mixpanel's web UI and REST API. Most users start in the dashboard, but power users write queries programmatically.
Write a Query in the Mixpanel Dashboard
Navigate to Data Model in your Mixpanel workspace. Click Create and select Custom Query. Paste your JQL script into the editor. Mixpanel validates syntax and shows results below. You can save queries for reuse and schedule exports.
// Query in Data Model dashboard
data.filter(event => event.properties.cohort == 'early_adopter' && event.properties.days_active > 30)
.groupBy(['event'], mixpanel.reducer.count())Query the JQL API Programmatically
Use the JQL REST API endpoint to run queries from your app or scripts. Send a POST request with your query script, project token, and authentication. The API returns structured JSON results that you can process further.
const axios = require('axios');
const jqlQuery = `data.filter(event => event.properties.source == 'mobile')
.groupBy(['properties.version'], mixpanel.reducer.count())`;
axios.post('https://data.mixpanel.com/api/2.0/jql', {
script: jqlQuery
}, {
headers: {
'Authorization': 'Bearer YOUR_SERVICE_ACCOUNT_TOKEN'
}
}).then(res => console.log(res.data));Common JQL Patterns and Methods
Most JQL queries follow a few standard patterns. Learning these will let you write custom analyses quickly.
Filter and Aggregate Events
Combine filter() with groupBy() to slice your events by properties and calculate metrics. Use mixpanel.reducer.count(), sum(), avg(), or custom reducers to compute the metric you need.
// Find average session duration by user segment
data.filter(event => event.name == 'session_end')
.groupBy(['properties.segment'],
mixpanel.reducer.avg('properties.duration_seconds'))Chain Multiple Filters
Stack filter() calls to apply multiple conditions. Each filter narrows the dataset. Filters are applied in order, so start with the broadest condition for performance.
data
.filter(event => event.properties.country == 'US')
.filter(event => event.properties.plan != 'free')
.filter(event => event.properties.signup_date > '2025-01-01')
.groupBy(['properties.plan'], mixpanel.reducer.count())map() to reshape events before aggregating—extract only the fields you need to reduce memory overhead on large queries.Common Pitfalls
- Assuming JQL is the only way to analyze data—for simple funnels, retention, or segmentation, Mixpanel's UI reports are faster and require no coding.
- Running queries without date filters on huge datasets—JQL will process all events in your project. Always include a
filter(event => event.timestamp > ...)condition. - Forgetting that JQL is not real-time. Data in Mixpanel has a 1–2 hour processing delay, so recent events won't appear in JQL results immediately.
- Writing inefficient queries with too many
groupBy()orreduce()steps—Mixpanel has execution limits. Start simple and add complexity only if needed.
Wrapping Up
JQL gives you the flexibility to ask custom questions about your user behavior without building a separate analytics infrastructure. It's powerful for one-off analysis, but if you're writing recurring reports or need real-time dashboards, consider combining JQL with Mixpanel's UI tools or exporting to a data warehouse. If you want to track this automatically across all your tools, Product Analyst can help.