Menu

API reference · Rankings

Projects

seo_rankings · projects · 5 operations

GET/v1/seo/rankings/projects

List rank tracking projects

seo_rankings.projects.list

ReadIdempotentSince 2026-11-01

Lists the organization's rank tracking projects, oldest first, paginated by cursor (limit up to 200). An account normally has one project (the domain it tracks); each row brings the domain, the engines and countries measured, devices, the check schedule, active/inactive keyword counts, the status and date of the last check, the next scheduled check, the manual checks used and left this month, the plan's ranking quota, and the position summary of the last check (check_summary: the position bands top3 = 1–3, top10 = 4–10, top20 = 11–20, top30 = 21–30 and not_ranked, the average position of the keywords in the top 30, and the top gainers and losers on the main engine, country and device: among the ones measured today, the one with the most active keywords with a position, ties going to the project's order). Each project also brings measured_pos_keys (the engine, country and device combinations it measures today, as {engine}_{country}_{device}), combo_summaries (one pair per combination measured today that already has measurements: current_summary and previous_summary, the check before of that SAME combination or null; primary marks the one in check_summary) and retired_combos (the combinations it no longer measures: their history stays readable with seo_rankings.history.grid, never as today's positions). Use seo_rankings.keywords.list for the keywords and their positions.

Scope
seo_rankings:read
Permission
View the module
MCP tool
seo_rankings_projects_listOn the full surface; on the compact one, mb_execute_operation with id: "seo_rankings.projects.list".

Declared cost: up to 202 reads and 0 writes per call (they count against the daily quota).

Parameters

limitintegerqueryoptional

How many items this page brings at most (see the maximum and the default in the schema).

Default: 50

cursorstringqueryoptional

The next_cursor of the previous page, exactly as it came (only for the same list and filters); without it, the first page.

Response

dataarray<object>
has_moreboolean
next_cursorstring

Errors

Example

curl "https://api.mb-suite.com/v1/seo/rankings/projects" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl "https://api.mb-suite.com/v1/seo/rankings/projects" \
  -H "Authorization: Bearer $MB_TEST_KEY"
GET/v1/seo/rankings/projects/{project_id}

Get a rank tracking project

seo_rankings.projects.get

ReadIdempotentSince 2026-11-01

