Cloudflare Analytics Engine: store and query metrics
By Flavio Copes
How to write custom events from a Worker with Analytics Engine and query them later with SQL. Cheap, high-volume time-series data.
Say you want to track every time someone uses a feature, then chart it later. You could write each event into your database, but that gets expensive and slow when there are millions of them.
Analytics Engine is built for exactly this. You write huge numbers of events, cheaply, and query them later with SQL. It’s a time-series store, the kind of thing you’d use for usage metrics and dashboards.
Set up a dataset
Your events go into a dataset. You don’t create it ahead of time, you just name it in wrangler.jsonc and it appears:
{
"analytics_engine_datasets": [
{ "binding": "ANALYTICS", "dataset": "my_app_events" }
]
}
Now env.ANALYTICS is ready in your Worker.
Write an event
You record an event with writeDataPoint. It takes three kinds of fields:
export default {
async fetch(request, env) {
env.ANALYTICS.writeDataPoint({
blobs: ['feature-used', 'pricing-page'],
doubles: [1],
indexes: ['user-123'],
})
return new Response('ok')
},
}
Let me explain the three fields, because the names aren’t obvious:
- blobs are strings. The text you want to store: an event name, a page, a country.
- doubles are numbers. Things you’ll add up or average, like a count or a duration.
- indexes is the value you’ll group and filter by most. Pick one, like a user id or tenant id.
Notice there’s no await. Writing is fire-and-forget, so it adds basically nothing to your response time. That’s the point.
Query with SQL
Later, you query the data with SQL through Cloudflare’s API. You send a query to an HTTP endpoint with an API token:
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/analytics_engine/sql" \
-H "Authorization: Bearer $API_TOKEN" \
-d "SELECT blob1 AS event, count() AS total
FROM my_app_events
WHERE timestamp > now() - INTERVAL '1' DAY
GROUP BY event"
A couple of things to notice. Your blobs come back as blob1, blob2, and so on, in the order you wrote them. Your doubles are double1, double2. And there’s a timestamp column for free on every event.
So this query counts each event type over the last day. Perfect for a dashboard.
A real use: per-tenant usage
In a SaaS app, I track usage per customer. The tenant id goes in indexes, the event in blobs, and a count in doubles:
env.ANALYTICS.writeDataPoint({
blobs: ['form-submission'],
doubles: [1],
indexes: [tenantId],
})
Then at the end of the month, one SQL query tells me how much each tenant used. No giant table in my main database, no slow queries.
When to use it
Analytics Engine is for events you have a lot of and want to analyze in aggregate: page views, feature usage, API calls, durations.
It’s not for data you need to read back exactly, one record at a time. That’s still D1 or KV. Analytics Engine is for “how many, how often, grouped by what.”
It’s cheap, it’s fast to write, and the SQL makes it easy to slice. The full reference is in the Analytics Engine docs.
Related posts about cloudflare: