VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Essentials
API overview
Authentication
Event capture
Push device API
Webhooks
Webhook delivery
Webhook events
Projects & keys
Projects
API keys
Audit log
Customers & access
Persons
Products
Perks
Sandbox
Paywalls
Paywalls
Paywall locations
Components
Component registry
Assets
Experiments & flags
Experiments
Feature flags
Analytics & events
Analytics
Analytics alerts
Cohorts
Group types
Groups
Events
Ingest policy
VoidQL
Tracking domains
Ads
Ads
Ad accounts
Meta measurement
Payments & stores
Payment providers
Payment provider products
Purchase history imports
Payment sources
Webhooks & notifications
Webhooks
Push notifications
Push configurations
Automations
Organization & team
Organizations
Members
Invitations
Billing
Your account
Profile
Session
Notices
Connected apps
Terms of Service
Developer tools
Schema
SDK
Compute insights
Compute charts
Measure metrics
List the people behind a metric
Report acquisition
Report acquisition cohorts
Report tracking health
List live users

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

Errors

StatustypecodeWhen
400invalid_requestinvalid_report_requestWith a message saying what to change.
400invalid_requestinvalid_time_rangeWith 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.

PreviousCompute chartsNextList the people behind a metric

On this page

AuthorizationPath parametersRequest bodyExamplesResponse bodyErrorsMore in Analytics
POST
/api/v1/projects/{projectId}/analytics/reports/metrics

Authorization

AuthorizationBearer <token>

A project secret key: vh_sk_... for production, vh_sk_test_... for test. Keep it on your servers.

In: header

Path parameters

projectId*string

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.

queries*array<>

The metrics to measure, 1 to 20.

Items1 <= items <= 20

Examples

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