The Query API
The Query API is for when you know exactly the answer you want. You describe it: who to count, in which place, broken down by what, with which numbers for each group. Hiasynth computes it from the whole population, combining any attributes of people and of their homes in one query, and hands back one entry per group, ready for your dashboard, model or pipeline.
The tools answer a question in one call, written for an assistant to read. The Query API answers a specification, written for code to read. Reach for it when your question has a shape of its own: an audience defined by either-or conditions, an age pyramid crossed with income, every NUTS3 region of a country for a map, the share of each district's people living with an electric car, or the median household income of the children in each municipality.
Access comes with Builder
The Query API is part of Builder. Once Builder is on for your account, every API key on it can call the Query API. Create keys under API keys on your connector page. A key acts as your account, so keep it on your server.
What you can ask for
where with an any groupgroup_by with attributes, bands or geography"attribute": "age", "bins": [18, 30, 45, 65]"geo": "lau", "geo": "nuts3" …metricsrate metric"compare_to": "scope"limit and offsetYour first query
People in Munich by sex, with the median age of each:
curl https://mcp.hiasynth.co/v1/query \
-H "Authorization: Bearer $HIASYNTH_KEY" \
-H "Content-Type: application/json" \
-d '{
"place": "Munich",
"group_by": ["sex"],
"metrics": [{ "op": "median", "attribute": "age" }]
}'
The answer:
{
"fields": ["sex", "count", "share_pct", "median_age"],
"groups": [
{ "sex": "F", "count": 756864, "share_pct": 51, "median_age": 40 },
{ "sex": "M", "count": 727251, "share_pct": 49, "median_age": 38 }
],
"total": { "count": 1484115, "median_age": 39 },
"page": { "returned": 2, "available": 2, "suppressed": 0, "offset": 0, "next_offset": null },
"scope": {
"label": "München (city DE003C)", "level": "city", "population": 1484115,
"other_matches": [{ "label": "München (metro area DE003F)", "level": "metro", "code": "DE003F", "population": 2977659 }]
},
"unit": "people",
"min_cell": 100,
"data_vintage": 2024
}
other_matches shows that Munich also names a metro area of nearly three million. Send "code": "DE003F" to ask about that instead.
The address
https://mcp.hiasynth.coPOST /v1/queryPOST /v1/query/batch, up to 10GET /v1/attributes, freeGET /v1/versionGET /v1/openapi.json, publicAuthorization: Bearer YOUR_API_KEY, or an OAuth access tokenThe OpenAPI document describes every endpoint and schema. Load it into Postman, or generate a typed client from it with any OpenAPI generator.
Anatomy of a query
Every field is optional. An empty body counts everyone in Europe.
{
"unit": "people",
"place": "Stockholm",
"where": [ ... ],
"group_by": [ ... ],
"metrics": [ ... ],
"compare_to": "scope",
"order_by": { "field": "lift", "desc": true },
"limit": 100,
"offset": 0
}
unitis what each count counts:people(the default) orhouseholds. Attributes of people and of their homes work together whichever you count.- The place is
place, a name or code from a country down to a district, ornearwith aradius_km, orpostcode. Answers come back in seconds when a place is named. whereis who to count: conditions on attributes, combined with and, withanyandallgroups for or-logic.group_byis how to break the count down: categorical attributes, numeric bands and one geography level.metricsare the numbers to compute for each group, on top of the count and share that always come back.compare_toadds the population of each group, the audience's share of it, and the lift against the whole place.order_by,limitandoffsetsort the groups and page through them.
The reference covers every field, operator and limit. The recipes are complete queries for common jobs.
Reading the answer
groupsholds one entry per group, with its dimension values,count,share_pct(its share of everyone counted), and each metric under its name.fieldslists the names in each group, in order.totalis the same numbers for everyone counted, across all groups.pagesays how many groups there are and where the next page starts.next_offsetisnullon the last page.scopeis the place the query ran in, its level and population. When a name could mean several places,other_matcheslists the rest.notesexplain anything that shaped the numbers, such as a condition that leaves out children.- The
Hiasynth-Data-Versionresponse header names the population release that answered. Key your caches on it.
Privacy, built in
The population is synthetic, and it is fit to real statistics for real places. So the API protects the people behind places.
- A group of fewer than 100 people is withheld from
groupsand counted inpage.suppressed. When the withheld groups add up to fewer than 100 people, the next-smallest group is withheld with them, so the total never reveals a small group. - A metric computed from fewer than 100 values comes back as
null, with a note. - Health, religion, sexual orientation and political attributes answer in a place as small as a NUTS2 region and broken down by country. With a smaller place or a finer geographic breakdown they come back as
"suppressed": true. - When a whole query lands under the floor, the answer is
"suppressed": truewith arecoverythat says which lever to pull: the place or the audience.
Credits
A suppressed answer counts as an answer: the query ran, and the floor is the finding. Credits come from your account's allowance and packs, the same ones every Hiasynth tool draws on; see pricing.
Errors
Errors come back as JSON with an HTTP status your code can branch on, and a message written to be shown or logged as it is.
errorinvalid_query, invalid_args, invalid_jsonmissing_or_malformed_api_key, invalid_api_keyno_subscription, insufficient_creditsnot_found and similar, with resolved: falsecountryengine_busyquery_timeoutEvery query is read-only and idempotent, so retrying is always safe.