APIRank tracker
Rank tracker API. Your Google positions in your own dashboard.
Every keyword you track in MarketingTracer, with its position per month, the page that ranks and where your competitors stand. Your dashboard gets it from four endpoints; your AI assistant gets the same four as MCP tools.
Per keyword
Position now, last time, and per month.
Per page
Which of your pages ranks, and where.
Competitors
Their position on every keyword you track.
In your plan
100 keywords per site, no extra fee.
Four endpoints, four MCP tools
Each endpoint is a POST to /rest/v1/<endpoint> on app.marketingtracer.com. Your AI assistant calls the same data through the MCP tool under each endpoint.
| Endpoint and MCP tool | What you get |
|---|---|
serp.summaryget_serp_summary | Whether measuring is on, how many of your keywords are in use, the months with data and when we last measured. |
serp.keywordsget_serp_keywords | Every tracked keyword: position, previous position, the change, six months of positions, the position per own page and per competitor, and the search volume. |
serp.keywordget_serp_keyword | One keyword up close: the whole top of the latest measurement, and the position per month of every domain in it. |
serp.competitorsget_serp_competitors | Your competitors, plus the domains that show up most often in the tops of your keywords. |
Every endpoint takes site_id. serp.summary and serp.keywords take month (YYYY-MM) for an earlier month. serp.keywords also takes group, limit (1 to 10,000, default 1,000) and cursor. serp.keyword takes the keyword_id from serp.keywords.
A keyword, as the API returns it
curl -X POST https://app.marketingtracer.com/rest/v1/serp.keywords \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id": "YOUR_SITE_ID", "group": "prio", "limit": 1}'
From a real shop, with its domain and id changed and shortened where you see …:
{
"data": [{
"id": "6ae0d00f1a123fbf9d03c6d2",
"keyword": "auto zonnescherm",
"country": "nl",
"device": "desktop",
"group": "prio",
"starred": false,
"search_volume": 5400,
"last_measured_month": "2026-10",
"position": 2,
"previous_position": null,
"change": null,
"positions": [
{"month": "2026-09", "position": null, "measured": false},
{"month": "2026-10", "position": 2, "measured": true}
],
"pages": [{
"url": "https://www.example-shop.com/nl/interieur/auto-zonneschermen.html",
"positions": [{"month": "2026-09", "position": null}, {"month": "2026-10", "position": 2}]
}],
"competitors": [{"domain": "…", "positions": […]}],
…
}],
"meta": {"site_id": "YOUR_SITE_ID", "next_cursor": "6ae0d00f1a123fbf9d03c6d2", "total": 12}
}
This shop added the keyword in October, so previous_position and change are still null; after the next measurement they fill in. A position of null with measured: true means you were not in the measured top that month. A keyword is a keyword plus a country plus a device; the same words on mobile are a separate keyword with their own id.
Positions in the report you already send
On the first of the month your report script asks serp.keywords with last month's month and writes the positions into your own template. The app reads the same endpoint, so nobody has to explain why the PDF says 4 and the dashboard says 5.
A warning when a keyword leaves the top 10
MarketingTracer does not send rank alerts; with the API you build exactly the one you want. Run through the list after each measurement and look at change. A keyword that went from 8 to 14 shows up in your own Slack channel or ticket board the next morning, with the page that lost it.
Every endpoint, argument and field
Generated from the same definitions the API itself runs on, and the example answers are tested against them, so this reference is never behind. Every endpoint is a POST with a JSON body; reading endpoints also take a GET with the same arguments in the query string. Send your API key as Authorization: Bearer YOUR_API_KEY. Every answer is {"data": …, "meta": {…}}: meta.site_id always, meta.next_cursor and meta.total for a list, meta.period where the endpoint covers a period. An error is {"error": {"code", "message"}} with the HTTP status: 400 invalid_argument, unknown_argument or missing_argument; 401 not_authenticated; 403 no_access or tool_off; 404 not_found; 429 rate_limited. The machine-readable version is openapi.json.
serp.summary
POST /rest/v1/serp.summary · MCP tool get_serp_summary
Returns the Rank tracker status of one site: whether measuring is on, whether the site is in its trial, how deep each measurement goes, the months with data, how many keywords are in use of the site's quota, and when the latest and the next measurement are. Positions are real Google results per keyword, country and device, separate from Search Console.
Arguments
| Argument | Type | What it does |
|---|---|---|
site_id (required) | string | The site: its id from list_sites, or the id in the app’s address. |
month | string, YYYY-MM | Last month to show, YYYY-MM, for a report of an earlier month. Defaults to this month. |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/serp.summary \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID"}'
Example response · A real answer, anonymised and shortened to two items per list.
{
"data": {
"measuring_enabled": true,
"trial": false,
"own_domain": "example-shop.com",
"depth": 20,
"months": ["2026-09", "2026-10"],
"report_month": null,
"quota": {"used": 10, "total": 100},
"last_measured_at": "2026-10-29T23:20:05Z",
"next_measurement_at": "2026-11-28T23:15:00Z",
"can_set_plan": true,
"can_buy": false
},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
measuring_enabled | boolean | Whether the Rank tracker is measuring at all right now (one switch for all sites). |
trial | boolean | Whether the site is in its free trial. The Rank tracker does not measure during the trial. |
own_domain | string | The domain of the site, without www. |
depth | integer | How deep each measurement goes: 20 means the top 20. A position below that is null. |
months | array<string> | The months serp.keywords returns, YYYY-MM, oldest first: the six months before this month (or before month), plus that month itself once it has a measurement. |
report_month | string or null | The month asked for with month; null when month was not given. |
quota | object | Keywords this calendar month. |
quota.used | integer | Keywords (keyword, country and device) measured or queued this calendar month, including ones deleted since; deleting a keyword does not give its slot back. |
quota.total | integer | Keywords the site's plan allows. |
last_measured_at | string or null | When the site's newest measurement of any keyword came in, ISO 8601 UTC; null before the first one. Not limited by month. |
next_measurement_at | string or null | When the next scheduled measurement starts, ISO 8601 UTC; null for a report of an earlier month. |
can_set_plan | boolean | Whether the caller may set a measuring plan for the site (white label admins). |
can_buy | boolean | Whether the caller may buy a bigger plan for the site. |
serp.keywords
POST /rest/v1/serp.keywords · MCP tool get_serp_keywords
Returns the tracked keywords of one site, oldest first, one item per keyword, country and device: the site's best position in the latest measured month and in the measured month before, the change, the position per month, the position per month of each own page that ranked and of each followed competitor, plus the monthly search volume. A position is null when the site was not in the measured top, or when nothing was measured that month (measured false).
Arguments
| Argument | Type | What it does |
|---|---|---|
site_id (required) | string | The site: its id from list_sites, or the id in the app’s address. |
month | string, YYYY-MM | Last month of the series, YYYY-MM, for a report of an earlier month. Defaults to this month. |
group | string | Only the keywords in this group. |
limit | integer, 1 to 10,000, default 1,000 | Keywords per call. |
cursor | string | meta.next_cursor of the previous call, for the next page. |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/serp.keywords \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID","limit":2}'
Example response · A real answer, anonymised and shortened to two items per list.
{
"data": [
{
"id": "6ae0d00f1a123fbf9d03c6d2",
"keyword": "auto zonnescherm",
"country": "nl",
"device": "desktop",
"group": "prio",
"starred": false,
"target_url": null,
"search_volume": 5400,
"state": "ok",
"paused": false,
"last_measured_month": "2026-10",
"position": 2,
"previous_position": null,
"change": null,
"positions": [
{"month": "2026-09", "position": null, "measured": false},
{"month": "2026-10", "position": 2, "measured": true}
],
"pages": [
{
"url": "https://www.example-shop.com/sunshades",
"positions": [{"month": "2026-09", "position": null}, {"month": "2026-10", "position": 2}]
}
],
"competitors": []
},
{
"id": "6ae0d00f1a123fbf9d03c6d4",
"keyword": "automatten",
"country": "nl",
"device": "desktop",
"group": "overig",
"starred": false,
"target_url": null,
"search_volume": 4400,
"state": "ok",
"paused": false,
"last_measured_month": "2026-10",
"position": null,
"previous_position": null,
"change": null,
"positions": [
{"month": "2026-09", "position": null, "measured": false},
{"month": "2026-10", "position": null, "measured": true}
],
"pages": [],
"competitors": []
}
],
"meta": {"site_id": "YOUR_SITE_ID", "next_cursor": "2", "total": 10}
}
Response fields · data is a list; each item has:
| Field | Type | What it is |
|---|---|---|
id | string | The keyword id; keyword_id in serp.keyword. |
keyword | string | The keyword as tracked. |
country | string | Two-letter country code of the Google that is measured, e.g. nl. |
device | string | desktop or mobile. |
group | string or null | The group the keyword is in; null without a group. |
starred | boolean | Whether the keyword has a star. |
target_url | string or null | The page that should rank for this keyword; null when not set. |
search_volume | integer or null | Average searches per month in this country; null when not known yet. |
state | string | new (waiting for its first measurement), queued, ok or error. |
paused | boolean | Whether the keyword falls outside the plan's keyword count and is not measured (starred keywords count first, then the oldest). |
last_measured_month | string or null | The latest month in the series with a measurement, YYYY-MM; null when no month has one. |
position | integer or null | The site's best position at the last measurement of last_measured_month; null when not in the measured top or without a measurement. |
previous_position | integer or null | The site's best position in the measured month before last_measured_month (its last measurement that month); null when not in the top or without such a month. |
change | integer or null | Places gained between the last two measured months (positive is up); a month outside the measured top counts as depth + 1. Null with fewer than two measured months. |
positions | array | The best position of the site per month, oldest first. |
positions[].month | string | YYYY-MM. |
positions[].position | integer or null | The best position that month; null when not in the top or not measured. |
positions[].measured | boolean | Whether the keyword was measured that month. |
pages | array | Each own page that ranked for the keyword, with its position per month. |
pages[].url | string | The page. |
pages[].positions | array | Per month, oldest first. |
pages[].positions[].month | string | YYYY-MM. |
pages[].positions[].position | integer or null | The position of this page; null when not in the measured top or not measured that month. |
competitors | array | Each followed competitor (also when it never ranked), with its best position per month. |
competitors[].domain | string | The competitor. |
competitors[].positions | array | Per month, oldest first. |
competitors[].positions[].month | string | YYYY-MM. |
competitors[].positions[].position | integer or null | The best position of this domain; null when not in the measured top or not measured that month. |
serp.keyword
POST /rest/v1/serp.keyword · MCP tool get_serp_keyword
Returns one tracked keyword up close: the organic top of its latest measurement (every result with its position, domain, URL and whether it is the site's own page), and the position per month of every other domain in that top, of each followed competitor and of each own page.
Arguments
| Argument | Type | What it does |
|---|---|---|
site_id (required) | string | The site: its id from list_sites, or the id in the app’s address. |
keyword_id (required) | string | The id from serp.keywords. |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/serp.keyword \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID","keyword_id":"6ae0d00f1a123fbf9d03c6d2"}'
Example response · A real answer, anonymised and shortened to two items per list.
{
"data": {
"id": "6ae0d00f1a123fbf9d03c6d2",
"keyword": "auto zonnescherm",
"country": "nl",
"device": "desktop",
"measured_at": "2026-10-29T23:06:31Z",
"months": [{"month": "2026-09", "measured": false}, {"month": "2026-10", "measured": true}],
"top": [
{
"position": 1,
"domain": "halfords.nl",
"url": "https://www.halfords.nl/auto/interieur-accessoires/zonnescherm-auto",
"own": false
},
{
"position": 2,
"domain": "example-shop.com",
"url": "https://www.example-shop.com/sunshades",
"own": true
}
],
"pages": [
{
"url": "https://www.example-shop.com/sunshades",
"positions": [{"month": "2026-09", "position": null}, {"month": "2026-10", "position": 2}]
}
],
"domains": [
{
"domain": "halfords.nl",
"positions": [{"month": "2026-09", "position": null}, {"month": "2026-10", "position": 1}]
}
]
},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
id | string | The keyword id. |
keyword | string | The keyword. |
country | string | Two-letter country code. |
device | string | desktop or mobile. |
measured_at | string or null | When the latest measurement came in, ISO 8601 UTC; null before the first one (top is then empty). |
months | array | The six months before this month, plus this month once it has a measurement, oldest first. |
months[].month | string | YYYY-MM. |
months[].measured | boolean | Whether the keyword was measured that month. |
top | array | The organic results of the latest measurement, best first. |
top[].position | integer | The organic position in Google. |
top[].domain | string | The domain, without www. |
top[].url | string | The page. |
top[].own | boolean | Whether it is a page of the site itself. |
pages | array | Each own page that ranked, with its position per month. |
pages[].url | string | The page. |
pages[].positions | array | Per month, oldest first. |
pages[].positions[].month | string | YYYY-MM. |
pages[].positions[].position | integer or null | The position of this page; null when not in the measured top or not measured that month. |
domains | array | Each other domain in the latest top, plus each followed competitor, with its best position per month. |
domains[].domain | string | The domain. |
domains[].positions | array | Per month, oldest first. |
domains[].positions[].month | string | YYYY-MM. |
domains[].positions[].position | integer or null | The best position of this domain; null when not in the measured top or not measured that month. |
serp.competitors
POST /rest/v1/serp.competitors · MCP tool get_serp_competitors
Returns the followed competitors of one site (one list shared by the Rank tracker, Core Web Vitals and AI Insights), and suggestions: the domains that show up most often in the latest tops of the site's tracked keywords.
Arguments
| Argument | Type | What it does |
|---|---|---|
site_id (required) | string | The site: its id from list_sites, or the id in the app’s address. |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/serp.competitors \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID"}'
Example response · A real answer, anonymised and shortened to two items per list.
{
"data": {
"competitors": [],
"suggestions": [{"domain": "bol.com", "keywords": 5}, {"domain": "anwb.nl", "keywords": 5}]
},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
competitors | array<string> | The followed competitors, at most 6 domains. |
suggestions | array | At most 6 domains, other than the site and its followed competitors, that are most often in the latest tops of the tracked keywords, most first. |
suggestions[].domain | string | The domain. |
suggestions[].keywords | integer | For how many tracked keywords it is in the latest top (once per keyword). |
Rankings for every client. From your own domain.
One key reads the Rank tracker of every client site you can open, so one script fills the reports of all your clients. On a white-label dashboard the API answers on your own domain. The whole dashboard is white label: your domain, logo and colours, and team roles that set which modules each role sees.
Free 14-day trial
MarketingTracer smart A.I. tool shows you what you need to rank. Then we'll help you get it done.
- No credit card required
- No installation needed
- No strings attached
FAQ
Something else? Ask us.
What is a rank tracker API?
A rank tracker API gives the Google positions of your keywords to your own software. This one gives the keywords you track in MarketingTracer: your position, your pages and your competitors, per month.
Can I look up any keyword on demand?
No. The API returns the keywords you track, with their history. Add a keyword in the app and it gets its first measurement shortly after.
Are these positions from Search Console?
No. They are real Google results, measured per keyword, country and device, and separate from Search Console. The average positions from Search Console are in the Search Console MCP.
Which countries and devices?
34 countries, desktop or mobile, chosen per keyword when you add it. The country and the device stay fixed after that, so every month compares with the one before.
How fresh are the numbers?
As fresh as the Rank tracker in the app: the API always returns the latest measurement. serp.summary tells you when that was.
Does it work during the free trial?
The API works, but the Rank tracker starts measuring when you subscribe, so serp.keywords stays empty during the trial.
Can I get Google's AI Overview for my keywords?
Yes, through aiinsights.ai_overviews in the AI visibility API.
What does it cost?
Nothing extra: the API is part of every plan, with 100 keywords per site and more on request. Prices are on the pricing page.