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_KEY

2. 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

AnswerMeansWhat to do
401 invalid_api_keyThe key is wrong, expired or revokedCheck the variable is set, or make another
403 api_disabledThe Developer API is off for your organizationAsk an Owner or Admin to turn it on
403 export_not_allowedThe role can't export, and rows through the API count as exportingAsk an Admin for a role with data.export
403 marketplace_data_not_availableIt's Marketplace data, whose rows stay in HaloQuery one of your own datasets
404 not_foundNo such id, or you don't have accessCheck the id with step 3
422 invalid_queryThe query doesn't fit the dataset, such as a field it doesn't haveRead detail, and check names with step 4

Every code is in Errors.

Next