Core concepts
This guide is the conceptual backbone for the rest of the documentation. It explains how Ranktracker's resources relate to each other, how they map onto URLs, and the shape of every request and response. Once these ideas are clear, the endpoint-specific guides and the API reference will read as variations on the same handful of patterns.
Every example authenticates with an API key sent verbatim in the
Authorization header — no Bearer prefix. Create and manage keys in the
Ranktracker app. See the
Authentication guide for the details.
The resource model
Your data forms a simple hierarchy rooted at your account:
- An account owns domains and account-wide tags, and exposes usage (your plan's quota).
- A domain is a site you track. Each domain has competitors and keywords, plus its own ranking history and share of voice.
- A keyword is a word tracked on a domain for a specific search engine, location, language, and device. Each keyword carries SERP data (its rankings and the raw search-results page) and can be labelled with tags.
- A tag is an account-level label. You attach tags to keywords; the same tag can be reused across many keywords and domains.
Read it top-down: your account has domains, a domain has competitors and
keywords, a keyword has SERP data and tags, and account/usage tells you how
much of your plan you have consumed.
account
├── usage (plan quota: keywords, data rows)
├── tags (account-wide labels)
└── domains
├── history (ranking & visibility over time)
├── share-of-voice
├── competitors
└── keywords
├── serp (latest search results)
├── serp-html (cached SERP page)
└── tags (taggings for this keyword)
:::note Everything is scoped to your account
The API only ever returns data your API key's account owns. A domain, keyword,
or tag that belongs to another account is invisible to you — you get a 404, as
though it did not exist, rather than a 403. See
Errors & rate limits.
:::
IDs are UUIDs
Every resource is identified by a UUID string — for example
3f2a9c7e-1b6d-4e2a-9f10-8c5b0a1d2e3f. These UUIDs appear as the id on each
resource and are the values you drop into URL paths. There are no
auto-incrementing integer IDs in the public API; always treat an ID as an opaque
string and never try to parse or predict one.
You collect a resource's UUID from the response when you create or list it, then use it to address that resource directly:
curl https://api.ranktracker.com/v1/domains/3f2a9c7e-1b6d-4e2a-9f10-8c5b0a1d2e3f \
-H "Authorization: tkn_usr_your_api_key_here"
The JSON:API envelope
Every response body is wrapped in a JSON:API-style envelope. The rules are consistent across the whole API:
- The payload lives under a top-level
datakey. - A single resource is an object; a collection is an array.
- Each resource object has an
id(its UUID), atype(the resource kind), and anattributesobject holding its fields. - Attribute keys are
camelCase(for exampleprojectName,matchType,createdAt).
A single resource
GET /v1/domains/{uuid} returns one object under data:
{
"data": {
"id": "3f2a9c7e-1b6d-4e2a-9f10-8c5b0a1d2e3f",
"type": "domain",
"attributes": {
"domain": "https://www.example.com",
"scheme": "https",
"host": "www.example.com",
"matchType": "any",
"projectName": "Example Site",
"colour": "#4f46e5",
"createdAt": "2026-01-14T09:12:44Z",
"updatedAt": "2026-06-30T18:03:11Z"
}
}
}
A list of resources
List endpoints return an array under data. GET /v1/domains:
{
"data": [
{
"id": "3f2a9c7e-1b6d-4e2a-9f10-8c5b0a1d2e3f",
"type": "domain",
"attributes": {
"host": "www.example.com",
"projectName": "Example Site",
"matchType": "any"
}
},
{
"id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"type": "domain",
"attributes": {
"host": "shop.example.com",
"projectName": "Example Shop",
"matchType": "domain"
}
}
]
}
:::tip Request bodies are not camelCase
Responses use camelCase, but request bodies for writes use snake_case and
are nested under a resource key. Creating a domain expects
{ "domain": { "host": "...", "match_type": "..." } }, not matchType. Each
write guide shows the exact body it wants — follow those rather than mirroring
the response shape.
:::
Error envelope
Errors follow the same envelope idea, but under an errors array instead of
data. Each entry carries a code, status, and human-readable detail:
{
"errors": [
{
"code": "unprocessable_entity",
"status": 422,
"detail": "Host can't be blank"
}
]
}
The Errors & rate limits guide covers every status you may see and how to handle it.
HTTP verbs and status codes
The API uses standard HTTP verbs, and the verb tells you the intent:
| Verb | Meaning | Typical success |
|---|---|---|
GET | Read a resource or list | 200 OK |
POST | Create a resource | 201 Created |
PATCH | Update part of a resource | 200 OK |
DELETE | Remove (usually soft-delete) a resource | 204 No Content |
A DELETE returns 204 with an empty body. Deletes are generally
idempotent — deleting something already gone still returns 204 rather than
an error, so retries are safe.
The status codes you will encounter across the API:
| Status | When it happens |
|---|---|
200 OK | Successful read or update |
201 Created | A POST created the resource |
204 No Content | A DELETE succeeded (empty body) |
400 Bad Request | Required parameters are missing entirely |
402 Payment Required | The write would exceed your plan's keyword or data-row quota |
403 Forbidden | Your plan does not have API access enabled, or the action is forbidden |
404 Not Found | The resource does not exist, or is not in your account |
422 Unprocessable Entity | Validation failed (e.g. a bad or missing attribute) |
503 Service Unavailable | The request was throttled — retry with backoff |
:::note Throttling returns 503
When you are rate-limited the API currently responds with 503, and there are
no RateLimit-* headers yet. Treat a 503 as transient and retry with
exponential backoff. Full detail is in
Errors & rate limits.
:::
Nested resources map to nested paths
The URL structure mirrors the resource hierarchy. A resource that only exists in the context of a parent lives under that parent's path, addressed by the parent's UUID.
Top-level resources sit directly under /v1:
GET /v1/domains List your domains
POST /v1/domains Create a domain
GET /v1/domains/{uuid} Retrieve one domain
PATCH /v1/domains/{uuid} Update a domain
DELETE /v1/domains/{uuid} Delete a domain
GET /v1/tags List account-wide tags
POST /v1/tags Create a tag
GET /v1/account/usage Read plan usage and quota
Competitors and keywords belong to a domain, so they nest under that domain's UUID:
GET /v1/domains/{domain_uuid}/competitors
POST /v1/domains/{domain_uuid}/competitors
GET /v1/domains/{domain_uuid}/competitors/{uuid}
GET /v1/domains/{domain_uuid}/keywords
POST /v1/domains/{domain_uuid}/keywords
GET /v1/domains/{domain_uuid}/keywords/{uuid}
SERP data and per-keyword taggings belong to a keyword, so they nest one level deeper still:
GET /v1/domains/{domain_uuid}/keywords/{keyword_uuid}/serp
GET /v1/domains/{domain_uuid}/keywords/{keyword_uuid}/serp-html
POST /v1/domains/{domain_uuid}/keywords/{keyword_uuid}/tags/{tag_uuid}
DELETE /v1/domains/{domain_uuid}/keywords/{keyword_uuid}/tags/{tag_uuid}
To address a nested resource you supply every UUID on the path. Tagging one keyword needs the domain, the keyword, and the tag:
curl -X POST \
https://api.ranktracker.com/v1/domains/DOMAIN_UUID/keywords/KEYWORD_UUID/tags/TAG_UUID \
-H "Authorization: tkn_usr_your_api_key_here"
Tags themselves are account-wide (/v1/tags), but taggings — the link between
a tag and a keyword — are per-keyword and live under the keyword's path. You can
attach a tag to many keywords at once with the bulk endpoint,
POST /v1/domains/{domain_uuid}/keywords/taggings. There is currently no
bulk untag endpoint; to untag in bulk, remove the tagging from each keyword
individually. See Bulk operations and
Keyword tags.
A quick tour of each resource
The reference documents every field; this is the mental model.
Domains
Domains support full CRUD. When you create one, its host, scheme, and
matchType define the identity of the site and are immutable afterwards
— to change them you delete the domain and create a new one. Fields like
projectName can be updated with PATCH.
Two extra read endpoints expose time-series data for a domain,
GET /v1/domains/{uuid}/history and GET /v1/domains/{uuid}/share-of-voice.
Both accept start_date and end_date as YYYY-MM-DD strings and a
device filter of all, desktop, or mobile. The maximum range is
365 days. See Domains.
curl "https://api.ranktracker.com/v1/domains/DOMAIN_UUID/history?start_date=2026-06-01&end_date=2026-06-30&device=all" \
-H "Authorization: tkn_usr_your_api_key_here"
Competitors
Competitors are the rival sites tracked against one of your domains. They support full CRUD and are always nested under a domain. See Competitors.
Keywords
Keywords are nested under a domain. Creating them is the one place the API does
real fan-out: a POST tracks the cartesian product of the words array and
the search_engines array, so ten words across three search-engine
configurations create thirty tracked keywords in one call. PATCH toggles the
tracked flag to pause or resume a keyword, and DELETE stops tracking it (a
soft-delete of the tracking link). If a create would push you over your plan's
keyword or data-row quota, the whole request is rejected with 402 and nothing
is tracked. See Keywords.
SERP
Each keyword exposes its search-results data. GET .../serp returns the latest
parsed SERP (organic results, features, and rankings), and GET .../serp-html
returns the cached raw HTML of the results page. See SERP.
Tags and taggings
A tag (/v1/tags) is an account-wide label with full CRUD. A tagging is
the association between a tag and a keyword, managed under the keyword's path
(individually) or via the bulk-tag endpoint. See Tags and
Keyword tags.
Account usage
GET /v1/account/usage reports your plan's keyword and data-row usage and
limits — the same quota that produces a 402 on keyword creates. Poll it
before large writes to stay within plan. See Account and the
Usage & quota guide.
curl https://api.ranktracker.com/v1/account/usage \
-H "Authorization: tkn_usr_your_api_key_here"
Where to go next
- Authentication — how API keys work.
- Pagination —
page/per_pageand the count headers on list endpoints. - Errors & rate limits — every status code and
how to handle
503throttling. - Usage & quota — staying under your plan's limits.
- Bulk operations — creating many keywords and bulk-tagging.