Reference
API reference
Read published datasets with an API key.
OpenAPI document
This page is rendered from the OpenAPI 3.1 document, which is generated from the data API's implementation.
Read a page of a published dataset's rows.
GET/v1/datasets/{dataset_id}/rows
The Query Specification travels as JSON in the q parameter; a request with a matching If-None-Match is answered 304.
API key operationrows
Parameters
| Name | In | Required | Description |
|---|---|---|---|
dataset_id | path | Yes | The published dataset's ID. |
If-None-Match | header | No | The ETag of a page already held. |
q | query | No | The Query Specification as JSON; absent, the first page of every returnable field. |
Responses
| Status | Answer | Headers |
|---|---|---|
200 | A page of rows. | ETagX-Correlation-IDX-Credits-ConsumedX-Quota-Limit?X-Quota-Period?X-Quota-Remaining?X-Quota-Reset?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
304 | The page is unchanged since the ETag sent; a tenth of the credits. | ETagX-Correlation-IDX-Credits-ConsumedX-Quota-Limit?X-Quota-Period?X-Quota-Remaining?X-Quota-Reset?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
400 | query_invalidquery_malformed | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
401 | unauthenticated | WWW-AuthenticateX-Correlation-ID |
403 | operation_not_permitted | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
404 | not_found | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
413 | query_malformed | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
429 | rate_limitedquota_exceeded | Retry-AfterX-Correlation-IDX-Credits-Consumed?X-Quota-Limit?X-Quota-Period?X-Quota-Remaining?X-Quota-Reset?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
500 | internal_error | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
503 | unavailablerate_limit_unavailablemetering_unavailable | Retry-After?X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
504 | query_timeout | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
A header marked with a question mark is sent only when it applies.
Query a published dataset with the specification in the request body.
POST/v1/datasets/{dataset_id}/query
For a specification too long for a URL; it answers exactly as the rows endpoint does.
API key operationquery
Parameters
| Name | In | Required | Description |
|---|---|---|---|
dataset_id | path | Yes | The published dataset's ID. |
If-None-Match | header | No | The ETag of a page already held. |
body | application/json | Yes | The Query Specification, at most 64 KiB. |
Responses
| Status | Answer | Headers |
|---|---|---|
200 | A page of rows. | ETagX-Correlation-IDX-Credits-ConsumedX-Quota-Limit?X-Quota-Period?X-Quota-Remaining?X-Quota-Reset?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
400 | query_invalidquery_malformed | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
401 | unauthenticated | WWW-AuthenticateX-Correlation-ID |
403 | operation_not_permitted | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
404 | not_found | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
412 | precondition_failed | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
413 | query_malformed | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
429 | rate_limitedquota_exceeded | Retry-AfterX-Correlation-IDX-Credits-Consumed?X-Quota-Limit?X-Quota-Period?X-Quota-Remaining?X-Quota-Reset?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
500 | internal_error | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
503 | unavailablerate_limit_unavailablemetering_unavailable | Retry-After?X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
504 | query_timeout | X-Correlation-IDX-Credits-Consumed?X-RateLimit-Limit?X-RateLimit-Remaining?X-RateLimit-Reset?X-Request-ID? |
A header marked with a question mark is sent only when it applies.
Error codes
Every error has the same shape: a stable code, a message, the correlation ID, and details where there is something to act on.
| Code | Status | Meaning |
|---|---|---|
query_invalid | 400 | The dataset's descriptor refuses the query; details.rejections lists every reason. |
query_malformed | 400, 413 | The Query Specification cannot be read: not a JSON object, another query parameter, or larger than 64 KiB. |
unauthenticated | 401 | No API key, or one that is unknown, revoked, expired or of a deactivated organisation. |
operation_not_permitted | 403 | The API key is not scoped to this operation. |
not_found | 404 | No such dataset, not published, or outside the key's datasets: one answer for all three. |
method_not_allowed | 405 | The resource does not answer that method. |
precondition_failed | 412 | A POST whose If-None-Match matched the current representation. |
rate_limited | 429 | The key's or the organisation's burst is spent; retry after Retry-After seconds. |
quota_exceeded | 429 | The request would pass the period's credit quota; details say which quota and where to get more. |
internal_error | 500 | Something failed on our side; quote the correlation ID. |
unavailable | 503 | A dependency is out of reach or the dataset is being republished; try again shortly. |
rate_limit_unavailable | 503 | Rate limiting cannot be consulted and this deployment refuses what it cannot limit. |
metering_unavailable | 503 | Usage cannot be recorded, and nothing is served unrecorded; try again shortly. |
query_timeout | 504 | The query ran longer than the service allows; narrow the filter or page with the cursor. |
Response headers
| Header | Meaning |
|---|---|
Deprecation | When this operation was deprecated, as @ and Unix seconds. |
ETag | The page's strong entity tag; send it back in If-None-Match. |
Link | Where the deprecation notice is published. |
Retry-After | Seconds to wait before the next request would be served. |
Sunset | The first day this operation may no longer be served. |
WWW-Authenticate | The authentication scheme the data API expects. |
X-Correlation-ID | The correlation ID of this request; quote it when reporting a problem. |
X-Credits-Consumed | The credits this request consumed, by the published formula. |
X-Quota-Limit | The credits of the binding quota for the period. |
X-Quota-Period | The quota period: hour, day or month. |
X-Quota-Remaining | The credits of that quota left after this request. |
X-Quota-Reset | Seconds until the quota period ends. |
X-RateLimit-Limit | The burst of the rate-limit bucket that binds this request. |
X-RateLimit-Remaining | Requests left in that bucket after this one. |
X-RateLimit-Reset | Seconds until that bucket is full again. |
X-Request-ID | The ID this request's metering event is recorded under. |
Schemas
Page
{
"additionalProperties": false,
"description": "One page of results (FR-API-003, FR-API-025).\n\n`credits_consumed` is on every response, not only on a quota error. Opaque consumption destroys developer trust faster than a restrictive limit does, so the cost of a call is reported at the moment the call is made.",
"properties": {
"credits_consumed": {
"minimum": 0,
"type": "number"
},
"data": {
"items": {
"type": "object"
},
"type": "array"
},
"dataset_version": {
"description": "The version actually read, so a dynamic subset's staleness is visible.",
"type": [
"string",
"null"
]
},
"next_cursor": {
"description": "Absent when this is the last page. Opaque.",
"type": [
"string",
"null"
]
},
"total": {
"description": "Absent unless the caller asked for it: an exact count is a second scan, and most callers do not need one.",
"minimum": 0,
"type": [
"integer",
"null"
]
}
},
"required": [
"data",
"credits_consumed"
],
"title": "Page",
"type": "object"
}Error
{
"additionalProperties": false,
"description": "The one error shape every AkilIQ surface returns.\n\n`code` is stable and machine-readable, never a bare string, so an integrator can branch on it without parsing prose. `correlation_id` is present on every error, so a user reporting a problem quotes something that maps to a trace (NFR-OBS-003, FR-ADM-006).\n\nNo error message carries a secret, a DSN, a stack trace or a database identifier.",
"properties": {
"code": {
"examples": [
"quota_exceeded",
"rate_limited",
"field_not_filterable",
"not_found"
],
"pattern": "^[a-z][a-z0-9_]*$",
"type": "string"
},
"correlation_id": {
"pattern": "^cor_[0-9a-f]{32}$",
"type": "string"
},
"details": {
"description": "Structured specifics — which field, which operator, which limit — so a client can act without parsing the message.",
"type": "object"
},
"documentation_url": {
"format": "uri",
"type": "string"
},
"message": {
"description": "Human-readable, and safe to show a caller.",
"minLength": 1,
"type": "string"
}
},
"required": [
"code",
"message",
"correlation_id"
],
"title": "Error",
"type": "object"
}Rejection
{
"additionalProperties": false,
"properties": {
"code": {
"enum": [
"unknown_field",
"field_not_returnable",
"field_not_filterable",
"field_not_sortable",
"field_not_searchable",
"operator_not_permitted",
"value_required",
"value_not_permitted",
"value_must_be_list",
"value_must_not_be_list",
"limit_exceeded",
"search_not_supported",
"dataset_mismatch",
"value_type_mismatch",
"limit_invalid",
"offset_invalid",
"offset_exceeded",
"pagination_conflict",
"cursor_invalid",
"sort_direction_invalid",
"cost_exceeded"
],
"type": "string"
},
"field": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
}QuerySpecification
{
"additionalProperties": false,
"description": "The one internal representation every read surface compiles to (FR-API-001).\n\nREST today, GraphQL later, internal consumers throughout — all of them produce this. Authorisation, field permissions and limits are applied once, to this specification, never per surface. That is what makes a second surface additive rather than a second place to get permissions wrong.",
"properties": {
"cursor": {
"description": "An opaque continuation token (FR-API-003). Opaque on purpose: a caller that can construct one can page outside the ordering the descriptor guarantees.\n\nWithin the specification it is the unpadded base64url encoding of `{\"after\": [...], \"order\": [[field, direction], ...]}` — the key values of the last row returned, and the order they were read in. A surface that hands cursors to callers seals that payload and opens it before building a specification.",
"type": "string"
},
"dataset": {
"description": "May be left out: the path names the dataset. If given, it must be the path's.",
"pattern": "^ds_[0-9A-HJKMNP-TV-Z]{26}$",
"type": "string"
},
"filters": {
"items": {
"$ref": "#/components/schemas/QuerySpecificationFilter"
},
"type": "array"
},
"limit": {
"minimum": 1,
"type": "integer"
},
"offset": {
"description": "Offset pagination (FR-API-003). Available but capped, because deep offsets scan what they skip.",
"minimum": 0,
"type": "integer"
},
"search": {
"additionalProperties": false,
"properties": {
"fields": {
"items": {
"$ref": "#/components/schemas/QuerySpecificationIdentifier"
},
"type": "array",
"uniqueItems": true
},
"query": {
"maxLength": 512,
"minLength": 1,
"type": "string"
}
},
"required": [
"query"
],
"type": "object"
},
"select": {
"description": "Field projection (FR-API-004). Intersected against the descriptor's returnable set; omitted means every returnable field.",
"items": {
"$ref": "#/components/schemas/QuerySpecificationIdentifier"
},
"type": "array",
"uniqueItems": true
},
"sort": {
"description": "Sorting, on publisher-permitted fields only (FR-API-005).",
"items": {
"$ref": "#/components/schemas/QuerySpecificationSort"
},
"type": "array"
},
"version": {
"default": "latest",
"description": "A dataset version, or `latest`. A dynamic subset resolves this at read time and reports what it resolved against, so staleness is never ambiguous (FR-FLOW-013).",
"type": "string"
}
},
"title": "Query Specification",
"type": "object"
}