Quickstart
Get an API key, list your datasets, describe one, query it and read the answer, in about five minutes.
By the end of this page you'll have a working API key and a real answer from one of your datasets: revenue by region over the last 30 days. It takes about five minutes and needs only cURL.
To see what the API answers before you have an account, open the playground. Its sample mode needs no key.
Before you start
A Halo account in an organization. Your role needs
api.use, which Owner, Admin, Builder and Analyst have by default, and Viewer and External guest don't.The Developer API on for your organization. It's off until an Owner or Admin turns it on. If it's off, the keys page says so and gives you a link to send them.
1. Make a key
Go to dev.usehalo.net/keys and choose Sign in with Halo. There's no separate developer account. Choose New key, give it a Name, and choose Make key. The defaults allow everything a key can do, and last 90 days. If you're in several organizations, pick one first.
Copy the key now. Halo keeps only a hash, so you won't see it again. Put it in your environment, pasting it at the prompt so it stays out of your shell history:
read -rs HALO_API_KEY && export HALO_API_KEY2. Ask who the key is
whoami needs no scope, so it's the first call to try.
curl -sS https://api.usehalo.net/v1/developer/whoami \
-H "Authorization: Bearer $HALO_API_KEY"{
"user": { "id": "…", "name": "Dana Reyes", "email": "dana@example.com" },
"organization": { "id": "…", "slug": "acme", "name": "Acme" },
"role": "Analyst",
"key": {
"id": "…",
"name": "First try",
"hint": "hak_x7Qp2a…",
"scopes": ["datasets.read", "data.query", "content.read"],
"temporary": false,
"expiresAt": "2027-02-01T15:04:05Z"
},
"limits": {
"requestsPerMinute": 240,
"rowBudget": { "perDay": 100000, "used": 0, "left": 100000 }
}
}The key acts as this person, in this organization, with their role as it is now.
3. List your datasets
curl -sS "https://api.usehalo.net/v1/developer/datasets?search=orders" \
-H "Authorization: Bearer $HALO_API_KEY"{
"items": [
{
"id": "0195f3a2-…",
"name": "Orders",
"description": "Orders by region and channel.",
"fieldCount": 5,
"connection": "Warehouse",
"marketplaceData": false,
"level": "view",
"updatedAt": "2026-10-28T14:02:11Z"
}
],
"truncated": false
}search is optional, and matches any of its words in names and descriptions. A list holds up to 200 items, and truncated says more match, so search with more specific words. Keep the id you want:
export DATASET_ID="paste the id here"4. Describe the dataset
curl -sS "https://api.usehalo.net/v1/developer/datasets/$DATASET_ID" \
-H "Authorization: Bearer $HALO_API_KEY"{
"defaultDateField": "order_date",
"fields": [
{ "name": "order_date", "label": "Order date", "role": "dimension", "type": "date" },
{ "name": "region", "label": "Region", "role": "dimension", "type": "string" },
{ "name": "channel", "label": "Channel", "role": "dimension", "type": "string" },
{ "name": "revenue", "label": "Revenue", "role": "measure", "type": "number", "aggregate": "sum", "format": "currency:USD" },
{ "name": "orders", "label": "Orders", "role": "measure", "type": "integer", "aggregate": "sum" }
]
}name is what queries use. Use your own dataset's field names in the next step.
5. Query it
curl -sS -X POST "https://api.usehalo.net/v1/developer/datasets/$DATASET_ID/query" \
-H "Authorization: Bearer $HALO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dimensions": [{ "field": "region" }],
"measures": [{ "field": "revenue" }],
"filters": [{
"field": "order_date",
"op": "date_range",
"range": { "kind": "relative", "preset": "last_n_days", "n": 30 }
}],
"sorts": [{ "field": "revenue", "direction": "desc" }],
"limit": 10
}'6. Read the answer
{
"columns": [
{ "key": "region", "field": "region", "label": "Region", "role": "dimension", "type": "string" },
{ "key": "revenue", "field": "revenue", "label": "Revenue", "role": "measure", "type": "number", "aggregate": "sum", "format": "currency:USD" }
],
"rows": [
["North", 182340.5],
["West", 151220],
["South", 98410.25]
],
"stats": {
"durationMs": 84,
"rowCount": 3,
"hasMore": false,
"cached": false,
"computedAt": "2026-11-03T15:04:05Z",
"timeZone": "America/Denver"
},
"warnings": []
}Each row is an array in the order of columns, so North took in 182,340.50. stats.hasMore says more rows follow, stats.timeZone says the zone last_n_days resolved in, and warnings holds anything Halo wants you to know about the result.
If it didn't work
| Answer | Means | What to do |
|---|---|---|
401 invalid_api_key | The key is wrong, expired or revoked | Check the variable is set, or make another |
403 api_disabled | The Developer API is off for your organization | Ask an Owner or Admin to turn it on |
403 export_not_allowed | The role can't export, and rows through the API count as exporting | Ask an Admin for a role with data.export |
403 marketplace_data_not_available | It's Marketplace data, whose rows stay in Halo | Query one of your own datasets |
404 not_found | No such id, or you don't have access | Check the id with step 3 |
422 invalid_query | The query doesn't fit the dataset, such as a field it doesn't have | Read detail, and check names with step 4 |
Every code is in Errors.
Next
Query data: filters, grains, paging, counts and totals.
Authentication: scopes, and how a key acts as its person.
Choose an integration: whether REST is the right tool for the job.