Returns one rank tracking project: domain, engines and countries (with the engine keys used elsewhere, {engine}_{country}), devices, schedule, keyword counts, last and next check, manual checks used and remaining this month (UTC), the ranking quota of the plan and the position summary of the last check on the main engine, country and device (check_summary: among the combinations measured today, the one with the most active keywords with a position, ties going to the project's order; previous_summary is the check before of the SAME combination, or null when there is none, never another country's or device's), including the keywords that rose or dropped the most since the previous check (gainers and losers, up to 5 each: the quickest answer to «which keywords went down»). combo_summaries brings the same pair for every combination measured today that already has measurements (primary marks the main one; one in measured_pos_keys without an entry has none yet: they arrive with the next check), measured_pos_keys lists the combinations measured today and retired_combos the ones the project no longer measures (with their first and last measurement; their history stays in seo_rankings.history.grid). Every summary brings last_checked_at, its newest measurement; compare positions only within the same engine, country and device. Its distribution is in BANDS, not cumulative: top3 = positions 1–3, top10 = 4–10, top20 = 11–20, top30 = 21–30, not_ranked = no position in the top 30 (so «in the first page» is top3 + top10); average_position averages only the keywords in the top 30. last_check_status is dispatching, pending, collecting, completed or failed while a check runs or after it ends. Not found if the id is not a project of this organization.

Scope
seo_rankings:read
Permission
View the module
MCP tool
seo_rankings_projects_getOn the full surface; on the compact one, mb_execute_operation with id: "seo_rankings.projects.get".

Declared cost: up to 1 reads and 0 writes per call (they count against the daily quota).

Parameters

project_idstringpathrequired

Rank project id (from seo_rankings.projects.list).

Response

idstring
namestring
domainstring

Host measured (example.com; subdomains count).

enginesarray<object>

Engine and country combinations measured.

devicesarray<string>

Devices measured: desktop and/or mobile.

scheduleobject

When the scheduled check runs.

keyword_countnumber

Active keywords (they count against the quota).

inactive_keyword_countnumber

Paused keywords (history kept).

last_check_atnumber | null
last_check_statusstring | null

Status of the latest check batch: dispatching, pending, collecting, completed or failed.

next_check_atnumber | null

Next scheduled check (ms); null when the schedule is off.

manual_checks_usednumber

Manual checks used this month (UTC).

manual_checks_remainingnumber
quotaobject

The plan's ranking quota.

check_summaryobject | null

Summary of the main engine, country and device (its pos_key) after the latest check; null when nothing the project measures today has been measured yet.

previous_summaryobject | null

The check before, of the SAME engine, country and device as check_summary (same pos_key); null when there is none, never another combination's.

measured_pos_keysarray<string>

The engine, country and device combinations the project measures today, {engine}_{country}_{device}, in the project's order (engines, then devices): only these are today's positions.

combo_summariesarray<object>

One pair of summaries per combination measured today that already has measurements, in the project's order: its latest check (current_summary) and the check before of that SAME combination (previous_summary); primary marks the one in check_summary. Positions are compared only within one combination.

retired_combosarray<object>

Combinations the project measured and no longer measures, the most recently measured first. Their history stays readable with seo_rankings.history.grid (with that engine, country and device): it is never a position of today.

created_atnumber
updated_atnumber
created_by_idstring | null

Errors

Example

curl "https://api.mb-suite.com/v1/seo/rankings/projects/{project_id}" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl "https://api.mb-suite.com/v1/seo/rankings/projects/{project_id}" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/seo/rankings/projects

Create the rank tracking project

seo_rankings.projects.create

WriteSince 2026-11-01

Creates the rank tracking project of the organization: the domain to track (a host like example.com; protocol, path and port are dropped, positions match the host and its subdomains), the engine and country combinations to measure (google or bing + ISO country; supported countries fill in location and language, others need location_name and language_code), the devices (default desktop) and the check schedule (default weekly, Monday 09:00 UTC, enabled, as in the app: once the project has keywords a check runs by itself every week, DataForSEO included in the plan; send schedule.enabled false to create it paused, and next_check_at in the answer says when the first one runs). An account has ONE project (409 rank_project_exists if it already has one: edit it instead). The engines and devices are capped by the plan's ranking quota (409 limit_reached, details.resource rank_engines / rank_devices). Keywords are added afterwards with seo_rankings.keywords.add. Nothing is measured until a scheduled or manual check runs.

Scope
seo_rankings:write
Permission
Edit in the module
MCP tool
seo_rankings_projects_createOn the full surface; on the compact one, mb_execute_operation with id: "seo_rankings.projects.create".

Declared cost: up to 2 reads and 1 writes per call (they count against the daily quota).

Parameters

domainstringbodyrequired

Domain to track (example.com). A protocol, path or port is dropped: positions are matched by host, subdomains included.

enginesarray<object>bodyrequired

Engine and country combinations to measure (each one is paid per keyword and device on every check). Limited by the plan's ranking quota (quota.max_engines of the project).

namestringbodyoptional

Display name. Default: the domain.

devicesarray<enum>bodyoptional

Devices to measure. Default: [desktop]. Limited by the plan's ranking quota (quota.max_devices of the project).

Values: desktop, mobile

scheduleobjectbodyoptional

Scheduled check. Default: weekly, Monday 09:00 UTC, enabled.

Response

project_idstring
projectobject

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/seo/rankings/projects" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "domain": "DOMAIN",
  "engines": []
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X POST "https://api.mb-suite.com/v1/seo/rankings/projects" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "domain": "DOMAIN",
  "engines": []
}'
PATCH/v1/seo/rankings/projects/{project_id}

Update a rank tracking project

seo_rankings.projects.update

WriteIdempotentSince 2026-11-01

