Measure metrics
Measures up to 20 metrics over a time range, each narrowed by filters and split into groups: MRR, revenue, or the count or unique people of one of your events, by store, product, billing period, plan, a person's trait (grouped into bins when it is a number) or an event property.
MRR and revenue count what the built-in charts count, so a split's groups add up to its total. A trait or plan is taken as it stood at the time: when an event or charge happened, or at the end of each period for MRR. Rows without a value form the $unknown group. When a query reads a trait, a group of fewer than five people is summed under $other, and a result narrowed below five people is withheld. Money is in integer minor units of currency, the organization's reporting currency. A secret key reports its own environment; a person's own credentials choose with x-environment: production (the default), test or all.
Guide: Analytics overview
A project secret key: vh_sk_... for production, vh_sk_test_... for test. Keep it on your servers.
In: header
Path parameters
The project's id, proj_.... A secret key works only in the project it belongs to.
Request body
application/json
TypeScript definitions
Use the request body type in TypeScript.
The metrics to measure, 1 to 20.
1 <= items <= 20Examples
curl -X POST "https://api.voidhash.com/api/v1/projects/proj_tz4a98xxat96iws9zmbrgj3a/analytics/reports/metrics" \ -H "Authorization: Bearer vh_sk_..." \ -H "Content-Type: application/json" \ -d '{ "queries": [ { "key": "mrr-by-age", "metric": { "kind": "mrr" }, "timeRange": { "preset": "custom", "start": "2026-01-01T00:00:00.000Z", "end": "2026-03-31T23:59:59.999Z" }, "granularity": "month", "filters": [ { "dimension": { "kind": "store" }, "op": "in", "values": [ "apple-app-store" ] } ], "split": { "kind": "trait", "key": "age", "bins": [ 18, 25, 35, 45 ] } } ] }'{ "results": [ { "key": "mrr-by-age", "resolvedTimeRange": { "start": "2026-01-01T00:00:00.000Z", "end": "2026-03-31T23:59:59.999Z" }, "currency": "USD", "result": { "unit": "money", "buckets": [ "2026-01-01T00:00:00.000Z", "2026-02-01T00:00:00.000Z", "2026-03-01T00:00:00.000Z" ], "total": [ 912400, 948100, 990250 ], "series": [ { "key": "18–24", "label": null, "values": [ 301200, 318400, 342100 ] }, { "key": "25–34", "label": null, "values": [ 402500, 410300, 421950 ] }, { "key": "35–44", "label": null, "values": [ 121700, 127300, 131600 ] }, { "key": "$other", "label": null, "values": [ 9800, 9800, 10400 ] }, { "key": "$unknown", "label": null, "values": [ 77200, 82300, 84200 ] } ], "isWithheld": false, "hasOverlappingGroups": false } } ]}{ "error": { "type": "invalid_request", "code": "invalid_report_request", "message": "At most 20 insight queries are allowed per batch" }}{ "error": { "type": "unauthenticated", "code": "invalid_api_key", "message": "This secret key is not valid." }}{ "error": { "type": "permission_denied", "code": "permission_denied", "message": "Insufficient permissions." }}{ "error": { "type": "not_found", "code": "project_not_found", "message": "There is no project proj_tz4a98xxat96iws9zmbrgj3a, or this credential cannot use it.", "param": "projectId" }}{ "error": { "type": "internal", "code": "internal_error", "message": "Something went wrong on our side. Try again, and contact support with the Request-Id header if it keeps failing." }}Response body
application/json
application/json
application/json
application/json
application/json
application/json
Errors
| Status | type | code | When |
|---|---|---|---|
| 400 | invalid_request | invalid_report_request | With a message saying what to change. |
| 400 | invalid_request | invalid_time_range | With a message saying what to change. |
It can also fail with the errors every operation shares, such as unauthenticated; see Errors.
More in Analytics
- Compute insights
POST - Compute charts
POST - List the people behind a metric
POST - Report acquisition
POST - Report acquisition cohorts
POST - Report tracking health
POST - List live users
POST
Something wrong or missing on this page? Tell us by email or on Discord.