Get started with the API
In about five minutes you'll get numbers for a place from your own code. You need a Kadrin account (the beta is by invitation) and one of: curl, Python 3 with the requests package (pip install requests), or Node.js 18 or later.
1. Make a test key
Sign in to Kadrin Batch, open Keys, and make a test key. A test key is free and gives made-up numbers, in exactly the shape of real ones, so you can build and try things without spending credits. Copy it: we show it only once.
Keep the key in an environment variable rather than in your code:
export KADRIN_API_KEY="kdr_test_..."
Call the API from your own computer or server, never from a web page: anyone can read a key in a page.
2. Check that your key works
Ask how many credits you have. This is free.
curl https://api.kadrindata.com/v1/usage -H "Authorization: Bearer $KADRIN_API_KEY"
import os
import requests
r = requests.get(
"https://api.kadrindata.com/v1/usage",
headers={"Authorization": f"Bearer {os.environ['KADRIN_API_KEY']}"},
)
r.raise_for_status()
print(r.json())
const r = await fetch("https://api.kadrindata.com/v1/usage", {
headers: { Authorization: `Bearer ${process.env.KADRIN_API_KEY}` },
});
if (!r.ok) throw new Error(await r.text());
console.log(await r.json());
To run the JavaScript, save it in a file ending .mjs, such as usage.mjs, and run node usage.mjs. You'll see something like:
{"balance": 50000, "free_credits_remaining_today": 50, "daily_cap": null, "days": []}
3. Get numbers for a place
Ask for the population and median household income within 3 miles of Boston City Hall. A place goes in areas, with an id you choose and its geometry: here a buffer, which is a radius around a point given as [longitude, latitude].
curl https://api.kadrindata.com/v1/enrich \
-H "Authorization: Bearer $KADRIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"areas": [{"id": "city-hall",
"geometry": {"type": "buffer", "center": [-71.0589, 42.3601], "radius": 3, "unit": "mi"}}],
"variables": ["demographics.population.total", "income.household.median"]
}'
import os
import requests
r = requests.post(
"https://api.kadrindata.com/v1/enrich",
headers={"Authorization": f"Bearer {os.environ['KADRIN_API_KEY']}"},
json={
"areas": [{"id": "city-hall",
"geometry": {"type": "buffer", "center": [-71.0589, 42.3601], "radius": 3, "unit": "mi"}}],
"variables": ["demographics.population.total", "income.household.median"],
},
)
r.raise_for_status()
for measure, v in r.json()["results"][0]["values"].items():
print(f"{measure}: {v['value']:,.0f} {v['unit']}, give or take {v['moe']:,.0f}")
const r = await fetch("https://api.kadrindata.com/v1/enrich", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KADRIN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
areas: [{ id: "city-hall",
geometry: { type: "buffer", center: [-71.0589, 42.3601], radius: 3, unit: "mi" } }],
variables: ["demographics.population.total", "income.household.median"],
}),
});
if (!r.ok) throw new Error(await r.text());
const { results } = await r.json();
for (const [measure, v] of Object.entries(results[0].values)) {
console.log(`${measure}: ${v.value.toLocaleString()} ${v.unit}, give or take ${v.moe.toLocaleString()}`);
}
The answer looks like this, shortened, from a live key. The numbers here are made up, as a test key's are; a live key gets real ones.
{
"request_id": "req_5239cfca03d24a6bb6a0",
"release": "2026.11.1",
"credits_used": 2,
"results": [{
"id": "city-hall",
"area": {"type": "buffer", "area_km2": 73.23, "units_intersected": 112, "units_fully_contained": 81},
"values": {
"demographics.population.total": {"value": 46147, "unit": "people", "aggregation": "sum",
"source": "acs5", "vintage": "2020-2024", "moe": 3236, "resolution": "block_group"},
"income.household.median": {"value": 78390, "unit": "dollars", "aggregation": "interpolated_median",
"source": "acs5", "vintage": "2020-2024", "moe": 5538, "resolution": "block_group"}
}
}],
"sources": [{"id": "acs5", "name": "U.S. Census Bureau, American Community Survey 5-year estimates",
"vintage": "2020-2024"}]
}
4. Read the answer
Each measure in values has:
value: the number for your place, in itsunit.moe: its margin of error (how far the true number could reasonably be from our estimate). The true number is very likely between value − moe and value + moe. Counts of jobs and businesses have none, since they aren't survey estimates.sourceandvintage: where the number comes from and the years it describes.sourcesat the end names each source in full, with its license.aggregation: how the number was combined for your shape. Counts are added up; a median is worked out again from the full spread of incomes, never averaged. How we estimate any shape explains why.
area says how big the place is and how many official areas it touches. A number you should know something about before relying on it comes with a warning, such as high_cv when few households were surveyed: see Errors and warnings.
The headers say what the request cost: Kadrin-Credits-Used and Kadrin-Credits-Remaining. This one used 2 credits, one for each group of measures. A test key is never charged.
5. See the cost first
Set options.dry_run to true to find out what a request would cost, for free. This one asks for a site report, a ready-made set of measures, for a 10-minute drive-time area: everywhere you can reach by car in 10 minutes.
curl https://api.kadrindata.com/v1/enrich \
-H "Authorization: Bearer $KADRIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"areas": [{"id": "city-hall",
"geometry": {"type": "isochrone", "center": [-71.0589, 42.3601], "mode": "drive", "minutes": 10}}],
"preset": "site_report",
"options": {"dry_run": true}
}'
import os
import requests
r = requests.post(
"https://api.kadrindata.com/v1/enrich",
headers={"Authorization": f"Bearer {os.environ['KADRIN_API_KEY']}"},
json={
"areas": [{"id": "city-hall",
"geometry": {"type": "isochrone", "center": [-71.0589, 42.3601], "mode": "drive", "minutes": 10}}],
"preset": "site_report",
"options": {"dry_run": True},
},
)
r.raise_for_status()
print("This would cost", r.json()["estimated_credits"], "credits")
const r = await fetch("https://api.kadrindata.com/v1/enrich", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KADRIN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
areas: [{ id: "city-hall",
geometry: { type: "isochrone", center: [-71.0589, 42.3601], mode: "drive", minutes: 10 } }],
preset: "site_report",
options: { dry_run: true },
}),
});
if (!r.ok) throw new Error(await r.text());
console.log("This would cost", (await r.json()).estimated_credits, "credits");
It costs 10 credits: 8 for the report's groups of measures, and 2 to draw the drive-time area. Take out dry_run to run it. A site report also compares each number with the county, the state and the US, in each value's benchmarks.
6. Go live
When you're ready for real numbers, make a live key on the same Keys page. A live key uses your account's credits, starting with the 50 free credits each day. To be sure one request never costs more than you expect, send the most it may cost in the Kadrin-Max-Credits header: a request that would cost more is refused, for nothing.
Each key can make 60 requests a minute to /enrich, with up to 100 places in each. For more places, send a file: see Send a file of places.
Next
- Measures: everything you can ask for, by topic.
- API reference: every endpoint and field.
- Errors and warnings: what each one means and what to do.