Updates the domain, name, engines and countries, devices or schedule of a project; only the fields sent change. Send the whole list: engines and devices replace it; use dry_run to see what a change stops measuring (its items are those combinations, by pos_key, and counts.positions_cleared the latest positions it would clear; it writes nothing, and running the change does not need its confirm_token). Adding a country to a project with 2+ engines keeps the existing keywords measuring where they were (keywords_stamped says how many; opt them in with seo_rankings.keywords.bulk.set_engines); removing a country that some ACTIVE keyword is measured on only there is refused (409 rank_engine_in_use: assign them another country or deactivate them first). What the project already has is kept even above the plan's quota; only growing past it is refused. A new schedule recomputes the next scheduled check. Removing or changing a country or a device clears the latest positions measured there (the history stays: it is listed in retired_combos and readable with seo_rankings.history.grid) and recomputes the summaries; a combination that comes back starts without a previous position. Returns the updated project, keywords_stamped, positions_cleared (the latest positions removed) and retired_pos_keys (the combinations it stopped measuring that had positions: the ones noted in retired_combos).

Scope
seo_rankings:write
Permission
Edit in the module
MCP tool
seo_rankings_projects_updateOn the full surface; on the compact one, mb_execute_operation with id: "seo_rankings.projects.update".

Declared cost: up to 302 reads and 302 writes per call (they count against the daily quota).

Parameters

project_idstringpathrequired

Rank project id (from seo_rankings.projects.list).

domainstringbodyoptional

New domain to track (example.com); a protocol, path or port is dropped.

namestringbodyoptional

New display name.

enginesarray<object>bodyoptional

The full list of engine and country combinations (replaces the current one).

devicesarray<enum>bodyoptional

The full list of devices (replaces the current one).

Values: desktop, mobile

scheduleobjectbodyoptional

When the scheduled check runs. Replaces the whole schedule when sent.

Response

projectobject
keywords_stampednumber

Keywords without assigned countries that keep measuring on the previous ones (when a country is added).

positions_clearednumber

Latest positions removed from the keywords: those of a country or device the project stopped measuring (their history stays).

retired_pos_keysarray<string>

The combinations it stopped measuring that had positions (the ones noted in retired_combos).

Errors

Example

curl -X PATCH "https://api.mb-suite.com/v1/seo/rankings/projects/{project_id}" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X PATCH "https://api.mb-suite.com/v1/seo/rankings/projects/{project_id}" \
  -H "Authorization: Bearer $MB_TEST_KEY"
DELETE/v1/seo/rankings/projects/{project_id}

Delete a rank tracking project

seo_rankings.projects.delete

IrreversibleSince 2026-11-01

Deletes a rank tracking project with ALL its keywords, their whole position history and its check batches. The trash keeps a read-only copy of the project and its keywords for 30 days; it cannot be restored from there (the history is gone and an account has one project), so this is for good. To stop paying for checks without losing anything, disable the schedule with seo_rankings.projects.update instead; to stop measuring some keywords, deactivate them. Outside the app it always needs a dry run (which counts the keywords and measurements) and a confirm_token; the REST DELETE does not confirm it in line.

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
seo_rankings:write and “Allow deletions” (seo_rankings:delete in OAuth)
Permission
Edit in the module
MCP tool
seo_rankings_projects_deleteOn the full surface; on the compact one, mb_execute_operation with id: "seo_rankings.projects.delete".

Declared cost: up to 20000 reads and 20000 writes per call (they count against the daily quota).

Parameters

project_idstringpathrequired

Rank project id (from seo_rankings.projects.list).

Response

oktrue
keywords_deletednumber
checks_deletednumber
batches_deletednumber
trash_item_idstring

The trash item this deletion left: the item_id of platform.trash.restore (POST /trash/{item_id}/restore). Missing if nothing went to the trash.

trash_item_idsarray<string>

Only when the deletion left more than one copy: all of them, the first being trash_item_id.

trash_expires_atnumber

When the copy leaves the trash for good (epoch ms): 30 days after the deletion.

Errors

Example

curl -X DELETE "https://api.mb-suite.com/v1/seo/rankings/projects/{project_id}" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dry_run": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X DELETE "https://api.mb-suite.com/v1/seo/rankings/projects/{project_id}" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dry_run": true
}'