Skip to main content

Data API

Everything the interface shows arrives over an HTTP API, and the same API is open to your own tools: scripts, notebooks, a spreadsheet importer, another system's scheduler. This page is the reference for calling it with the personal API key the product hands you.

The credential

Your personal API key is on your profile page, masked, with controls to reveal, copy and regenerate it. Send it as an Authorization header, and note the scheme is the word Key, not Bearer:

Authorization: Key <your-api-key>

The key acts as you. Every call is checked against the same permissions your account has in the interface, so the API cannot show you a query, a result or a dashboard you could not open by clicking. Treat the key like a password, and regenerate it if it ever leaks; anything using the old key stops working immediately.

The ?api_key= query parameter works too, but only on the results endpoints: the result file URLs and POST .../results. Everywhere else, the instance's address accepts the header and nothing else, which keeps credentials out of URLs that get logged. Where both arrive on one request, the URL key is the one that gets read, so never send a stale URL key alongside a valid header.

Where to call

The API lives under your instance's own origin, at /api/node/. There is no separate API host, and no backend address to discover: the instance forwards the call and its credential to the query service for you. Connect → APIs in the product prints these values with your real address in them.

curl -H "Authorization: Key <your-api-key>" \
https://your-node.example.gov/api/node/queries

The endpoints

EndpointAnswers
GET /api/node/queriesSaved queries, paginated. page and page_size page it, q searches, and repeated tags narrow (?tags=a&tags=b)
GET /api/node/queries/<id>One query: its SQL, parameters, schedule, and the id of its latest result
POST /api/node/queries/<id>/resultsThe query's results, from cache or from a fresh run. See Running a query
GET /api/node/queries/<id>/results.jsonThe latest results as a file. Also .csv, .tsv and .xlsx
GET /api/node/query_results/<id>One specific result set, by the id a run handed back
GET /api/node/jobs/<job-id>An execution in progress. See Polling a run
GET /api/node/dashboardsDashboards, with the same pagination and search
GET /api/node/dashboards/<id>One dashboard, its widgets included

The same API also carries the query service's writes, creating and editing queries and dashboards under the same permission checks. The read surface above is the part an integration usually needs.

Running a query

POST /api/node/queries/<id>/results takes a JSON body, every field optional. max_age and parameters are below; a third, apply_auto_limit, overrides the saved query's own row-limit setting for this run.

curl -X POST \
-H "Authorization: Key <your-api-key>" \
-H "Content-Type: application/json" \
-d '{"max_age": 1800, "parameters": {"route_id": "12"}}' \
https://your-node.example.gov/api/node/queries/42/results

max_age decides whether a cached result is good enough:

max_ageWhat you get
Omitted, or -1Any cached result. The query only runs if there is none. A deployment that switches on the expired-results TTL caps "any" at that TTL
0A fresh run, always
NThe cached result if it is younger than N seconds, otherwise a fresh run

parameters maps each parameter name to its value. A parameterized query needs every parameter it declares: a missing value is refused with an error naming it, never quietly defaulted.

The response comes in one of two shapes. A cache hit answers with the result directly, under a query_result key. A fresh run answers with a job, because execution is asynchronous, and you poll it.

Polling a run

GET /api/node/jobs/<job-id> returns the job with a numeric status:

StatusMeans
1, 2, 6, 7Not done yet: queued, started, deferred or scheduled. Poll again
3Finished. query_result_id names the result; fetch it from /api/node/query_results/<id>
4Did not finish. The error string alongside says why, and a run someone cancelled reports here too, as Query cancelled by user

Result files

GET /api/node/queries/<id>/results.<format> returns the latest result as a download, in json, csv, tsv or xlsx.

These URLs accept the key in the URL itself, as ?api_key=, because a spreadsheet importer or a browser has nowhere to put a header. Two keys work there: your personal key, and the query's own results key, a per-query credential scoped to that one query, its definition included, and nothing beyond it. The API Key dialog on each query hands out the complete URLs with the query key already in them, which is the right credential to give out when the consumer should hold less than your account can reach.

What is not on this surface

  • AI clients connect over MCP instead, with the same personal key, and get a read-only tool surface scoped to saved queries and dashboards.
  • Anonymous consumers should not hold your key at all. A public published feed serves standard formats without a credential, and a share link or embed publishes one dashboard or visualization by token.