Goals
Goal properties
The custom properties sent with one goal, and how often each value came.
curl --request GET \
--url 'https://truestat.io/api/v1/sites/{siteId}/goals/{goal}/properties' \
--header 'Authorization: Bearer <token>'{
"data": [
{
"goals": 4,
"value": "usd",
"property": "currency",
"visitors": 1
},
{
"goals": 2,
"value": "2000",
"property": "mrr_delta_cents",
"visitors": 1
}
],
"meta": {
"page": {
"limit": 100,
"offset": 0,
"has_more": false
},
"range": {
"to": "2026-09-16T00:00:00.000Z",
"tz": "UTC",
"from": "2026-08-17T00:00:00.000Z",
"clamped": false
},
"site_id": "6fed8980-a119-4a9c-a4d4-53e448ef9f9e"
}
}What was inside your conversions, not just how many there were.
The question this answers
Breakdowns by goal tells you signup
fired 412 times. This tells you what was in those 412 — which plan, which
form, which variant.
That is usually the number you actually wanted. "412 signups" is a figure; "310 on Starter, 102 on Pro" is a decision.
One row per value
Rows are per property value, not per property. A plan property with
three values is three rows, each naming the property it belongs to:
[
{ "property": "plan", "value": "starter", "visitors": 288, "goals": 310 },
{ "property": "plan", "value": "pro", "visitors": 97, "goals": 102 },
{ "property": "currency", "value": "usd", "visitors": 385, "goals": 412 }
]Group by property to get the breakdown per property back. The flat shape
means a caller iterating properties writes one loop rather than walking a
nested object.
visitors counts distinct people and goals counts times the goal fired, so
goals is larger wherever somebody converted more than once.
An unknown goal returns an empty list
Not a 404. Goal names are whatever your site sends — there is no registry to check a name against, so a name nothing has ever sent and a real goal that fired nothing in this window are indistinguishable, and both lead a caller to the same empty state.
Names containing / must be url-encoded, as any path segment must.
Goals without properties return nothing
A goal sent with no properties has no rows here, and that is the common case:
truestat("signup") sends a name and nothing else.
Properties come from the second argument — truestat("signup", { plan: "pro" })
— and from server-sent goals. See
custom events for how to attach them.
This needs goals:read
Not analytics:read, unlike most read endpoints.
The distinction is deliberate: this reads what your site attached to its conversions, which is more specific than traffic figures. A key issued to a reporting dashboard so it can read visitor counts should not also be able to read the contents of every signup event.
A key without the scope gets 403 insufficient_scope — the same key can
still call /overview and the breakdowns.
Authorizations
AuthorizationstringheaderrequiredAn API key from Settings → API keys. Scopes available: analytics:read, sites:read, sites:write, settings:read, settings:write, goals:read, goals:write, funnels:read, funnels:write, notes:read, notes:write, alerts:read, alerts:write, team:read, team:write, revenue:read, revenue:write.
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path parameters
siteIdstring (uuid)pathrequiredgoalstringpathrequiredQuery parameters
periodenum<string>querytodayyesterdaylast24hlast7dlast30dlast12mmonthyearallfromstring (date-time)querytostring (date-time)querytzstringquerydefault: UTCbotsenum<string>querydefault: excludedexcludedincludedlimitintegerquerydefault: 100offsetintegerquerydefault: 0Response
The answer.
dataunknownmetaobjecterrorobjecterrorobjecterrorobjecterrorobject