SEO APIWhite label & accountSites API
Sites API for your own dashboard and client onboarding.
Add, update and remove client sites from your own backend, and set the competitors each site follows. The first SEO crawl starts the moment you add a domain, and we keep the measurements running in the background. Every plan includes the API and the MCP at no extra cost, with 600 calls per minute on a white label key and 60 on a personal key.
Add client sites
Create a site with its Google locale and your own tag, and start the first crawl right away.
Shared competitor list
One list of up to six domains feeds the Rank tracker, Core Web Vitals and AI Insights.
SERP rival suggestions
See which domains ranked above the site most often in its latest Rank tracker checks.
Google connection status
Check on every site whether Search Console and Google Analytics 4 are connected.
Manage client sites and their competitors from your code
Every endpoint in the MarketingTracer API takes a site_id. The sites and competitors endpoints let your own backend create that site and manage its settings and competitors.
| Endpoint | MCP tool | Role needed | What it returns |
|---|---|---|---|
sites.list |
list_sites |
guest or higher |
The sites you can open, starred first and then by domain. Supports q search and cursor pagination (limit up to 500, cursor). |
sites.get |
get_site |
guest or higher |
One site's details: locale and tag, your role, active with expires_at, plus search_console_connected and analytics_connected. |
sites.add |
API only | Account / white label | Adds a site and makes you its owner. Sets recommended alerts and starts the first crawl right away, returning the new site object. |
sites.update |
API only | owner or higher |
Updates a site's locale (country, language) or tag; only the fields you send change. Returns the updated site. |
sites.remove |
API only | owner or higher |
Deletes the site and all stored data permanently when confirm_domain matches the site's domain. Returns removed: true. |
competitors.list |
get_competitors_list |
guest or higher |
The site's followed domains (competitors, max: 6) plus suggestions with domain and keywords_above from the latest Rank tracker checks. |
competitors.save |
API only | user or higher |
Replaces the competitor list with up to 6 domains (strips www and paths; ignores duplicates and the site itself). An empty list clears all competitors. |
competitors.add |
API only | user or higher |
Adds one domain at the end of the competitor list, or returns 409 list_full when 6 domains are already followed. |
competitors.remove |
API only | user or higher |
Removes one domain from the competitor list and returns the remaining domains. |
Who sees which sites and how sites.add works
When you call sites.list, a white label owner or admin (superadmin or white_label_admin) gets every site of that white label. Anyone else gets the sites they were added to; on a white label domain, that list only shows sites of that white label. Starred sites (favorite: true) come first, and the rest follow alphabetically by domain.
Calling sites.add only requires domain, either as a host like www.example.co.uk or a homepage URL (we store the lowercase host). You can also pass a two-letter country code (gb is stored as uk) and a language code such as nl or en (or a regional code like pt-BR), which sets the app language for that site when available. An optional tag of up to 60 characters holds your own client ID or label. Any locale field you omit falls back to the white label's default. On a white label domain, the new site gets status: "white_label" and is billed to the white label. Its expiry date renews every day while the white label is paid.
A domain that does not resolve is refused with 400 invalid_domain. If the domain is already a site in MarketingTracer, only white label staff or a paid account can add it again, while trial accounts get 409 duplicate_domain.
Changing settings or deleting a site requires role owner or higher. Calling sites.remove wipes the site and everything stored for it, from scan history and rankings to links and reports. Because that cannot be undone, you must pass the exact domain from sites.get in confirm_domain, or the call stops with 400 confirm_mismatch.
One shared competitor list with SERP suggestions
Every site has a single list of up to 6 competitor domains (max: 6) shared by the Rank tracker, Core Web Vitals and AI Insights. We strip www and keep the domains in the order you add them; chart colours in the app follow that same order. Anyone on the site with role user or higher can replace the whole list with competitors.save, or add and remove individual domains with competitors.add and competitors.remove.
Along with the current list, competitors.list returns suggestions with the domains that ranked above the site most often in its latest Rank tracker measurements. Each suggestion includes keywords_above, with big platforms like Wikipedia and YouTube already left out. The Rank tracker stores the top 20 results for every measurement, so a competitor you add later already has ranking history from earlier checks.
Adding a site and reading its competitor suggestions
Every endpoint takes a POST request with a JSON body at /rest/v1/<endpoint> and an Authorization: Bearer header. The three reading endpoints also accept GET with query parameters and are in the MCP server as read-only tools.
curl -s https://app.marketingtracer.com/rest/v1/sites.add \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"https://www.example.co.uk/","country":"uk","language":"en","tag":"Campaign Q4"}' {
"data": {
"id": "6733f0e1d2c3b4a596877869",
"domain": "www.example.co.uk",
"url": "https://www.example.co.uk",
"country": "uk",
"language": "en",
"tag": "Campaign Q4",
"role": "white_label_admin",
"status": "white_label",
"active": true,
"expires_at": "2026-11-26T15:30:02Z",
"favorite": false,
"search_console_connected": false,
"analytics_connected": false,
"created_at": "2026-11-12T15:30:02Z"
},
"meta": []
} Once the site has Rank tracker measurements, competitors.list returns both the followed competitors and the suggested SERP rivals:
curl -s https://app.marketingtracer.com/rest/v1/competitors.list \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"65a1b2c3d4e5f60718293a4b"}' {
"data": {
"competitors": [
"example-shop.nl",
"voorbeeld.com"
],
"max": 6,
"suggestions": [
{"domain": "example-rival.nl", "keywords_above": 23},
{"domain": "another-example.com", "keywords_above": 14},
{"domain": "sample-store.nl", "keywords_above": 9}
]
},
"meta": {
"site_id": "65a1b2c3d4e5f60718293a4b"
}
}
Adding a new client straight from your onboarding
When a new client signs up in your agency portal or CRM, your backend calls sites.add with their domain and locale, plus your internal client ID in tag. On your white label domain, the site is added to your white label (status: "white_label") and billed to your account. The first SEO crawl starts right away, and you can follow its progress through scan.status. Save the returned id as site_id in your database and pass the client's rivals to competitors.save so every tool starts with the same competitor list.
Picking real SERP rivals with your client
Ask a new client who their competitors are and they usually name the company down the road. Once the Rank tracker has run, competitors.list returns the site's followed domains (up to 6) along with suggestions: the domains that ranked above the client most often in Google. Big platforms like Wikipedia and YouTube are already filtered out. Show that list in your onboarding screen and add the domains your client picks with competitors.add. The Rank tracker keeps the top 20 for every measurement, so a competitor you add weeks later still has ranking history from day one.
Checking Google connections and offboarding a client
Your admin overview or daily sync calls sites.list to pull every site you manage, starred sites first. Every row includes active and expires_at along with search_console_connected and analytics_connected, so your team sees right away which client still needs to connect Search Console or GA4. If a client moves to another market or account manager, sites.update changes country, language or tag. When a contract ends, call sites.remove with the domain in confirm_domain to wipe the site and all its stored history.
Endpoints, arguments and field descriptions
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; 409 not_connected (a connection the endpoint needs, such as Google Analytics 4, is missing); 429 rate_limited; 502 google_error (Google returned an error). The machine-readable version is openapi.json.
sites.list
POST /rest/v1/sites.list · MCP tool
Returns the sites you can open: for an owner or admin of a white label every site of that white label, for anyone else the sites you were added to (on a white label domain only those of that white label). Starred sites first, then by domain.
Arguments
| Argument | Type | What it does |
|---|---|---|
q | string | Only sites whose domain contains this text. |
limit | integer, 1 to 500, default 100 | Sites 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/sites.list \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"limit":3,"site_id":"YOUR_SITE_ID"}'
Example response · A white label admin with 41 sites; domains anonymised.
{
"data": [
{
"id": "65a1b2c3d4e5f60718293a4b",
"domain": "www.example.com",
"url": "https://www.example.com",
"country": "nl",
"language": "nl",
"tag": "Klant A",
"role": "white_label_admin",
"status": "white_label",
"active": true,
"expires_at": "2026-11-26T09:12:40Z",
"favorite": true,
"search_console_connected": true,
"analytics_connected": true,
"created_at": "2024-01-12T14:03:31Z"
},
{
"id": "65f0c1d2e3f4a5b6c7d8e9f0",
"domain": "shop.example.org",
"url": "https://shop.example.org",
"country": "be",
"language": "nl",
"tag": null,
"role": "white_label_admin",
"status": "white_label",
"active": true,
"expires_at": "2026-11-26T09:12:40Z",
"favorite": false,
"search_console_connected": true,
"analytics_connected": false,
"created_at": "2024-03-13T08:41:20Z"
},
{
"id": "6731aa02b4c5d6e7f8091a2b",
"domain": "www.example.net",
"url": "https://www.example.net",
"country": "de",
"language": "de",
"tag": null,
"role": "white_label_admin",
"status": "on_hold",
"active": false,
"expires_at": "2026-09-28T09:12:40Z",
"favorite": false,
"search_console_connected": false,
"analytics_connected": false,
"created_at": "2024-11-11T10:22:58Z"
}
],
"meta": {"next_cursor": "3", "total": 41, "site_id": "YOUR_SITE_ID"}
}
Response fields · data is a list; each item has:
| Field | Type | What it is |
|---|---|---|
id | string | The site id: site_id in every other endpoint. |
domain | string | The domain as we scan it, e.g. www.example.com. |
url | string | https:// plus the domain. |
country | string or null | Two-letter country code Google data is filtered and measured for (Rank tracker, AI Insights); null when not set. |
language | string or null | Language code for Google (nl, en, pt-BR …); null when not set. |
tag | string or null | Your own label for the site; null without one. |
role | string or null | Your role on this site: superadmin, white_label_admin, admin, owner, user or guest. |
status | string | trial, paid, white_label (a site of a white label, billed to the white label), on_hold, test or other. |
active | boolean | Whether the site is active now: not expired and not on hold. Scans and measurements only run for active sites. |
expires_at | string or null | When the site expires unless renewed, ISO 8601 UTC. White label sites are renewed every day while the white label is paid. |
favorite | boolean | Whether you starred the site. |
search_console_connected | boolean | Whether Search Console is connected (with a property). |
analytics_connected | boolean | Whether Google Analytics 4 is connected (with a property). |
created_at | string or null | When the site was added, ISO 8601 UTC. |
sites.get
POST /rest/v1/sites.get · MCP tool get_site
Returns one site: its domain, country and language, your role, whether it is active and until when, and which Google accounts are connected.
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/sites.get \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID"}'
Example response · Domain anonymised.
{
"data": {
"id": "65a1b2c3d4e5f60718293a4b",
"domain": "www.example.com",
"url": "https://www.example.com",
"country": "nl",
"language": "nl",
"tag": "Klant A",
"role": "white_label_admin",
"status": "white_label",
"active": true,
"expires_at": "2026-11-26T09:12:40Z",
"favorite": true,
"search_console_connected": true,
"analytics_connected": true,
"created_at": "2024-01-12T14:03:31Z"
},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
id | string | The site id: site_id in every other endpoint. |
domain | string | The domain as we scan it, e.g. www.example.com. |
url | string | https:// plus the domain. |
country | string or null | Two-letter country code Google data is filtered and measured for (Rank tracker, AI Insights); null when not set. |
language | string or null | Language code for Google (nl, en, pt-BR …); null when not set. |
tag | string or null | Your own label for the site; null without one. |
role | string or null | Your role on this site: superadmin, white_label_admin, admin, owner, user or guest. |
status | string | trial, paid, white_label (a site of a white label, billed to the white label), on_hold, test or other. |
active | boolean | Whether the site is active now: not expired and not on hold. Scans and measurements only run for active sites. |
expires_at | string or null | When the site expires unless renewed, ISO 8601 UTC. White label sites are renewed every day while the white label is paid. |
favorite | boolean | Whether you starred the site. |
search_console_connected | boolean | Whether Search Console is connected (with a property). |
analytics_connected | boolean | Whether Google Analytics 4 is connected (with a property). |
created_at | string or null | When the site was added, ISO 8601 UTC. |
sites.add
POST /rest/v1/sites.add · MCP tool
Returns the new site (as sites.get) after adding it. You become its owner. On a white label domain the site belongs to that white label and is billed to it like every site of the white label. The first crawl starts at once; follow it with scan.status. A domain that already exists can only be added again by white label staff or a paid account (409 duplicate_domain); a domain that does not resolve is refused (400 invalid_domain).
Arguments
| Argument | Type | What it does |
|---|---|---|
domain (required) | string | The domain or the address of the homepage: www.example.com or https://www.example.com/. Only the host counts. |
tag | string | Your own label for the site. |
country | string | Two-letter country code (nl, be, de, uk, us …; gb works too and is stored as uk, like everywhere in this API). Without it: the default of the white label. |
language | string | Language code (nl, en, de, fr, pt-BR …). Without it: the default of the white label. Also sets the language of the app for this site when we have it (nl, en, de, fr; otherwise English). |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/sites.add \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"https://www.example.co.uk/","country":"uk","language":"en","tag":"Campaign Q4","site_id":"YOUR_SITE_ID"}'
Example response · Added by a white label admin on the white label domain; the first crawl has started.
{
"data": {
"id": "6733f0e1d2c3b4a596877869",
"domain": "www.example.co.uk",
"url": "https://www.example.co.uk",
"country": "uk",
"language": "en",
"tag": "Campaign Q4",
"role": "white_label_admin",
"status": "white_label",
"active": true,
"expires_at": "2026-11-26T15:30:02Z",
"favorite": false,
"search_console_connected": false,
"analytics_connected": false,
"created_at": "2026-11-12T15:30:02Z"
},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
id | string | The site id: site_id in every other endpoint. |
domain | string | The domain as we scan it, e.g. www.example.com. |
url | string | https:// plus the domain. |
country | string or null | Two-letter country code Google data is filtered and measured for (Rank tracker, AI Insights); null when not set. |
language | string or null | Language code for Google (nl, en, pt-BR …); null when not set. |
tag | string or null | Your own label for the site; null without one. |
role | string or null | Your role on this site: superadmin, white_label_admin, admin, owner, user or guest. |
status | string | trial, paid, white_label (a site of a white label, billed to the white label), on_hold, test or other. |
active | boolean | Whether the site is active now: not expired and not on hold. Scans and measurements only run for active sites. |
expires_at | string or null | When the site expires unless renewed, ISO 8601 UTC. White label sites are renewed every day while the white label is paid. |
favorite | boolean | Whether you starred the site. |
search_console_connected | boolean | Whether Search Console is connected (with a property). |
analytics_connected | boolean | Whether Google Analytics 4 is connected (with a property). |
created_at | string or null | When the site was added, ISO 8601 UTC. |
sites.update
POST /rest/v1/sites.update · MCP tool
Returns the site (as sites.get) after changing its country, language or tag. Only what you send changes. Owner or higher.
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. |
tag | string | Your own label; an empty string removes it. |
country | string | Two-letter country code (nl, be, de, uk, us …; gb works too and is stored as uk, like everywhere in this API). Without it: the default of the white label. |
language | string | Language code (nl, en, de, fr, pt-BR …). Without it: the default of the white label. Also sets the language of the app for this site when we have it (nl, en, de, fr; otherwise English). |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/sites.update \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID","country":"be","tag":"Klant B"}'
Example response · Only country and tag changed.
{
"data": {
"id": "65a1b2c3d4e5f60718293a4b",
"domain": "www.example.com",
"url": "https://www.example.com",
"country": "be",
"language": "nl",
"tag": "Klant B",
"role": "white_label_admin",
"status": "white_label",
"active": true,
"expires_at": "2026-11-26T09:12:40Z",
"favorite": true,
"search_console_connected": true,
"analytics_connected": true,
"created_at": "2024-01-12T14:03:31Z"
},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
id | string | The site id: site_id in every other endpoint. |
domain | string | The domain as we scan it, e.g. www.example.com. |
url | string | https:// plus the domain. |
country | string or null | Two-letter country code Google data is filtered and measured for (Rank tracker, AI Insights); null when not set. |
language | string or null | Language code for Google (nl, en, pt-BR …); null when not set. |
tag | string or null | Your own label for the site; null without one. |
role | string or null | Your role on this site: superadmin, white_label_admin, admin, owner, user or guest. |
status | string | trial, paid, white_label (a site of a white label, billed to the white label), on_hold, test or other. |
active | boolean | Whether the site is active now: not expired and not on hold. Scans and measurements only run for active sites. |
expires_at | string or null | When the site expires unless renewed, ISO 8601 UTC. White label sites are renewed every day while the white label is paid. |
favorite | boolean | Whether you starred the site. |
search_console_connected | boolean | Whether Search Console is connected (with a property). |
analytics_connected | boolean | Whether Google Analytics 4 is connected (with a property). |
created_at | string or null | When the site was added, ISO 8601 UTC. |
sites.remove
POST /rest/v1/sites.remove · MCP tool
Returns removed: true after removing a site with everything we stored for it (scans, keywords, links, history). This cannot be undone. Owner or higher. Send the domain as confirm_domain, so a wrong site_id never removes another site.
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. |
confirm_domain (required) | string | The domain of the site, exactly as in sites.get. |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/sites.remove \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID","confirm_domain":"www.example.net"}'
Example response · The answer is always this shape.
{
"data": {"id": "6731aa02b4c5d6e7f8091a2b", "domain": "www.example.net", "removed": true},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
id | string | The id of the removed site. |
domain | string | Its domain. |
removed | boolean | Always true. |
Endpoints, arguments and field descriptions
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; 409 not_connected (a connection the endpoint needs, such as Google Analytics 4, is missing); 429 rate_limited; 502 google_error (Google returned an error). The machine-readable version is openapi.json.
competitors.list
POST /rest/v1/competitors.list · MCP tool get_competitors_list
Returns the competitors the site follows: one list for the Rank tracker, Core Web Vitals and AI Insights. With suggestions: the domains that most often rank above the site in its latest Rank tracker measurements (platforms like Wikipedia or YouTube left out).
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/competitors.list \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID"}'
Example response · Domains anonymised; suggestions from the latest Rank tracker measurements of 61 keywords.
{
"data": {
"competitors": ["example-shop.nl", "voorbeeld.com"],
"max": 6,
"suggestions": [
{"domain": "example-rival.nl", "keywords_above": 23},
{"domain": "another-example.com", "keywords_above": 14},
{"domain": "sample-store.nl", "keywords_above": 9}
]
},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
competitors | array<string> | The followed competitors: domains without www, in the order they were added. Their colour in the charts follows this order. |
max | integer | How many a site can follow (6). |
suggestions | array | Domains worth following, most often above you first; empty without Rank tracker measurements. |
suggestions[].domain | string | The domain. |
suggestions[].keywords_above | integer | In how many of the latest measurements it ranked above the site. |
competitors.save
POST /rest/v1/competitors.save · MCP tool
Returns the competitors after replacing the whole list. Cleaned like everywhere: without www, no duplicates, never the site itself, at most 6 (the rest is dropped). An empty list removes all. Positions come from the stored Rank tracker results, so a competitor added later still has history.
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. |
competitors (required) | list of string | The domains to follow, in order. |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/competitors.save \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID","competitors":["https://www.example-shop.nl/","voorbeeld.com","example-rival.nl"]}'
Example response · Cleaned: without www and path.
{
"data": {"competitors": ["example-shop.nl", "voorbeeld.com", "example-rival.nl"], "max": 6},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
competitors | array<string> | The followed competitors: domains without www, in the order they were added. Their colour in the charts follows this order. |
max | integer | How many a site can follow (6). |
competitors.add
POST /rest/v1/competitors.add · MCP tool
Returns the competitors after adding one at the end. Already followed: nothing changes. With 6 already: 409 list_full.
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. |
domain (required) | string | A domain or an address on it (https://www.example.com/page works too); stored without www. |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/competitors.add \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID","domain":"sample-store.nl"}'
Example response · The answer is always this shape.
{
"data": {
"competitors": ["example-shop.nl", "voorbeeld.com", "example-rival.nl", "sample-store.nl"],
"max": 6
},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
competitors | array<string> | The followed competitors: domains without www, in the order they were added. Their colour in the charts follows this order. |
max | integer | How many a site can follow (6). |
competitors.remove
POST /rest/v1/competitors.remove · MCP tool
Returns the competitors after removing one. Not followed: nothing changes.
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. |
domain (required) | string | A domain or an address on it (https://www.example.com/page works too); stored without www. |
Example request
curl -X POST https://app.marketingtracer.com/rest/v1/competitors.remove \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"site_id":"YOUR_SITE_ID","domain":"voorbeeld.com"}'
Example response · The answer is always this shape.
{
"data": {"competitors": ["example-shop.nl", "example-rival.nl", "sample-store.nl"], "max": 6},
"meta": {"site_id": "YOUR_SITE_ID"}
}
Response fields · in data:
| Field | Type | What it is |
|---|---|---|
competitors | array<string> | The followed competitors: domains without www, in the order they were added. Their colour in the charts follows this order. |
max | integer | How many a site can follow (6). |
Run the Sites API on your own domain with one white label key for every client site.
On your agency's white label domain, the REST API and the MCP server answer on your own URL under your name and logo. An owner or admin creates a white label key (mtw_…) on the MCP page in the app. That single key works on your own domains, covers every client site in your white label and gives you 600 calls per minute. Personal keys (mtk_…, 60 calls per minute) follow the exact site access and role of the person who made them. You create and revoke keys on the MCP page in the app, and an API key can never create or read keys.
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.
Does adding a site with sites.add start the SEO crawl immediately?
Yes. As soon as sites.add creates the site, the first crawl starts in the background and the recommended alert settings are set for your account. You can poll scan.status with the returned id as site_id to follow the crawl.
What is the difference between a white label key (mtw_…) and a personal key (mtk_…)?
White label keys (mtw_…) are made by a white label owner or admin and work on the white label's own domains. They give access to every site of that white label at 600 calls per minute. Personal keys (mtk_…) have the same site access and role as the user who created them, with a limit of 60 calls per minute.
Can an AI assistant add or delete sites through the MCP server?
No. The MCP server is read-only. An AI assistant can call list_sites and get_site to inspect sites, or get_competitors_list to read competitors and suggestions. Every write call goes through the REST API with an API key.
Why does sites.add return 409 duplicate_domain?
That domain is already a site in MarketingTracer. Trial accounts cannot add a domain that already exists in the system. White label staff and paid accounts can add an existing domain again under their own account or white label.
Do competitors added later with competitors.add get historical ranking data?
Yes. The Rank tracker stores the top 20 results for every keyword measurement. Any competitor domain you add later is matched against those stored results and shows its ranking history right away.
Which tools share the competitor list from competitors.list?
The Rank tracker, Core Web Vitals and AI Insights all share the same list of up to six competitor domains per site. Any change you make to that list updates the comparison in all three tools at once, and chart colours follow the order of the domains.
What gets deleted when I call sites.remove?
Everything stored for that site is deleted permanently: scanned pages and weekly audit history; Rank tracker keywords and AI visibility data; watched links and the linkbuilding pipeline; topic models and planning tasks; cached reports and the site itself. You need role owner or higher and must pass the site's exact domain in confirm_domain.