Report acquisition
Groups the people a network, campaign, ad set or ad brought in, or people by the surface they signed up on (web, iOS or Android), and reports what Voidhash measured for them on the web and in apps: visits, signups, measured app installs, trials, payers, revenue and refunds in one currency, the funnel by surface, where payers converted and the project's revenue coverage by credited touch.
A signup is credited to the person's last touch with an ad network before it within the attribution window (the project's, unless attributionWindowDays overrides it), else to their last touch of any kind in that time, or to a touch up to an hour after it; a signup without one is unattributed. The person is acquired on that touch's day and their later revenue stays with it. People count from the earliest of signing up and first paying or starting a trial, so someone who buys and creates an account later stays with the touch before the purchase; other anonymous visitors count only as visits. Revenue counts once on the person an identify merge keeps, wherever they pay, and only production revenue counts by default. Money is in integer minor units of currency, such as cents. People a touch reached only after they had signed up, such as customers reached by retargeting, are reported only as returningPeople, returningPayers and returningRevenue, which are never part of other revenue. selfReported lists what at least 2 unattributed people answered in the $acquisition_source person trait.
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.
Only people whose credited touch carried this ad id.
Only people whose credited touch carried this ad set id.
How many days, 1 to 90, before a signup a touch can be credited with it. Defaults to the project's attribution window.
Only people whose credited touch carried this campaign id.
The ISO 4217 code money is reported in. Amounts in another currency are converted at each day's exchange rate. Defaults to USD.
The environment whose revenue counts: production (the default), sandbox or test. Visits come from production keys, or from test keys for test; signups count in every environment.
The first acquisition day, YYYY-MM-DD, in the report time zone.
Whether every row also carries days, its outcomes per acquisition day.
Whether every row also carries signupSurfaces and paymentSurfaces, its outcomes by signup and payment surface.
The conversion ad cost is measured against: signup, trial or payer. Omitted or null uses the project's setting.
What rows group by: the credited touch's network, campaign, adset or ad, or surface, where people group by the surface they signed up on, or cross_surface.
Value in
- "network"
- "campaign"
- "adset"
- "ad"
- "surface"
Only people whose credited touch came from this ad network, such as meta or google. Compared in lowercase.
Which revenue, signups and installs count: the first days after each person's acquisition, or everything up to the end of the until day. Defaults to everything up to now, so recent people have had less time to pay.
The IANA time zone that sets day boundaries, such as the ad account's. Offsets such as +01:00 are rejected. Defaults to UTC.
The last acquisition day, inclusive, YYYY-MM-DD. The range spans at most 366 days.
Examples
curl -X POST "https://api.voidhash.com/api/v1/projects/proj_tz4a98xxat96iws9zmbrgj3a/analytics/reports/acquisition" \ -H "Authorization: Bearer vh_sk_..." \ -H "Content-Type: application/json" \ -d '{ "from": "2026-03-01", "to": "2026-03-31", "level": "network" }'{ "projectId": "proj_tz4a98xxat96iws9zmbrgj3a", "level": "network", "network": null, "campaignId": null, "adsetId": null, "adId": null, "currency": "USD", "timezone": "UTC", "environment": "production", "keyConversion": "payer", "from": "2026-03-01", "to": "2026-03-31", "start": "2026-03-01T00:00:00.000Z", "end": "2026-04-01T00:00:00.000Z", "revenueWindow": { "kind": "all" }, "attributionWindowDays": 7, "revenueEnd": "2026-04-02T08:00:00.000Z", "groups": [ { "peopleAcquired": 1046, "signups": 1046, "visits": 7020, "appInstalls": 1204, "trialStarters": 412, "trialConversions": 131, "payingPeople": 168, "crossSurfacePayers": 97, "keyConversions": 168, "signupToPayerRate": 0.1606, "returningPeople": 23, "returningPayers": 7, "returningRevenue": 6993, "grossRevenue": 268430, "refunds": 4995, "netRevenue": 263435, "network": "meta", "campaignId": null, "campaignName": null, "adsetId": null, "adsetName": null, "adId": null, "adName": null, "surface": null }, { "peopleAcquired": 214, "signups": 214, "visits": 1390, "appInstalls": 120, "trialStarters": 71, "trialConversions": 24, "payingPeople": 38, "crossSurfacePayers": 12, "keyConversions": 38, "signupToPayerRate": 0.1776, "returningPeople": 0, "returningPayers": 0, "returningRevenue": 0, "grossRevenue": 52962, "refunds": 0, "netRevenue": 52962, "network": "newsletter", "campaignId": null, "campaignName": null, "adsetId": null, "adsetName": null, "adId": null, "adName": null, "surface": null } ], "unattributed": { "peopleAcquired": 1880, "signups": 1880, "visits": 0, "appInstalls": 1702, "trialStarters": 604, "trialConversions": 198, "payingPeople": 251, "crossSurfacePayers": 4, "keyConversions": 251, "signupToPayerRate": 0.1335, "returningPeople": 0, "returningPayers": 0, "returningRevenue": 0, "grossRevenue": 391488, "refunds": 9990, "netRevenue": 381498 }, "totals": { "peopleAcquired": 3140, "signups": 3140, "visits": 8410, "appInstalls": 3026, "trialStarters": 1087, "trialConversions": 353, "payingPeople": 457, "crossSurfacePayers": 113, "keyConversions": 457, "signupToPayerRate": 0.1455, "returningPeople": 23, "returningPayers": 7, "returningRevenue": 6993, "grossRevenue": 712880, "refunds": 14985, "netRevenue": 697895 }, "selfReported": [ { "answer": "podcast", "people": 212, "netRevenue": 48951 }, { "answer": "friend", "people": 167, "netRevenue": 37962 }, { "answer": "app store search", "people": 94, "netRevenue": 19980 } ], "funnel": [ { "step": "visits", "count": 8410, "surfaces": [ { "surface": "web", "count": 8410 } ] }, { "step": "app_installs", "count": 1324, "surfaces": [ { "surface": "ios", "count": 1012 }, { "surface": "android", "count": 312 } ] }, { "step": "signups", "count": 1260, "surfaces": [ { "surface": "web", "count": 655 }, { "surface": "ios", "count": 470 }, { "surface": "android", "count": 135 } ] }, { "step": "trials", "count": 483, "surfaces": [ { "surface": "ios", "count": 371 }, { "surface": "android", "count": 112 } ] }, { "step": "payers", "count": 206, "surfaces": [ { "surface": "web", "count": 89 }, { "surface": "ios", "count": 98 }, { "surface": "android", "count": 19 } ] } ], "convertedOn": { "surfaces": [ { "surface": "web", "payingPeople": 112, "grossRevenue": 172540, "refunds": 4995, "netRevenue": 167545 }, { "surface": "ios", "payingPeople": 289, "grossRevenue": 456120, "refunds": 9990, "netRevenue": 446130 }, { "surface": "android", "payingPeople": 56, "grossRevenue": 84220, "refunds": 0, "netRevenue": 84220 } ], "crossSurfacePayers": 113, "journeys": [ { "touch": "web", "signup": "web", "payment": "ios", "payingPeople": 71 }, { "touch": "web", "signup": "ios", "payment": "ios", "payingPeople": 30 }, { "touch": "web", "signup": "web", "payment": "android", "payingPeople": 8 }, { "touch": null, "signup": "web", "payment": "ios", "payingPeople": 4 } ] }, "coverage": { "attributedRevenue": 528940, "totalRevenue": 1486220, "share": 0.3559, "netRevenue": 1458310, "classes": [ { "class": "paid", "network": "meta", "reason": null, "netRevenue": 401250, "share": 0.2751 }, { "class": "organic", "network": null, "reason": null, "netRevenue": 112640, "share": 0.0772 }, { "class": "none", "network": null, "reason": "no_touch", "netRevenue": 902380, "share": 0.6188 }, { "class": "none", "network": null, "reason": "outside_window", "netRevenue": 30120, "share": 0.0207 }, { "class": "none", "network": null, "reason": "no_person", "netRevenue": 11920, "share": 0.0082 } ] }}{ "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 | For a request over the size limits or in a currency without exchange rates for the period, with a message saying what to change. |
| 400 | invalid_request | invalid_time_range | For a bad range. |
It can also fail with the errors every operation shares, such as unauthenticated; see Errors.
More in Analytics
- Compute insights
POST - Compute charts
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.