Heading API reference
Read-only REST API for Heading. Every endpoint is GET, and only paid properties return data.
Programmatic access to properties, prompts, visibility, AI responses, competitors, sources, and tasks. Authenticate with API keys. Get JSON responses.
Two limits before you start. The API is read-only: every endpoint is GET, and no write endpoints have shipped yet. It covers paid properties only: free properties do not appear in any endpoint, including /v1/properties.
Base URL
https://api.useheading.com/v1OpenAPI specification
The same surface, written for a program:
https://api.useheading.com/openapi.jsonOpenAPI 3.1, served with permissive CORS so a browser-based client can read it. Every endpoint documented on this site has an operation in it, with a unique operationId, typed parameters and a response schema. A test walks the route handlers and fails when an endpoint appears in one place and not the others, so the specification, these pages and the running API cannot drift apart.
Authentication
All requests require an API key passed as a Bearer token in the Authorization header.
Create API keys from Settings > API in the Heading dashboard. Keys use the hdg_sk_live_ prefix.
curl https://api.useheading.com/v1/properties \
-H "Authorization: Bearer hdg_sk_live_abc123..."API keys are shown once at creation and cannot be retrieved later. Store them securely.
Access levels
Both key access levels are accepted, because there is nothing to write yet:
| Level | Permissions |
|---|---|
| Read | GET requests |
| Write | GET requests. Reserved for upcoming write endpoints. Grants nothing extra today. |
Data access
The API returns data for properties on a paid plan (Starter, Growth, or Scale). Free properties do not appear in any endpoint, including /v1/properties.
Property scoping
Keys can be scoped to specific properties or granted access to all properties on the team. Scoped keys only return data for their allowed properties. Filtering by a propertyId outside the key's scope returns an empty list, not an error.
Resources
Pagination
List endpoints accept limit and offset:
limit: integer. Maximum results per page. Default: 100, max: 1000.offset: integer. Rows to skip. Default: 0.
total in the response is the full match count, independent of pagination. There is no cursor and no next link, and there is no batch or bulk endpoint: fetching a lot of data means paging these list endpoints.
Date ranges
Time-series endpoints (/v1/visibility, /v1/responses, /v1/sources, /v1/sources/urls) accept from and to as UTC calendar days (yyyy-MM-dd). Both bounds are inclusive. Omitting both gives the last 30 UTC days, ending today. Omitting from alone starts the range 29 days before to, so it is 30 UTC days counting both ends. All dates in responses are UTC.
Response format
All responses return JSON with a consistent structure.
Success: list
{
"data": [...],
"total": 5
}Success: detail
{
"data": { ... }
}Error
{
"error": {
"code": "MISSING_AUTH",
"message": "Authorization header is required"
}
}Every error uses this envelope, including 429, a request to a path under /v1 that does not exist, and a method other than GET on a path that does exist. The API never answers an HTML error page.
Methods
Every endpoint serves GET, HEAD and OPTIONS, and nothing else. HEAD returns the headers of the GET; OPTIONS answers 204 with Allow: GET, HEAD, OPTIONS. Any other method answers 405 with code METHOD_NOT_ALLOWED and an Allow header, so a mistyped method fails loudly rather than looking like a write that did nothing.
Status codes
| Status | Meaning |
|---|---|
200 | Success |
400 | Invalid request: malformed query parameter |
401 | Unauthorized: invalid or missing API key |
403 | Reserved. FORBIDDEN is for write endpoints, which have not shipped, so no v1 endpoint returns 403 today |
404 | Not found: no such resource in the key's scope, or no endpoint at that path |
405 | Method not allowed: the path exists but serves GET, HEAD and OPTIONS only |
429 | Rate limited: too many requests |
500 | Internal server error |
Error codes
| Code | Status | When |
|---|---|---|
MISSING_AUTH | 401 | No Authorization header |
INVALID_AUTH | 401 | Header is not Bearer {key} |
INVALID_KEY | 401 | Malformed, unknown, or revoked API key |
FORBIDDEN | 403 | Reserved for write endpoints, which have not shipped. No v1 endpoint returns this today |
INVALID_PARAM | 400 | Query parameter failed validation |
NOT_FOUND | 404 | Detail id does not exist in the key's scope, or no endpoint exists at that path |
METHOD_NOT_ALLOWED | 405 | The path exists, but the method is not GET, HEAD or OPTIONS |
RATE_LIMIT_EXCEEDED | 429 | Rate limit exceeded |
INTERNAL_ERROR | 500 | Server error |
Rate limits
- 100 requests/min per API key
X-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Resetheaders are included on every200and every429- No other error carries them. A
401is decided before the key is identified, so there is no budget to report, and a404for an unknown path under/v1is answered without reading the key at all. Read your remaining budget from a success or from the429itself, never from a400,401,404or500 - Rate-limited responses include a
Retry-Afterheader (seconds until reset) and the standard error envelope with codeRATE_LIMIT_EXCEEDED
Versioning
The major version is the first path segment, and it is the whole compatibility promise.
Inside /v1, changes are additive. New endpoints, new fields on an existing response and new optional query parameters ship without notice, so read responses in a way that ignores fields you do not recognise rather than one that fails on them.
A change that could break a working client ships under a new prefix. Removing or renaming a field, changing its type, withdrawing an endpoint and tightening validation are all breaking. /v1 is not altered underneath you.
Deprecation and sunset
A withdrawal is announced before it happens, on the wire and not only in a changelog.
Once an endpoint is deprecated, every 200 it answers carries two headers:
Deprecation: an RFC 9745 structured-field date (@followed by Unix seconds) naming when the deprecation took effect.Sunset: an RFC 8594 HTTP-date naming when the endpoint stops answering.
Deprecation: @1688169599
Sunset: Sun, 30 Jun 2024 23:59:59 GMTThe sunset date is always at least 90 days after the deprecation date.
Neither header is on any endpoint today, because nothing is deprecated. Watch for their presence on a success rather than for their absence: errors do not carry them, since a 404 for an unknown path names no endpoint and a 400 or 401 is decided before the endpoint is reached.