Research keywords, track rankings, audit sites, monitor backlinks and AI-search visibility — from…
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
MisarSEO API key. Format: Bearer mseo_… — create one at https://seo.misar.io/ai
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Start monitoring one competitor page for changes: title/meta, a real line-level content diff (added/removed/unchanged line counts — never a fabricated similarity score), outbound links, and structured-data types. Checked once a day; the first snapshot is captured on the NEXT scheduled pass, not immediately. FREE — this codebase's own SSRF-safe fetch, no paid provider. Idempotent (re-adding an already-tracked URL is a no-op). A project can monitor at most 20 URLs.
The specific competitor page URL to monitor — not a domain to crawl. Normalised and SSRF-validated server-side; a URL resolving to a private/loopback/metadata address is rejected.
Required. The MisarSEO project ID to scope this call to. Get one from list_projects.
Optional human-readable label, e.g. 'Acme pricing page'.
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Start monitoring one competitor page for changes: title/meta, a real line-level content diff (added/removed/unchanged line counts — never a fabricated similarity score), outbound links, and structured-data types. Checked once a day; the first snapshot is captured on the NEXT scheduled pass, not immediately. FREE — this codebase's own SSRF-safe fetch, no paid provider. Idempotent (re-adding an already-tracked URL is a no-op). A project can monitor at most 20 URLs.
| Argument | Type | Description |
|---|---|---|
| url* | string | The specific competitor page URL to monitor — not a domain to crawl. Normalised and SSRF-validated server-side; a URL resolving to a private/loopback/metadata address is rejected. |
| label | string | Optional human-readable label, e.g. 'Acme pricing page'. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"url"
],
"properties": {
"url": {
"type": "string",
"minLength": 1,
"description": "The specific competitor page URL to monitor — not a domain to crawl. Normalised and SSRF-validated server-side; a URL resolving to a private/loopback/metadata address is rejected."
},
"label": {
"type": "string",
"maxLength": 100,
"description": "Optional human-readable label, e.g. 'Acme pricing page'."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Disavow file management: mark one URL or domain for disavowal. FREE (no credits), but this is a MUTATION. entryType 'url' disavows exactly that page (value must be a full absolute URL); entryType 'domain' disavows the whole domain AND its subdomains (value is a bare host, no scheme) — per Google's own documented disavow-file format, there is no way to disavow just a subpath.
| Argument | Type | Description |
|---|---|---|
| note | string | Optional reason, written as a comment above the entry in the exported file. |
| value* | string | A full absolute URL for entryType 'url'; a bare domain (no scheme) for entryType 'domain'. |
| source | string | Where this entry came from. Defaults to 'manual'. |
| entryType* | string | 'url' disavows one page; 'domain' disavows the whole domain and its subdomains. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"entryType",
"value"
],
"properties": {
"note": {
"type": "string",
"maxLength": 500,
"description": "Optional reason, written as a comment above the entry in the exported file."
},
"value": {
"type": "string",
"minLength": 1,
"description": "A full absolute URL for entryType 'url'; a bare domain (no scheme) for entryType 'domain'."
},
"source": {
"enum": [
"manual",
"bing_own_site"
],
"type": "string",
"description": "Where this entry came from. Defaults to 'manual'."
},
"entryType": {
"enum": [
"url",
"domain"
],
"type": "string",
"description": "'url' disavows one page; 'domain' disavows the whole domain and its subdomains."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Link Building Tool: add one prospect domain/page to a project's list. FREE (no credits), but this is a MUTATION. White-hat scope: contact fields are plain manually-typed free text — NEVER auto-scraped or guessed. New prospects start at status 'prospected'; use update_link_prospect_status to move them through the pipeline.
| Argument | Type | Description |
|---|---|---|
| url | string | Optional specific page on domain, a full absolute URL. |
| note | string | Optional first activity-log entry, e.g. 'Found via competitor backlink profile.' |
| label | string | |
| domain* | string | Bare domain, e.g. example.com. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| targetUrl | string | The project's OWN page this outreach is trying to get linked. |
| contactName | string | |
| contactEmail | string | |
| contactNotes | string |
Link Building Tool: add one manual activity-log entry to a prospect, e.g. "emailed on 9/5" or "follow-up sent". FREE (no credits), but this is a MUTATION.
| Argument | Type | Description |
|---|---|---|
| note* | string | |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| prospectId* | string | Prospect id, from list_link_prospects. |
{
"type": "object",
"required": [
"projectId",
"prospectId",
"note"
],
"properties": {
"note": {
"type": "string",
"maxLength": 2000,
"minLength": 1
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"prospectId": {
"type": "string",
"minLength": 1,
"description": "Prospect id, from list_link_prospects."
}
}
}Adds projects to a portfolio. Returns `{ added }` — only ids that resolve to a project of the CALLER'S organization are added; any other id is silently dropped rather than erroring the whole call. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectIds* | array | MisarSEO project IDs. Use list_projects to find them. |
| portfolioId* | string | Portfolio ID. Use list_portfolios to find it. |
{
"type": "object",
"required": [
"portfolioId",
"projectIds"
],
"properties": {
"projectIds": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 100,
"minItems": 1,
"description": "MisarSEO project IDs. Use list_projects to find them."
},
"portfolioId": {
"type": "string",
"minLength": 1,
"description": "Portfolio ID. Use list_portfolios to find it."
}
}
}Adds a chart note to a rank tracker's own timeline. FREE: database only, no SEO data provider is called and no credits are spent. Config-level, not per-keyword or per-device. A config can hold at most 100 notes.
| Argument | Type | Description |
|---|---|---|
| date* | string | UTC day the note applies to, YYYY-MM-DD. |
| label* | string | What happened, 1-200 characters. |
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId",
"date",
"label"
],
"properties": {
"date": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "UTC day the note applies to, YYYY-MM-DD."
},
"label": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "What happened, 1-200 characters."
},
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Adds keywords to an existing rank tracker config. Returns `{ added, addedIds }` — `added` counts only genuinely new keywords; anything already tracked for this config is silently skipped, so `added` can legitimately be lower than the number of keywords sent. FREE: database only, no SEO data provider is called and no credits are spent — but this CAN fail with a plan-limit error if adding these keywords would put the organization over its plan tier's rank-tracked-keywords cap, since that is a count limit, not a spend limit. This does not trigger a rank check or refresh keyword metrics — call get_rank_tracker afterward once you want positions.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Rank tracker config ID. |
| keywords* | array | 1-100 keywords to add. Duplicates of already-tracked keywords are silently skipped. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId",
"keywords"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID."
},
"keywords": {
"type": "array",
"items": {
"type": "string",
"maxLength": 500,
"minLength": 1
},
"maxItems": 100,
"minItems": 1,
"description": "1-100 keywords to add. Duplicates of already-tracked keywords are silently skipped."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Patches: AI-drafts title AND metaDescription for up to 10 pages in one call. This is a MUTATION and costs one AI-gateway operation per field per page (up to 20) — never a bulk discount. One page/field failing is reported in that entry's own error without sinking the rest of the batch. Requires an org admin.
| Argument | Type | Description |
|---|---|---|
| pageUrls* | array | 1-10 crawled page URLs to draft title+metaDescription patches for. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"pageUrls"
],
"properties": {
"pageUrls": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 10,
"minItems": 1,
"description": "1-10 crawled page URLs to draft title+metaDescription patches for."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Capture a domain-authority snapshot now: calls the same free resolver get_domain_overview already uses and records today's reading, so get_domain_authority_history has a trend to read back. FREE (no credits), but this is a MUTATION — idempotent per UTC day, so calling it twice in one day does not write twice. Useful for a domain get_domain_overview would not auto-capture on its own — e.g. a tracked competitor rather than the project's own domain.
| Argument | Type | Description |
|---|---|---|
| domain* | string | Domain to capture a snapshot for (e.g. 'example.com'). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"domain"
],
"properties": {
"domain": {
"type": "string",
"minLength": 1,
"description": "Domain to capture a snapshot for (e.g. 'example.com')."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Classify 1-200 keywords' search intent from their wording: informational, navigational, commercial, transactional, or unknown. Uses the SAME wording heuristic keyword research already applies as its free-tier fallback, made directly callable for a keyword list with no research/volume data yet. FREE and purely local: no upstream call, no billing, no market dependency. Navigational detection is limited to destination/account phrasing ("login", "official website", ...) that means the same thing regardless of brand — a brand-name navigational query with no such phrasing classifies as `unknown` rather than a guessed label.
| Argument | Type | Description |
|---|---|---|
| keywords* | array | 1-200 keywords to classify. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"keywords"
],
"properties": {
"keywords": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 200,
"minItems": 1,
"description": "1-200 keywords to classify."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Runs ONE prompt through the explore_ai_prompt pipeline and checks a brand plus its named competitors against the SAME real per-model answers for mentions and citations. Bounded scope, stated plainly: this is what one prompt's observed answers show for the named entities — never a comprehensive cross-prompt visibility score. `mentionedByCount` / `citedByCount` are raw counts out of `successfulModelCount`, never a percentage or ranking. `entities[0]` is always the target brand; the rest are the competitors in submitted order. A model that failed upstream reports `status: "error"` with `mentioned`/`cited: null` for every entity — null, not false, because that model was never checked. REQUIRES A PAID PLAN and CHARGES the same as explore_ai_prompt for the requested models; comparing more competitors against that one set of answers costs nothing extra.
| Argument | Type | Description |
|---|---|---|
| brand* | string | The target brand, domain or product checked in each answer. |
| models* | array | 1-4 model personas, as for explore_ai_prompt (persona labels, not vendor-verified models). |
| prompt* | string | The one prompt every requested model answers. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| webSearch | boolean | Ground the answers in a real retrieval pass with cited sources. Defaults to true. |
Publishes a new "What's New" post on the linked Google Business Profile location. It goes LIVE and PUBLIC immediately — there is no draft state and no undo here. Show the user the exact text and get their explicit approval before calling. Only standard posts are supported (no event, offer or alert fields).
| Argument | Type | Description |
|---|---|---|
| summary* | string | The post text. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language of the post. Defaults to en. |
{
"type": "object",
"required": [
"projectId",
"summary"
],
"properties": {
"summary": {
"type": "string",
"maxLength": 1500,
"minLength": 1,
"description": "The post text."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"maxLength": 10,
"minLength": 2,
"description": "Language of the post. Defaults to en."
}
}
}Creates a local pack rank check config: where businessName sits in Google's local pack for one keyword at one location. The location is composed into the query as "<keyword> in <locationLabel>" — not geocoded. Run checks with run_local_pack_rank_check and read them with get_local_pack_rank. FREE: database only.
| Argument | Type | Description |
|---|---|---|
| keyword* | string | The search keyword, e.g. 'coffee shop'. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| businessName* | string | The business name as it appears on Google. |
| languageCode | string | Search language code. |
| locationLabel* | string | Place appended to the query, e.g. 'Shoreditch, London'. |
{
"type": "object",
"required": [
"projectId",
"businessName",
"keyword",
"locationLabel"
],
"properties": {
"keyword": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The search keyword, e.g. 'coffee shop'."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"businessName": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The business name as it appears on Google."
},
"languageCode": {
"type": "string",
"maxLength": 10,
"minLength": 2,
"description": "Search language code."
},
"locationLabel": {
"type": "string",
"maxLength": 300,
"minLength": 1,
"description": "Place appended to the query, e.g. 'Shoreditch, London'."
}
}
}Creates a geo-grid rank-tracking config: a 3x3 grid of points around a centre coordinate, checking ORGANIC rank for one keyword and domain from each point. centerLabel must name the place the grid is centred on — a heatmap with no stated centre is a claim about nowhere. Run it with run_local_rank_grid and read it with get_local_rank_grid. FREE: database only.
| Argument | Type | Description |
|---|---|---|
| domain* | string | Domain whose organic rank is checked. |
| keyword* | string | The search keyword. |
| centerLat* | number | Latitude of the grid centre. |
| centerLng* | number | Longitude of the grid centre. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| spacingKm* | number | Distance between grid points, 0.5-25 km. |
| centerLabel* | string | What the centre point is called, e.g. 'Manchester city centre'. |
| languageCode |
Creates a portfolio. Returns `{ id }`. Starts with no member projects — use add_portfolio_projects next. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| name* | string | Portfolio name, 1-100 characters. |
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Portfolio name, 1-100 characters."
}
}
}Creates a rank tracker config (domain + market + devices + schedule) for a project and returns its configId. Next, add keywords with add_rank_tracking_keywords. Positions are collected by the SCHEDULED check ('daily' or 'weekly'; 'manual' means never on a schedule) and read with get_rank_tracker — nothing here triggers a check on demand. One tracker per (domain, market) per project: repeating a pair is a validation error, not a second tracker. FREE at creation: database only, no credits spent.
| Argument | Type | Description |
|---|---|---|
| domain* | string | Domain whose positions are tracked, e.g. example.com. |
| devices | string | Devices to track. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| serpDepth* | integer | How deep to search, 10-100 in steps of 10. |
| languageCode | string | Language code (e.g. 'en'). Omit it to use the project's default market language. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market. |
| locationHint | string | null | Free-text city/region hint, honoured only where the SERP provider supports it. |
Creates a report definition with a chosen set of widgets, in print order. Returns `{ id }`. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| name* | string | Report name, 1-100 characters. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| widgetTypes* | array | Widget types, in print order. Catalogue: site_health (Crawl findings and issue counts from the latest audit.); internal_linking (Orphan pages, dead ends and link depth from the latest crawl.); rank_tracking (Rank-tracker scorecards per tracked domain.); keywords (Saved keywords with volume, difficulty and CPC.); search_console (Search Console totals and top queries/pages.); web_analytics (Sessions, pageviews and bounce rate from the tracking snippet.); gsc_coverage (How many of your pages Search Console has inspected, and their most common state.); rank_movers (Keywords that newly started ranking or climbed fast.); rank_volatility (How much your own positions churned between the last two checks.); share_of_voice (Your tracked domain's share of visibility against its competitors.); log_analyzer (Total server hits and the AI-crawler share of them.) |
{
"type": "object",
"required": [
"projectId",
"name",
"widgetTypes"
],
"properties": {
"name": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Report name, 1-100 characters."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"widgetTypes": {
"type": "array",
"items": {
"enum": [
"site_health",
"internal_linking",
"rank_tracking",
"keywords",
"search_console",
"web_analytics",
"gsc_coverage",
"rank_movers",
"rank_volatility",
"share_of_voice",
"log_analyzer"
],
"type": "string"
},
"maxItems": 11,
"description": "Widget types, in print order. Catalogue: site_health (Crawl findings and issue counts from the latest audit.); internal_linking (Orphan pages, dead ends and link depth from the latest crawl.); rank_tracking (Rank-tracker scorecards per tracked domain.); keywords (Saved keywords with volume, difficulty and CPC.); search_console (Search Console totals and top queries/pages.); web_analytics (Sessions, pageviews and bounce rate from the tracking snippet.); gsc_coverage (How many of your pages Search Console has inspected, and their most common state.); rank_movers (Keywords that newly started ranking or climbed fast.); rank_volatility (How much your own positions churned between the last two checks.); share_of_voice (Your tracked domain's share of visibility against its competitors.); log_analyzer (Total server hits and the AI-crawler share of them.)"
}
}
}Deletes a brand monitor, stopping its scheduled scans. Requires the admin role in the calling organization. Get the monitorId from list_brand_monitors.
| Argument | Type | Description |
|---|---|---|
| monitorId* | string | Brand monitor ID, from list_brand_monitors. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"monitorId"
],
"properties": {
"monitorId": {
"type": "string",
"minLength": 1,
"description": "Brand monitor ID, from list_brand_monitors."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Removes the owner's published reply from one Google review (reviewId from get_gbp_reviews). The removal is public and immediate. Confirm with the user before calling.
| Argument | Type | Description |
|---|---|---|
| reviewId* | string | Review ID, from get_gbp_reviews. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"reviewId"
],
"properties": {
"reviewId": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Review ID, from get_gbp_reviews."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Deletes a local pack rank check config, INCLUDING its whole check history. Confirm with the user before calling.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Config ID, from the matching get_* tool called without configId. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Config ID, from the matching get_* tool called without configId."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Deletes a geo-grid rank-tracking config, INCLUDING its whole run and point history. Confirm with the user before calling.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Config ID, from the matching get_* tool called without configId. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Config ID, from the matching get_* tool called without configId."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Deletes a portfolio. Member projects themselves are untouched — only the grouping is removed. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| portfolioId* | string | Portfolio ID. Use list_portfolios to find it. |
{
"type": "object",
"required": [
"portfolioId"
],
"properties": {
"portfolioId": {
"type": "string",
"minLength": 1,
"description": "Portfolio ID. Use list_portfolios to find it."
}
}
}Deletes a report definition. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| reportDefinitionId* | string | Report definition ID. Use list_report_definitions to find it. |
{
"type": "object",
"required": [
"projectId",
"reportDefinitionId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"reportDefinitionId": {
"type": "string",
"minLength": 1,
"description": "Report definition ID. Use list_report_definitions to find it."
}
}
}Deletes a report definition's schedule. Requires the admin role in the calling organization, same as creating one. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| reportDefinitionId* | string | Report definition ID. Use list_report_definitions to find it. |
{
"type": "object",
"required": [
"projectId",
"reportDefinitionId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"reportDefinitionId": {
"type": "string",
"minLength": 1,
"description": "Report definition ID. Use list_report_definitions to find it."
}
}
}Deletes a tracked prompt, stopping its scheduled checks. Requires the admin role in the calling organization. Get the trackedPromptId from list_tracked_prompts.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| trackedPromptId* | string | Tracked prompt ID, from list_tracked_prompts. |
{
"type": "object",
"required": [
"projectId",
"trackedPromptId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"trackedPromptId": {
"type": "string",
"minLength": 1,
"description": "Tracked prompt ID, from list_tracked_prompts."
}
}
}Patches: stops auto-publishing freshly AI-drafted patches for this issue type — future drafts wait for a manual publish_patch call again. Already-published patches are unaffected. FREE (no credits), but this is a MUTATION. Requires an org admin.
| Argument | Type | Description |
|---|---|---|
| issueType* | string | Which issue type's auto-publish rule to remove. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"issueType"
],
"properties": {
"issueType": {
"enum": [
"missing_title",
"title_too_long",
"title_too_short",
"missing_meta_description",
"meta_description_too_long",
"missing_canonical",
"broken_page"
],
"type": "string",
"description": "Which issue type's auto-publish rule to remove."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Full disconnect: drops BOTH the Rank Tracker and Site Audit bindings and the stored grant. Breaks Ahrefs data for every member of the organization, not just the caller — requires at least the admin role.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Remove a project's Baidu Webmaster connection. Breaks Baidu push submission for every member of the organization, not just the caller — requires at least the admin role.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Unlink a project's Bing Webmaster site. Breaks Bing Webmaster data for every member of the organization, not just the caller — requires at least the admin role.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Unlink a project's Google Analytics property. Breaks Analytics data for every member of the organization, not just the caller — requires at least the admin role.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Removes the project's link to its Google Business Profile location. Only MisarSEO's own record of the link is removed — the location on Google is untouched — but every GBP tool stops working for the project until it is linked again.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Unlink a project's Yandex Webmaster host. Breaks Yandex Webmaster data for every member of the organization, not just the caller — requires at least the admin role.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Patches: drafts a title/metaDescription/canonical/redirect fix for one page's finding. FREE for canonical/redirect (deterministic — canonical is always the page's own URL, redirect always needs an explicit value); title/metaDescription cost one AI-gateway operation UNLESS `value` is given, which sets an explicit replacement instead of drafting one. This is a MUTATION — it creates or overwrites this page+field's draft, but does not publish it (use publish_patch for that). Requires an org admin.
| Argument | Type | Description |
|---|---|---|
| value | string | An explicit replacement value instead of AI-drafting one. Required for redirect (broken_page) — there is no computable default. Never used for canonical, which is always the page's own URL. |
| pageUrl* | string | The exact crawled page URL this finding is on. |
| issueType* | string | Which finding this patch addresses — determines the field (title/metaDescription/canonical/redirect). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"pageUrl",
"issueType"
],
"properties": {
"value": {
"type": "string",
"maxLength": 500,
"minLength": 1,
"description": "An explicit replacement value instead of AI-drafting one. Required for redirect (broken_page) — there is no computable default. Never used for canonical, which is always the page's own URL."
},
"pageUrl": {
"type": "string",
"minLength": 1,
"description": "The exact crawled page URL this finding is on."
},
"issueType": {
"enum": [
"missing_title",
"title_too_long",
"title_too_short",
"missing_meta_description",
"meta_description_too_long",
"missing_canonical",
"broken_page"
],
"type": "string",
"description": "Which finding this patch addresses — determines the field (title/metaDescription/canonical/redirect)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Patches: from now on, a freshly AI-drafted patch for this issue type publishes immediately instead of waiting for a manual publish_patch call — capped at 20 auto-publishes per project per day. A hand-typed replacement value (draft_patch's `value` argument) is NEVER auto-published regardless of this rule. FREE (no credits), but this is a MUTATION. Requires an org admin.
| Argument | Type | Description |
|---|---|---|
| issueType* | string | Which finding type to auto-publish AI-drafted patches for. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"issueType"
],
"properties": {
"issueType": {
"enum": [
"missing_title",
"title_too_long",
"title_too_short",
"missing_meta_description",
"meta_description_too_long",
"missing_canonical",
"broken_page"
],
"type": "string",
"description": "Which finding type to auto-publish AI-drafted patches for."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Runs one prompt through up to 4 model personas and returns each answer verbatim, with real citations when webSearch is on. The four persona labels (chat_gpt/claude/gemini/perplexity) are NOT vendor-verified — none is guaranteed to be that vendor's own model; 'claude' and 'perplexity' in particular have no real vendor route at all. Treat every answer as 'a model answering as X', never as ground truth from X. REQUIRES A PAID PLAN and CHARGES one shared AI-gateway operation per call (not per model) — plus one more if analyzeSentiment and highlightBrand are BOTH set.
| Argument | Type | Description |
|---|---|---|
| models* | array | 1-4 model personas to answer the prompt (chat_gpt, claude, gemini, perplexity). See this tool's own description for what these labels do and do not guarantee. |
| prompt* | string | The prompt to send to each requested model. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| webSearch | boolean | Ground the answer in a real retrieval pass with cited sources, shared across every requested persona. Defaults to true. |
| highlightBrand | string | A brand/entity name to highlight mentions of in each answer. Required for analyzeSentiment. |
| analyzeSentiment | boolean | Classify sentiment toward highlightBrand in each answer. Requires highlightBrand. Spends one EXTRA AI-gateway operation on top of the base call — never charged unless both this and highlightBrand are set. |
Disavow file management: export a project's disavow entries as the exact .txt content Google's disavow tool accepts (support.google.com/webmasters/answer/2648487) — one bare URL per line to disavow that exact page, or `domain:<host>` to disavow an entire domain and its subdomains. FREE (no credits). Save `fileContent` to a .txt file as-is (UTF-8).
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Link Building Tool: export a project's prospect list as CSV text. FREE (no credits). Save `csvContent` to a .csv file as-is (UTF-8).
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Compares domains competing in search results for a supplied keyword set. Useful for market and search-intelligence reports; not radius-based local SEO. The inverse of get_keyword_gap, which asks what one competitor ranks for that we do not. CHARGES CREDITS ON THE PAID DATA PROVIDER, which is also the only one that honours `resultTypes` and `includeSubdomains` and that measures `visibility`, `etv` and `rating`. On the default free provider competitors are aggregated from one live SERP per keyword: NOTHING IS CHARGED, only ORGANIC results are counted whatever `resultTypes` says, `includeSubdomains` is ignored, and visibility/traffic-estimate/rating are left unmeasured. Sorting never treats an unmeasured field as zero: rows the provider did not measure sort last and are reported in `unmeasuredForSort`. If that count equals the row count, the list is NOT ranked by anything — say so instead of presenting the first row as the strongest competitor. Omit `locationCode`/`languageCode` to use the project's default market.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Maximum competitors to return (1-100). Defaults to 50. |
| offset | integer | Rows to skip for pagination. |
| sortBy | string | Sort order for returned competitors. Defaults to visibility. Only `keyword_count` and `avg_position` are measured on every provider — `visibility` and `traffic_estimate` are unmeasured on the free provider, and rows without the chosen field sort last rather than as zero. Prefer `keyword_count` when you need an order that always means something. |
| keywords* | array | Keywords whose SERPs are compared (1-100). |
| projectId |
Compare a site audit against an earlier one: which issues are new, which were fixed, how many persist, and the health-score delta. Only pages BOTH crawls visited are compared, so a shorter crawl never reports the pages it missed as fixed — those are reported separately as notComparableCount. `newIssues` only ever covers an existing page newly showing a finding, never a brand-new page's own findings (those fold into notComparableCount/addedUrlCount instead, since a page neither crawl could compare has no meaningful 'new' vs. 'always there' answer). Returns an error when there is no earlier completed audit to compare against. Free — charges no credits.
| Argument | Type | Description |
|---|---|---|
| auditId* | string | Audit ID to compare, from crawl_site or get_crawl_results. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| baselineAuditId | string | Audit ID to compare against. OMIT IT unless you have a reason: omitting picks the COMPLETED run immediately before auditId, while a baseline named here is used as given with no completed-status check — pointing it at a failed or still-running crawl diffs against a partial page set. Comparing an audit to itself is a validation error. |
{
"type": "object",
"required": [
"projectId",
"auditId"
],
"properties": {
"auditId": {
"type": "string",
"minLength": 1,
"description": "Audit ID to compare, from crawl_site or get_crawl_results."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"baselineAuditId": {
"type": "string",
"minLength": 1,
"description": "Audit ID to compare against. OMIT IT unless you have a reason: omitting picks the COMPLETED run immediately before auditId, while a baseline named here is used as given with no completed-status check — pointing it at a failed or still-running crawl diffs against a partial page set. Comparing an audit to itself is a validation error."
}
}
}Core Web Vitals report: LCP/INP/CLS per page/strategy this audit already ran Lighthouse against, classified against Google's own published thresholds. No new fetch — reads audit_lighthouse_results, the same table the results UI already renders as raw numbers. LAB DATA ONLY, never Search Console's real Chrome UX Report (CrUX) field data — every number is a single simulated Lighthouse run, not an aggregated real-user measurement. This codebase's audit pipeline runs local headless Chrome as its PRIMARY path, so CrUX field data is not reliably available for any given page. ALWAYS narrow on `state` first: `no_lighthouse_data` (this audit has no Lighthouse rows at all — an absence of data, NOT every page passing) or `analysed`. Errors when no audit with that id belongs to the project. `pages[].allGood` is true only when all three metrics were measured AND all three are "good". Free — charges no credits.
| Argument | Type | Description |
|---|---|---|
| auditId* | string | Audit ID, from crawl_site or get_crawl_results. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"auditId"
],
"properties": {
"auditId": {
"type": "string",
"minLength": 1,
"description": "Audit ID, from crawl_site or get_crawl_results."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}HTTPS Report: percentage of pages secure, mixed-content and insecure-link counts, homepage HTTPS-path status, and HTTP URLs in an HTTPS site's sitemap — for a project's latest completed crawl (or a named earlier one). Derives entirely from already-computed crawl data — no new work at read time. SOCKET-LEVEL TLS CHECKS STAY EXPLICITLY UNBUILT: expiring/expired certificate, old security protocol, incorrect certificate name, and subdomains without SNI/HSTS all need a real TLS handshake this fetch-based crawler does not make. `notDerivable` lists these with the reason. `homepageStatus.redirectsToHttps` is null (not measured), never assumed healthy, unless a standalone fetch actually observed a failure — this report cannot distinguish "checked and fine" from "never checked" from stored issue rows alone. ALWAYS narrow on `state` first: `no_audit` (no completed crawl yet), `analysed`. Free — charges no credits, makes no external call.
| Argument | Type | Description |
|---|---|---|
| auditId | string | Audit ID, from crawl_site or get_crawl_results. Omitted means the latest completed audit for this project. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"auditId": {
"type": "string",
"minLength": 1,
"description": "Audit ID, from crawl_site or get_crawl_results. Omitted means the latest completed audit for this project."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Internal Link Report: outbound/inbound internal link counts per page, orphan pages, dead ends, and click-depth distribution for a project's latest completed crawl (or a named earlier one). No new computation — reads the same audit_pages/audit_issues columns already powering the in-app internal-links page. INTERNAL LINKS ONLY. External links are explicitly out of scope: an external link graph needs data this codebase does not collect. `totals.externalLinkOccurrences` is the one external-facing number here — a raw per-page outbound tally, never a graph. ALWAYS narrow on `state` first: `no_audit` (no completed crawl yet), `signal_not_captured` (a crawl exists but its link counts predate the inbound-link/click-depth migration or were otherwise never computed — an absence of data, NOT a site with no internal links), `analysed`. `notDerivable` is returned with every state and lists what this report can never compute from what is stored, and why. Free — charges no credits, makes no external call.
| Argument | Type | Description |
|---|---|---|
| auditId | string | Audit ID, from crawl_site or get_crawl_results. Omitted means the latest completed audit for this project. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"auditId": {
"type": "string",
"minLength": 1,
"description": "Audit ID, from crawl_site or get_crawl_results. Omitted means the latest completed audit for this project."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Structured Data Report: pages grouped by schema.org @type, which pages have none, and which pages carry an unparseable JSON-LD block — for a project's latest completed crawl (or a named earlier one). Derives entirely from structured data already extracted at crawl time — no new crawl-time work. PRESENCE AND PARSE VALIDITY, NOT A RICH-RESULTS ELIGIBILITY CHECK. Only `typeGroups[].invalidCount` on the BreadcrumbList group is a real, verified per-type validity verdict; every other type reports only whether pages declare it and whether ANY block on the page failed to parse (page-level — never attributable to one type when a page declares several). ALWAYS narrow on `state` first: `no_audit` (no completed crawl yet), `analysed` (`typeGroups: []` means every page was measured and none declared any structured data — a real finding, not a withheld section). `notDerivable` lists what this report can never compute, and why. Free — charges no credits, makes no external call.
| Argument | Type | Description |
|---|---|---|
| auditId | string | Audit ID, from crawl_site or get_crawl_results. Omitted means the latest completed audit for this project. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"auditId": {
"type": "string",
"minLength": 1,
"description": "Audit ID, from crawl_site or get_crawl_results. Omitted means the latest completed audit for this project."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Anchor-text distribution for a domain or URL, one page at a time: which anchor texts link to it, with backlinks, referring domains, referring pages and dofollow links per anchor. This is what reveals an over-optimised profile (one exact-match commercial phrase dominating) or a hacked one (pharma/casino anchors nobody wrote) — both invisible link by link. CHARGES CREDITS ON THE PAID DATA PROVIDER (~200-500 typical). UNAVAILABLE ON THE DEFAULT FREE DATA PROVIDER, like the rest of the backlinks surface: SEO_DATA_UNAVAILABLE there means the question was never asked, never an empty distribution. There is deliberately no spam-score column — an averaged spam score is not a spam score.
| Argument | Type | Description |
|---|---|---|
| page | integer | 1-based page number. Defaults to 1. |
| scope | string | Whether target is a whole domain or one page. Defaults to domain. |
| target* | string | Domain or URL whose inbound anchor texts to list. |
| pageSize | integer | Rows per page. Defaults to 100. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| sortField | string | Sort column. Defaults to backlinks. |
| sortOrder | string | Sort direction. Defaults to desc. |
Returns a backlinks profile summary (total backlinks, referring domains, referring pages, rank) plus the top referring domains by backlink count. CHARGES CREDITS ON THE PAID DATA PROVIDER (~200-500 typical): two lookups per call, the summary and one page of referring domains. UNAVAILABLE ON THE DEFAULT FREE DATA PROVIDER: a backlink graph needs a proprietary web-scale crawl index, so every method of this surface throws SEO_DATA_UNAVAILABLE there and nothing is charged. That error means the question was never asked — never report it as a site with no backlinks. Self-hosted deployments also need the Backlinks API enabled on their DataForSEO account. The two halves count DIFFERENT populations: the summary totals are unfiltered, while the referring-domain list has domains above the spam-score threshold filtered out. Do not present the listed domains as the top slice of the summary total, and do not subtract one from the other.
| Argument | Type | Description |
|---|---|---|
| scope | string | 'domain' analyzes the whole domain; 'page' analyzes a specific URL. Defaults to 'domain'. |
| target* | string | Domain or URL to analyze (e.g. 'example.com' or 'https://example.com/blog'). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"target"
],
"properties": {
"scope": {
"enum": [
"domain",
"page"
],
"type": "string",
"description": "'domain' analyzes the whole domain; 'page' analyzes a specific URL. Defaults to 'domain'."
},
"target": {
"type": "string",
"minLength": 1,
"description": "Domain or URL to analyze (e.g. 'example.com' or 'https://example.com/blog')."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}The project's current Baidu Webmaster (百度搜索资源平台) connection, plus today's push-quota usage. `today.lastRemain` is Baidu's own authoritative remaining-quota count as of the last push (null until the first push of the day) — `today.assumedDailyQuota` is a courtesy default only, never a real ceiling, since Baidu computes quota per-site dynamically and publishes no fixed number.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}GetLinkCounts: own-site inbound-link page list for a project's matched Bing site — the focused, single-call form of the backlinks field already embedded in get_bing_site_overview. FREE (no credits). OWN-SITE ONLY: Bing exposes no competitor/any-domain backlink lookup, and some verified sites are known to return an empty result here even when populated. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| page | integer | 0-based page of Bing's own pagination (default 0). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"page": {
"type": "integer",
"minimum": 0,
"description": "0-based page of Bing's own pagination (default 0)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}GetQueryTrafficStats + GetPageStats + GetPageQueryStats + GetQueryPageStats + GetQueryPageDetailStats: per-query and per-page search-performance detail for a project's matched Bing site. FREE (no credits). pageStats (site-wide, weekly) is always attempted. queryTrafficStats/queryPageStats are null unless `query` is given; pageQueryStats is null unless `pageUrl` is given; queryPageDetailStats is null unless both are given — a null section absent from `errors` was not requested, not a failure. pageStats carries one unresolved caveat: Microsoft's own doc page is internally inconsistent about whether that method's `query` field on each row holds a query string or a page URL — surfaced as-is. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| query | string | The search query to fetch traffic/page stats for. Required for queryTrafficStats/queryPageStats (and, with pageUrl, queryPageDetailStats). |
| pageUrl | string | The page to fetch query stats for. Required for pageQueryStats (and, with query, queryPageDetailStats). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"query": {
"type": "string",
"minLength": 1,
"description": "The search query to fetch traffic/page stats for. Required for queryTrafficStats/queryPageStats (and, with pageUrl, queryPageDetailStats)."
},
"pageUrl": {
"type": "string",
"format": "uri",
"description": "The page to fetch query stats for. Required for pageQueryStats (and, with query, queryPageDetailStats)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}GetFeedDetails: the same Feed shape as one entry from Bing's GetFeeds list, for one specific feed URL on a project's matched site. FREE (no credits), read-only. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| feedUrl* | string | Absolute URL of the previously-submitted sitemap/feed. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"feedUrl"
],
"properties": {
"feedUrl": {
"type": "string",
"format": "uri",
"description": "Absolute URL of the previously-submitted sitemap/feed."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}GetKeyword + GetKeywordStats + GetRelatedKeywords: real Bing impression data for a query, its recent weekly history, and related queries — read straight from the Bing Webmaster API. FREE (no credits). Unlike derived/estimated keyword metrics, these numbers come from Bing's own search-impression logs. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| query* | string | The search query to research. |
| country | string | Two-letter country code, e.g. 'us'. |
| endDate | string | YYYY-MM-DD. Applies to keyword/relatedKeywords only. |
| language | string | Language-region code, e.g. 'en-US'. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate | string | YYYY-MM-DD. Applies to keyword/relatedKeywords only. |
{
"type": "object",
"required": [
"projectId",
"query"
],
"properties": {
"query": {
"type": "string",
"minLength": 1,
"description": "The search query to research."
},
"country": {
"type": "string",
"description": "Two-letter country code, e.g. 'us'."
},
"endDate": {
"type": "string",
"description": "YYYY-MM-DD. Applies to keyword/relatedKeywords only."
},
"language": {
"type": "string",
"description": "Language-region code, e.g. 'en-US'."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"description": "YYYY-MM-DD. Applies to keyword/relatedKeywords only."
}
}
}Bing's own view of a project's domain: daily search performance (clicks/impressions), per-query stats, own-site inbound links, remaining URL Submission quota, crawl diagnostics (crawl stats, crawl issues, crawl settings), submitted feeds and query-parameter settings — read straight from the Bing Webmaster API. FREE (no credits). Bring-your-own-key: requires the deployment operator to set BING_WEBMASTER_API_KEY (one key covers every site verified on that Bing account — no per-project connect step). Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account. Backlinks here are OWN-SITE ONLY — Bing exposes no competitor/any-domain backlink lookup, so this does not substitute for general backlink research. Per-URL lookups and keyword research have their own tools: get_bing_url_links, get_bing_url_info, get_bing_keyword_research.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}GetUrlInfo + optionally GetChildrenUrlInfo: index/crawl metadata (anchor count, discovery/last-crawled dates, HTTP status, whether Bing considers it a page) for one URL on a project's matched Bing site, and, when includeChildren is set, the same metadata for every URL Bing discovered under it. FREE (no credits). Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| url* | string | The URL to look up. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| includeChildren | boolean | Also fetch the index/crawl metadata for URLs discovered under this one (one page of Bing's own pagination). Default false. |
{
"type": "object",
"required": [
"projectId",
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "The URL to look up."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"includeChildren": {
"type": "boolean",
"description": "Also fetch the index/crawl metadata for URLs discovered under this one (one page of Bing's own pagination). Default false."
}
}
}GetUrlLinks: the inbound links (anchor text + source URL) pointing at one specific page on a project's matched Bing site, read straight from the Bing Webmaster API. FREE (no credits). Own-site only — Bing exposes no competitor/any-domain backlink lookup, and some verified sites are known to return an empty result here even when populated. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| url* | string | The specific page to find inbound links for. |
| page | integer | 0-based page of Bing's own pagination (default 0). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "The specific page to find inbound links for."
},
"page": {
"type": "integer",
"minimum": 0,
"description": "0-based page of Bing's own pagination (default 0)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Status of a Brand Radar scan started by run_brand_radar_scan, by its `jobId`. `status` is queued/running/complete/errored; a complete scan carries the shaped `result` (share of voice, top cited pages, top prompts), an errored one carries `error.message`. FREE to poll: a completed scan re-reads the cached result and charges nothing. The one exception is a scan whose cached result has already expired (older than ~24h) — answering that re-runs the lookup and is charged and rate limited like a new scan, so collect results promptly. Use this instead of re-running run_brand_radar_scan, which always spends another monthly scan.
| Argument | Type | Description |
|---|---|---|
| jobId* | string | The `jobId` returned by run_brand_radar_scan. |
| projectId* | string | Required. The MisarSEO project ID the scan was run for. |
{
"type": "object",
"required": [
"projectId",
"jobId"
],
"properties": {
"jobId": {
"type": "string",
"minLength": 1,
"description": "The `jobId` returned by run_brand_radar_scan."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID the scan was run for."
}
}
}The recorded AI-visibility history for one brand or domain: mentions, cited sources and competitors seen at each past Brand Radar scan, oldest first. Answers whether visibility in AI answers is moving, which a single run_brand_radar_scan cannot. Returns only scans that actually ran — empty for a target never scanned, filling in as scans run, with nothing interpolated or back-filled. FREE — reads recorded scans, charges no credits.
| Argument | Type | Description |
|---|---|---|
| since | string | Inclusive lower bound, YYYY-MM-DD. |
| target* | string | Brand or domain, matched case-insensitively against past scans. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"target"
],
"properties": {
"since": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Inclusive lower bound, YYYY-MM-DD."
},
"target": {
"type": "string",
"maxLength": 250,
"minLength": 1,
"description": "Brand or domain, matched case-insensitively against past scans."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Checks where a project's domain (or an explicit targetDomain) ranks in REAL Brave Search results for one keyword. Brave has run its own independent crawler and index since 2023 — a genuinely separate, separately-licensed data source, distinct from this server's free SearXNG/openserp-backed rank checks. Every successful response carries engine: 'brave_search' so it can never be mistaken for a free-tier number. PAID: bring-your-own-key — requires the deployment operator to set BRAVE_SEARCH_API_KEY (a single paid credential, like OPENPAGERANK_API_KEY — no OAuth flow). Every call spends that key's Brave Search API budget. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_domain' when neither targetDomain nor the project's own domain is set. Makes exactly one Brave Search API call per check — depth (max 20) is Brave's own per-request ceiling, never paginated to search deeper. Brave publishes no submission/indexing API at all, only manual re-fetch/delist web forms — this tool is query-only.
| Argument | Type | Description |
|---|---|---|
| depth | integer | Results to fetch, 1-20 (Brave's own per-request ceiling). Default 20. |
| country | string | 2-letter country code Brave uses to target results, e.g. 'US'. |
| keyword* | string | Keyword to check. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| searchLang | string |
Finds the queries where the project's connected Search Console property competes with itself: two or more of its own pages drawing impressions for the same query, splitting that query's clicks, click signals and ranking history across URLs. Read-only and FREE (no credits) — Search Console is first-party data and meters nothing. It does spend Google's per-property request quota, so it is rate limited per user. Requires Search Console to be connected for the project. Returns exactly ONE of four states, which are four different facts — branch on `state` before reading anything else: `not_connected` (nothing is bound; tell the user to connect a property), `requires_reconnect` (bound but the Google grant is revoked/expired; tell the user to reconnect), `no_search_data` (Search Console returned nothing for the window — an absence of data, NOT a clean bill of health), `analysed` (we looked; `findings` may legitimately be empty and `coverage` is the proof, including `truncated` when the property had more rows than one report can fetch). In each of the first three states nothing was measured at all, so never report any of them as 'no cannibalisation found'. Every per-page figure is Search Console's own for that (query, page) pair — ctr is a 0-1 fraction, position is a 1-based average, dates are Pacific Time and the last ~3 days are excluded for data lag. `thresholds` ships with each analysed report so you can say whether a threshold, rather than the absence of a problem, is why a query is missing.
| Argument | Type | Description |
|---|---|---|
| sort | string | Finding order (default impressions). 'impressions' = most affected search demand first. 'position_spread' = smallest gap between the competing pages' average positions first, i.e. the pages Google rated most interchangeable. Both order by one measured quantity; there is deliberately no composite severity score. |
| window | string | Analysis window (default last_28_days). Capped at three months on purpose — Search Console's longer ranges are NOT offered, because over a long window 'two pages received impressions' also describes a page migration, and calling that cannibalisation sends someone to consolidate pages that are already consolidated. The window end is backed off ~3 days for Search Console's data lag. |
Timeline of DETECTED changes for one monitored competitor URL, newest first — a check that found nothing does not appear here. Each entry's summary is a real diff: line-level content added/removed/unchanged counts with a short sample (never a fabricated similarity score), plus title/meta/status-code before-after values and added/removed external links and structured-data types.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Max entries to return. Defaults to 50, capped at 200. |
| targetId* | string | Monitored URL's target ID, from list_competitor_monitors or add_competitor_monitor. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"targetId"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Max entries to return. Defaults to 50, capped at 200."
},
"targetId": {
"type": "string",
"minLength": 1,
"description": "Monitored URL's target ID, from list_competitor_monitors or add_competitor_monitor."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Finds pages whose clicks declined by a meaningful amount between two adjacent windows, read entirely from MisarSEO's own archived Search Console history (the top-200-per-day per-page archive the nightly sync fills in). Read-only and FREE (no credits), and makes NO live Search Console call — unlike get_cannibalisation_report, this spends no Google quota, so it is rate limited only against ordinary abuse. Requires Search Console to be connected for the project. Returns exactly ONE of three states, which are three different facts — branch on `state` before reading anything else: `not_connected` (nothing is bound; tell the user to connect a property), `insufficient_history` (bound, but one of the two comparison windows has no archived rows at all — an absence of data, NOT a clean bill of health), `analysed` (we looked; `findings` may legitimately be empty and `coverage` is the proof). In both of the first two states nothing was measured, so never report either as 'no content decay found'. `window` selects the RECENT period length; the EARLIER period compared against it is the same length immediately before it, not a calendar-month split. `minEarlierClicks` (default 10) and `declineThresholdPct` (default 20) are this codebase's own chosen defaults, not a published vendor formula. A page that fell out of the archive's top-200-per-day entirely reports `recentClicks: 0` — correctly the most severe decay, not a data hole.
| Argument | Type | Description |
|---|---|---|
| window | string | The RECENT period length (default last_28_days). The EARLIER period compared against it is the same length immediately before it — a period-over-period comparison, not a calendar-month split. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| minEarlierClicks |
On-page recommendations and an AI content grade for one page, benchmarked against the pages currently ranking for its target keyword, built together — one AI-gateway call produces both the grade and the recommendations). CHARGES ONE AI-gateway operation against the organization's shared monthly ceiling — not a plan-quota dimension of its own. The page crawl (target + up to 5 competitors) and the SERP fetch are both free. `url` does not have to belong to the project's own domain or to a page a site audit has crawled — any http(s) URL is graded fresh. ALWAYS narrow on `state` first: `target_unreachable` means the target page itself could not be fetched — an ordinary outcome, never treat it as 'this page scored zero'. `analysed` carries the full report; check `coverage.serpUnavailable` and `competitorsCrawled` before trusting the competitor comparison. `grade` is an AI-generated qualitative read of the measured signals, NOT a calibrated industry score. Does NOT detect AI-written content — that is not built; no reliable free technique exists for it.
| Argument | Type | Description |
|---|---|---|
| url* | string | The page to grade (any http(s) URL). |
| keyword* | string | The keyword this page is meant to rank for. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code. Defaults to the project's stored market, else 'en'. |
Lightweight on-page metrics for one arbitrary URL — the content optimizer's keyword-free, AI-free sibling. Built for ambient per-page checks (e.g. the browser extension's on-page panel checking whatever page the user is currently viewing) with none of `get_content_optimizer_report`'s cost: no keyword required, no AI-gateway charge, no competitor crawl or SERP fetch. `url` does not have to belong to the project's own domain — any http(s) URL is analyzed fresh. ALWAYS narrow on `state` first: `unreachable` means the page itself could not be fetched — an ordinary outcome for an arbitrary URL check, never treat it as an error.
| Argument | Type | Description |
|---|---|---|
| url* | string | The page to analyze (any http(s) URL). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "The page to analyze (any http(s) URL)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}One card per active project in the caller's organization: latest audit health, rank-tracker status, and trailing-7-day web-analytics sessions. FREE: database only. No SEO data provider is called and no credits are spent — GSC and backlinks are deliberately excluded from this view rather than fetched live, so calling this never spends a Search Console quota unit or a paid-provider credit. `audit`/`rankTracking`/`webAnalytics` are null on a card whose project has none of that data yet (never audited, no tracker configured, no analytics traffic) — a real 'not set up' fact, not a zero standing in for missing data.
No arguments.
{
"type": "object",
"properties": {}
}Runs a prompt against up to 5 REAL, directly-verified vendor models — you_com (You.com Answer API), deepseek, grok (xAI), meta_ai (Meta's Model API) and minimax — each called through that vendor's own public API. Unlike explore_ai_prompt's persona labels, every result here reports the real vendor model id in `modelName`. A model whose API key is not configured on this deployment answers `status: "unavailable"` with no call made — a deployment fact, not an error; `status: "error"` means the vendor is configured but the call failed. `webSearch` is best-effort per vendor: You.com is always grounded, grok honours it, and deepseek, meta_ai and minimax always answer `webSearch: false`. meta_ai is a US-developers-only public preview, so an ineligible account reports an error naming that. Billed on the operator's own vendor keys: no plan gate and no MisarSEO credits are spent.
| Argument | Type | Description |
|---|---|---|
| models* | array | 1-5 vendor models to ask. |
| prompt* | string | The prompt to send to each requested vendor model. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| webSearch | boolean | Ask for grounded answers where the vendor supports it (see the tool description). Defaults to false. |
| highlightBrand | string | A brand/entity name to highlight mentions of in each answer. |
Domain-authority history: the stored trend of dated authority snapshots for one project+domain, oldest first. FREE (no credits) — reads back snapshots already captured, no provider call. Empty until at least one snapshot exists: get_domain_overview auto-captures one whenever `domain` is the project's own domain, or call capture_domain_authority_snapshot directly for any domain (including a tracked competitor).
| Argument | Type | Description |
|---|---|---|
| domain* | string | Domain to read history for (e.g. 'example.com'). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"domain"
],
"properties": {
"domain": {
"type": "string",
"minLength": 1,
"description": "Domain to read history for (e.g. 'example.com')."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Domain batch analysis: one authority row per domain, up to 100 at once. FREE — no paid provider call, unlike get_domain_overview. `organicTraffic`, `organicKeywords`, `backlinks` and `referringDomains` are ALWAYS null on every row — batch mode never attempts the per-domain paid rank-overview call the single-domain overview makes, since fanning that out across many domains in one call is a materially bigger paid-provider commitment than anything else this codebase does in one request. `organicUnavailableReason` states this once for the whole response; use get_domain_overview for organic figures on one domain at a time. Malformed domain entries are dropped and counted in `skippedInvalid` rather than failing the whole call. `authorityUnavailableReason` is set only when the authority provider itself failed to answer — null authority on every row without it set means every domain was looked up and found unranked, not that nothing could be looked up.
| Argument | Type | Description |
|---|---|---|
| domains* | array | 1-100 domains or URLs, any mix. Malformed entries are dropped, not rejected — see skippedInvalid in the result. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| includeSubdomains | boolean | Count subdomains as part of each domain. Defaults to false, unlike get_domain_overview's default true. |
{
"type": "object",
"required": [
"projectId",
"domains"
],
"properties": {
"domains": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 100,
"minItems": 1,
"description": "1-100 domains or URLs, any mix. Malformed entries are dropped, not rejected — see skippedInvalid in the result."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"includeSubdomains": {
"type": "boolean",
"default": false,
"description": "Count subdomains as part of each domain. Defaults to false, unlike get_domain_overview's default true."
}
}
}Returns a high-level view of a domain's organic footprint: estimated organic traffic, organic keyword count, and a 0-100 domain authority score. Use this first for domain research. `backlinks` and `referringDomains` are ALWAYS null on this tool — no data provider feeds them here, so a null is the absence of a source, not a domain with no links. Call get_backlinks_overview for those. CHARGES CREDITS ON THE PAID DATA PROVIDER (~100-300 typical) for the organic traffic/keyword figures. On the default free provider that endpoint has no equivalent, so `organicTraffic` and `organicKeywords` come back null, nothing is charged, and only the authority score is populated. Null there means not measured — never report it as zero traffic. Read `authoritySource` before quoting `authority`: 'openpagerank' is link-graph authority, 'tranco' is a POPULARITY rank used as a stand-in and must be labelled as such; a domain outside Tranco's top million honestly returns null. A result carrying data is cached 12 hours per domain; a result with neither organic keywords nor an authority score is deliberately not cached, so retrying it re-runs the lookup.
| Argument | Type | Description |
|---|---|---|
| domain* | string | Domain to analyze (e.g. 'example.com'). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
MisarSEO's own retained daily GA4 channel breakdown (sessionDefaultChannelGroup) for the connected property — isolating Organic Search from every other acquisition channel, the SEO-relevant signal GA4 adds beyond Search Console's own click/impression data. Read-only and FREE (no credits), archive-only — same sync/quota story as get_ga4_performance_summary.
| Argument | Type | Description |
|---|---|---|
| endDate* | string | YYYY-MM-DD, required, not before startDate. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | YYYY-MM-DD, required. |
{
"type": "object",
"required": [
"projectId",
"startDate",
"endDate"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required, not before startDate."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required."
}
}
}MisarSEO's own retained daily GA4 landing-page breakdown for the connected property: sessions/engagement/conversions per landing page, ordered by sessions descending within each day. Read-only and FREE (no credits), archive-only — same sync/quota story as get_ga4_performance_summary.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Rows to return, 1-1000 (default 1000). Landing-page cardinality is unbounded for a large site. |
| endDate* | string | YYYY-MM-DD, required, not before startDate. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | YYYY-MM-DD, required. |
{
"type": "object",
"required": [
"projectId",
"startDate",
"endDate"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 1000,
"minimum": 1,
"description": "Rows to return, 1-1000 (default 1000). Landing-page cardinality is unbounded for a large site."
},
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required, not before startDate."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required."
}
}
}MisarSEO's own retained daily Google Analytics totals for the connected property: sessions, activeUsers, newUsers, engagementRate, bounceRate, conversions, screenPageViews. Read-only and FREE (no credits) — makes NO call to Google, reads only what the nightly sync (04:25 UTC) has archived. A property with no completed sync yet simply has no rows, not stale data.
| Argument | Type | Description |
|---|---|---|
| endDate* | string | YYYY-MM-DD, required, not before startDate. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | YYYY-MM-DD, required. |
{
"type": "object",
"required": [
"projectId",
"startDate",
"endDate"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required, not before startDate."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required."
}
}
}The project's currently linked Google Business Profile location, or `{ connection: null }` when none is linked yet. FREE: database only.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Completeness/quality score and AI-grounded optimization recommendations for the project's linked Google Business Profile location. The `score` (0-100) is a DETERMINISTIC heuristic checklist over real measured facts — not AI-generated. Only `recommendations` comes from the model. CHARGES ONE AI-GATEWAY OPERATION against the organization's shared monthly ceiling.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Read-only Business Profile Performance API metrics for the project's linked location over an inclusive date range. `metrics` must be real DailyMetric values: BUSINESS_IMPRESSIONS_DESKTOP_MAPS, BUSINESS_IMPRESSIONS_DESKTOP_SEARCH, BUSINESS_IMPRESSIONS_MOBILE_MAPS, BUSINESS_IMPRESSIONS_MOBILE_SEARCH, BUSINESS_CONVERSATIONS, BUSINESS_DIRECTION_REQUESTS, CALL_CLICKS, WEBSITE_CLICKS, BUSINESS_BOOKINGS, BUSINESS_FOOD_ORDERS, BUSINESS_FOOD_MENU_CLICKS.
| Argument | Type | Description |
|---|---|---|
| endDate* | string | Inclusive end date, YYYY-MM-DD. |
| metrics* | array | Which daily metrics to fetch. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | Inclusive start date, YYYY-MM-DD. |
{
"type": "object",
"required": [
"projectId",
"metrics",
"startDate",
"endDate"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Inclusive end date, YYYY-MM-DD."
},
"metrics": {
"type": "array",
"items": {
"enum": [
"BUSINESS_IMPRESSIONS_DESKTOP_MAPS",
"BUSINESS_IMPRESSIONS_DESKTOP_SEARCH",
"BUSINESS_IMPRESSIONS_MOBILE_MAPS",
"BUSINESS_IMPRESSIONS_MOBILE_SEARCH",
"BUSINESS_CONVERSATIONS",
"BUSINESS_DIRECTION_REQUESTS",
"CALL_CLICKS",
"WEBSITE_CLICKS",
"BUSINESS_BOOKINGS",
"BUSINESS_FOOD_ORDERS",
"BUSINESS_FOOD_MENU_CLICKS"
],
"type": "string"
},
"minItems": 1,
"description": "Which daily metrics to fetch."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Inclusive start date, YYYY-MM-DD."
}
}
}Read-only Google Business Profile listing profile for the project's linked location: title, categories, phone, website, description, hours-period count, and which of Google's attribute taxonomy entries are set. Fetched LIVE from Google on every call. Returns NOT_FOUND if no location is linked yet — link one with set_gbp_connection (list_gbp_locations has the ids).
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Read-only reviews for the project's linked Google Business Profile location, one Google-paginated page at a time.
| Argument | Type | Description |
|---|---|---|
| pageToken | string | |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"pageToken": {
"type": "string"
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Fetches question-and-answer rows for one business keyword. Run this only when Q&A evidence is needed. THE TWO PROVIDERS RETURN DIFFERENT THINGS AND YOU MUST SAY WHICH YOU HAVE. On the paid data provider these are real Google Business Profile questions and answers for a profile near `near`, and it CHARGES CREDITS. On the default free provider they are Google/Bing autocomplete completions reframed as questions — real search phrasings, but NOT anybody's Business Profile Q&A, with no answers and no asker — and NOTHING IS CHARGED. Never attribute a free-provider row to a business or quote it as a customer's question. `near` IS IGNORED ON THE FREE PROVIDER: the suggest APIs take no geography, so the same keyword returns the same rows from any coordinate. It is required because the paid provider needs it, not because the result is always localised. On the paid provider `radiusKm` is converted to metres and clamped into 200 m - 199,999 m, so a radius above about 200 km is silently reduced to that ceiling. An empty result means no Q&A was found for this phrasing; whether the business has questions elsewhere was not measured.
| Argument | Type | Description |
|---|---|---|
| near* | object | Coordinate and radius to search around. |
| depth | integer | Maximum Q&A rows to fetch (1-100). Defaults to 20. |
| keyword* | string | Business name or search phrase identifying the Google Business Profile. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode |
Anonymized-query estimate: Search Console omits some rare queries entirely from a query-dimensioned response to protect searcher privacy (Google's own documented behaviour, not this codebase's assumption). This compares the SUM of named per-query clicks/impressions against the property's TRUE undimensioned total for the same window — the gap is a real, derived anonymized-query volume. Charges no credits but spends TWO Search Console requests of Google's per-property quota per call. ALWAYS narrow on `state` first: `not_connected`, `requires_reconnect` (the bound property's Google grant can no longer be used), `no_search_data` (Search Console returned nothing for the window — an absence of data, NOT zero anonymization), `analysed`. On `analysed`, ALWAYS check `possiblyRowCapped`: when true, part of the gap is queries the 1,000-row named-query fetch did not reach, not privacy anonymization — the estimate then overstates true anonymization.
| Argument | Type | Description |
|---|---|---|
| endDate | string | Explicit end date (YYYY-MM-DD). Use with startDate. |
| dateRange | string | Convenience window (default 'last_28_days'). This DOES call Google, so Search Console's usual ~16-month clamp and ~3-day data lag both apply. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate | string | Explicit start date (YYYY-MM-DD). Use with endDate. |
CTR by ranking position: the site's own historical Search Console clicks/impressions bucketed by position (1-3, 4-10, 11-20, 21+ — mirroring this codebase's existing rank-tracking position-distribution buckets), with the REAL clicks-weighted CTR this project achieved at each bucket. NEVER a fabricated industry-average CTR curve (the generic 'position 1 gets X% CTR' tables third-party vendors publish) — every figure is computed from this project's own archived Search Console history. Free and makes NO live Google call: reads only the archive `get_content_decay_report` also reads. ALWAYS narrow on `state` first: `not_connected` (connect a property), `no_data` (a property is bound but the archive has no rows at all for this dimension/window — an absence of data, NOT zero traffic), `analysed`.
| Argument | Type | Description |
|---|---|---|
| endDate | string | Explicit end date (YYYY-MM-DD). Use with startDate. |
| dateRange | string | Convenience window over MisarSEO's own archive (default 'last_28_days'). No Google clamp applies — this never calls Google. |
| dimension | string | Bucket by query or page position (default 'query'). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate | string | Explicit start date (YYYY-MM-DD). Use with endDate. |
Compares the project's own domain against up to 3 competitor domains and classifies every keyword a competitor ranks for as missing / behind / tied / ahead / unknown. The inverse of find_serp_competitors, which asks who competes for a keyword set. CHARGES CREDITS: one ranked-keywords lookup per domain, so one call costs up to 4 upstream requests (our domain plus the competitors). Each lookup is cached 12h per domain, so re-running the same comparison with a different sort or classification filter is free — do that rather than issuing a new comparison. Compares the top 1000 keywords per domain by search volume, so a large domain is compared on a slice; per-domain `coverage` reports `keywordsFetched`, `keywordsTotal`, `truncated` and `volumeFloor`. `missing` and `unknown` are NOT interchangeable: `missing` means a competitor ranks and we provably do not; `unknown` means our own slice ran out above that keyword's volume, so nothing was measured about whether we rank. Never present `unknown` as a gap. The project's own domain is read from the project record and cannot be passed in — if the project has no domain the call fails with a validation error telling the user to set one. When `unavailable` is true, no configured data provider has a ranked-keyword source and no comparison happened; the empty `rows` in that case does NOT mean the competitors rank for nothing. At most 200 rows are returned; `totalRows` is the real match count.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| sortField | string | Sort column: 'volume' (default), 'theirPosition' or 'ourPosition'. All three are measured values — there is deliberately no composite opportunity score. Nulls sort last in both directions. |
| sortOrder | string | Sort direction. Defaults to 'desc'. |
Share of Voice across a saved keyword list: the project's own domain plus up to 3 competitor domains, scored over a saved-keyword tag ("a saved keyword list" in this codebase IS a saved-keyword tag — omit tagId/tagName to mean every saved keyword in the project). Reuses the EXACT Share of Voice computation rank tracking's own Share of Voice already uses (CTR-weighted visibility, a comparative index — never a click forecast). BILLABLE-ADJACENT: one live SERP fetch per keyword in the resolved list, up to a bounded per-run cap (see `truncated`) — a saved list carries no stored positions the way a rank-tracking config does. The project's own domain is read from the project row, never accepted as a parameter. `keywordsFailed` keywords (a SERP fetch that errored) are excluded from the computation, never counted as "ranks nowhere".
| Argument | Type | Description |
|---|---|---|
| tagId | string | The saved-keyword tag ID to treat as 'the list'. Omit with tagName to mean every saved keyword in the project. |
| tagName | string | Alternative to tagId — the tag's name. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
Parent Topic and Traffic Potential for one keyword, built from real SERP overlap across a BOUNDED set (up to 5) of related keywords — not a full keyword-to-URL reverse index, which this codebase's free stack does not have. Parent Topic is the related keyword whose SERP shares the most top-10 organic URLs with the seed's. Traffic Potential is the seed's own volume plus the volume of every candidate whose own #1-ranking URL is the exact same page as the seed's — a bounded floor, never the true total traffic across every keyword that page ranks for. FREE — no paid provider or plan gate. Up to 7 upstream calls per cache miss, each independently cached (30 days for the combined result), so look keywords up one at a time. `parentTopic`/`trafficPotential` are null, not zero, when the bounded sample carries no qualifying evidence. A keyword whose SERP has no organic result throws SEO_DATA_UNAVAILABLE rather than returning zeros.
| Argument | Type | Description |
|---|---|---|
| keyword* | string | The keyword to analyze. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
Per-keyword SERP overview: a fresh live SERP snapshot for the keyword, PLUS — when this exact keyword happens to already be tracked in this project's rank tracking — its historical position trend and SERP volatility, read straight from the SAME rank-tracking services (`get_rank_keyword_history`/`get_rank_volatility_index`) already use. `tracked.isTracked: false` means this keyword is not currently tracked in any of the project's active rank-tracking configs — an honest absence, never a fabricated trend. One upstream call (the live SERP fetch, cached 12h); the rank-tracking portion is DB-only reads. `sinceDays` only affects `tracked.history` and is ignored when the keyword is not tracked.
| Argument | Type | Description |
|---|---|---|
| keyword* | string | The keyword to look up. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| sinceDays | integer | How many days of tracked history to return, 1-730 (default 365). Only meaningful when the keyword is tracked. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
Keyword Strategy Builder: a volume -> cluster -> page -> brief pipeline over 1-500 pasted keywords, composed entirely from existing pieces — the same bulk keyword lookup, the same shared-phrase clustering engine the saved-keyword Topic Clusters feature and get_topic_research both already use, a text-match against the project's own crawled pages, and a BOUNDED number of AI-gateway content-brief calls (one per cluster among the top few by measured volume, gated by the shared AI-gateway budget). `page.status: "existing"` on a cluster means the site already has a page ABOUT that topic (title/URL text-match), never a verified ranking claim — this codebase has no keyword-to-URL index. A cluster beyond the brief limit gets `brief: null, briefSkipped: true` — an honest 'not attempted', not an empty brief. Consumes the SAME keyword_lookups_per_day plan quota bulk lookups do (once per request) plus up to a handful of AI-gateway operations.
| Argument | Type | Description |
|---|---|---|
| keywords* | array | 1-500 keywords to analyze (e.g. from a bulk research run or an export). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
Finds the domains that link to 2–4 of the project's competitors but NOT to the project itself. Every row is a site that has already demonstrated it will link to something in this space, which is what makes it the most actionable backlink report there is. CHARGES CREDITS — the most expensive call in the backlinks surface: one referring-domains lookup per request, 5 of them paging the project's OWN referring domains plus 1 per competitor, so one run costs up to 9 upstream requests of 1000 domains each. Every request is cached per organization+target+page, so re-running the same intersect with a different threshold, proof filter or sort is FREE — do that rather than issuing a new run, and confirm with the user before sweeping several competitor sets. UNAVAILABLE ON THE FREE DATA PROVIDER: a backlink graph needs a proprietary web-scale crawl index, so a deployment on the free provider answers with `unavailable: true`, a reason, and no rows. `totalRows`, `provenRows` and `unprovenRows` are 0 on that response and are NOT measurements — they travel beside the flag precisely so an empty table is never read as an answer. Check `unavailable` before `rows`, and never report an unavailable run as 'no opportunities exist'. `absence` has two states that must NOT be conflated. `proven` means our own referring-domain read ruled the domain out, so "does not link to us" is a measurement. `unproven` means our read was cut off at or above that domain's authority, so it may already link to us and nothing was established — never present an `unproven` row as a confirmed opportunity. `provenRows` is the headline count and excludes them by construction. `absenceBound` says which regime applies: `complete` (our whole profile was read) or `authority-floor` with the `authorityFloor` strictly above which absence is evidence. `coverage[]` is the per-target ledger — `domainsRead`, the provider's `domainsTotal`, `requestsSpent`/`requestBudget`, `truncated`, `authorityFloor`, and a per-target `unavailable`/`unavailableReason`. A competitor cell's `found: false` only means 'does not link to them' when that competitor's coverage is not `truncated`. The project's own domain is read from the project record and cannot be passed in — if the project has no domain the call fails with a validation error telling the user to set one. `minCompetitors` is clamped to the number of competitor domains that actually resolved (duplicates and the project's own domain are dropped); compare it with `minCompetitorsRequested` before calling a run empty. At most 200 rows are returned; `totalRows` is the real match count. There is deliberately no composite opportunity score and no subdomain toggle.
Read-only access to local pack rank check configs and their latest check. With `configId`, returns that config's latest check: `status` is "found" (a real 1-based `position` within Google's local pack), "not_found" (the page parsed cleanly but the business was not among the entries seen), or "unavailable" (the scraper is disabled, blocked, or nothing could be parsed) — never a fabricated position or confidence score. Without `configId`, lists all local pack rank configs in the project. This checks Google's LOCAL PACK for a named BUSINESS, distinct from get_local_rank_grid, which checks ORGANIC rank for a DOMAIN across a geo-grid. Create a config with create_local_pack_rank_config and run a new check with run_local_pack_rank_check.
| Argument | Type | Description |
|---|---|---|
| configId | string | Local pack rank config ID. If omitted, lists all configs in the project. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"configId": {
"type": "string",
"description": "Local pack rank config ID. If omitted, lists all configs in the project."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Read-only access to geo-grid rank-tracking configs and their latest run. With `configId`, returns that config's latest run plus per-point results (position, url, and the place name each point was reverse-geocoded to for its location override — null means no override was applied). Without it, lists all grid configs in the project. Reports ORGANIC rank at each point, NEVER local-pack position — the free SERP backend does not parse a local pack. Create a config with create_local_rank_grid_config and trigger a run with run_local_rank_grid.
| Argument | Type | Description |
|---|---|---|
| configId | string | Grid config ID. If omitted, lists all grid configs in the project. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"configId": {
"type": "string",
"description": "Grid config ID. If omitted, lists all grid configs in the project."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Media Monitoring: get_unlinked_mentions_report's candidates filtered to known social/forum/news platform domains, optionally merged with the citation-platform breakdown from a Prompt Explorer scan you already ran for the same brand. Same cost profile as `get_unlinked_mentions_report`, which this calls internally — no AI-gateway spend, even when `citations` is supplied (that data is only classified here, never fetched). NOT a real-time media-monitoring product: no social-platform API polling, no forum-API crawling, no news-aggregator feed — `scopeNote` states this on every `analysed` response. ALWAYS narrow on `state` first, same as `get_unlinked_mentions_report`. `aiCitationPlatforms: null` means no `citations` were supplied — distinct from an empty array, which would mean a scan cited nothing on a known platform.
| Argument | Type | Description |
|---|---|---|
| brand* | string | Brand name or bare domain to search for as a SERP QUERY (not a URL to fetch). |
| citations | array | Citations from an ALREADY-RUN Prompt Explorer scan for this brand (map its `citations` to {url, domain}). Never fetched here — omitting it leaves `aiCitationPlatforms: null`. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code. Defaults to the project's stored market, else 'en'. |
| locationCode | integer |
One card per member project (same shape get_dashboard returns — no GSC, no backlinks, see its own description for why) plus aggregate totals. `totals.averageHealthScore` is averaged over member projects that HAVE been audited, not over every member. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| portfolioId* | string | Portfolio ID. Use list_portfolios to find it. |
{
"type": "object",
"required": [
"portfolioId"
],
"properties": {
"portfolioId": {
"type": "string",
"minLength": 1,
"description": "Portfolio ID. Use list_portfolios to find it."
}
}
}AI Overview presence trend across a tracker's keywords, oldest first — what share of tracked keywords showed Google's AI Overview in the SERP, per check run, and how that has moved over time. FREE: database only, no SEO data provider is called and no credits are spent. Reuses `rank_snapshots.serpFeatures`, already populated by the SERP-feature capture the rank checker already does — no new SERP calls. `aiOverviewPresenceRatePct` is `keywordsWithAiOverview / keywordsTotal * 100`, rounded to one decimal, and `null` (not 0) for a run where `keywordsTotal` is 0 — nothing was checked that run, which is a missing measurement, not a truthful "0% presence".
| Argument | Type | Description |
|---|---|---|
| device | string | On every free SERP source today this narrows nothing — there is no device dimension, so every reading folds to one series regardless of what is passed. Defaults to desktop. |
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| sinceDays | integer | How many days back to look, 1–730. Defaults to 365. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"device": {
"enum": [
"desktop",
"mobile"
],
"type": "string",
"description": "On every free SERP source today this narrows nothing — there is no device dimension, so every reading folds to one series regardless of what is passed. Defaults to desktop."
},
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"sinceDays": {
"type": "integer",
"maximum": 730,
"description": "How many days back to look, 1–730. Defaults to 365.",
"exclusiveMinimum": 0
}
}
}Per-run keyword-position-distribution buckets for a tracker, oldest first — how many tracked keywords sit in the top 3, 4-10, 11-20, or outside the top 20, per check run. Use it to answer 'is this domain's visibility trending up or down' without re-checking anything. FREE: database only. No SEO data provider is called and no credits are spent. Buckets are disjoint and cover every tracked keyword: `notRanking` is derived as the remainder, not fetched separately, and `top3 + top4to10 + top11to20 + notRanking` always equals the tracker's keyword count at that run. `device` is accepted but on every free SERP source today it narrows nothing — see get_rank_tracker's own note on this deployment's device support.
| Argument | Type | Description |
|---|---|---|
| device | string | Which device's positions to bucket. On every free SERP source today this narrows nothing — there is no device dimension, so every reading folds to one series regardless of what is passed. Defaults to desktop. |
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| sinceDays | integer | How many days back to look, 1–730. Defaults to 365. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"device": {
"enum": [
"desktop",
"mobile"
],
"type": "string",
"description": "Which device's positions to bucket. On every free SERP source today this narrows nothing — there is no device dimension, so every reading folds to one series regardless of what is passed. Defaults to desktop."
},
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"sinceDays": {
"type": "integer",
"maximum": 730,
"description": "How many days back to look, 1–730. Defaults to 365.",
"exclusiveMinimum": 0
}
}
}Per-keyword position series over time, oldest first — the same series the app's SERP-history chart draws for one tracked keyword. FREE: database only. No SEO data provider is called and no credits are spent. A `position` of `null` means checked but not found within that check's `searchedDepth` — a real event, not a missing check or a lost ranking. `searchedDepth: null` means the run predates that column, not that any particular depth was searched. At most one point per UTC day: a same-day re-check collapses to its latest reading.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| sinceDays | integer | How many days back to look, 1–730. Defaults to 365. |
| trackingKeywordId* | string | The tracked keyword's own row ID (from get_rank_tracker's results), not the keyword text. |
{
"type": "object",
"required": [
"projectId",
"configId",
"trackingKeywordId"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"sinceDays": {
"type": "integer",
"maximum": 730,
"description": "How many days back to look, 1–730. Defaults to 365.",
"exclusiveMinimum": 0
},
"trackingKeywordId": {
"type": "string",
"minLength": 1,
"description": "The tracked keyword's own row ID (from get_rank_tracker's results), not the keyword text."
}
}
}Newcomers and fast climbers for a tracker's OWN domain, over a window. `newlyRanking` is keywords that started ranking during the window (no earlier position, or an earlier position of null). `fastClimbers` is keywords that ranked at both ends of the window and gained at least `minGain` positions. FREE: database only. No SEO data provider is called and no credits are spent. THIS IS NOT COMPETITOR DISCOVERY. No corpus of undeclared domains exists in this codebase to discover a newcomer domain from — a rank check only ever persists positions for the target domain and the already-tracked competitor list; every other domain in a fetched SERP is read and discarded. Do not describe a `newlyRanking` entry as a 'new competitor' or 'newly discovered domain' — it is a keyword the TRACKED domain itself started ranking for, nothing about a competitor. `newlyRanking` entries always have `gain: null` — there is no earlier position to measure a gain from, and `minGain` never filters them.
| Argument | Type | Description |
|---|---|---|
| minGain | integer | Positions a keyword must have gained to count as a fast climber, 1–100. Defaults to 5. Does not affect newlyRanking, which has no earlier position to measure a gain from. |
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| sinceDays | integer | How many days back to look, 1–730. Defaults to 365. |
Read-only access to rank tracker configs and their latest results. With `trackerId`, returns the config plus the latest snapshot per keyword. Without it, lists all trackers in the project. FREE — reads stored MisarSEO state only, runs no SERP query and charges no credits. To trigger a new check, use the dashboard. A null position means THE DOMAIN WAS NOT FOUND WITHIN THE DEPTH SEARCHED. The response states that depth as a number — use THAT number, not the config's `serpDepth`, which is what was asked for and can be several times deeper than the search source reaches. Everything past the depth searched was not measured, so a null is neither a rank of zero nor proof the page stopped ranking — say 'not found in the top N' and give the N the response states. Device columns are printed only where this deployment measures desktop and mobile as separate searches; where it does not, the response says so and prints ONE position per keyword. Never report a mobile or desktop absence from a column the response did not print, and never infer one from the config's `devices` value. `previousPosition` is a roughly 7-day-old comparison point, and when no snapshot that old exists it falls back to the EARLIEST snapshot on record — so a 'was' figure can be much older than a week. Do not quote it as a week-over-week change without checking the tracker's history length.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| trackerId | string | Rank tracker config ID. If omitted, lists all rank trackers in the project. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"trackerId": {
"type": "string",
"description": "Rank tracker config ID. If omitted, lists all rank trackers in the project."
}
}
}SERP volatility index across recent runs — how much this tracker's own positions churned run over run, derived entirely from already-stored history (no new SERP calls). FREE: database only, no SEO data provider is called and no credits are spent. Modeled on the one step AccuRanker publicly discloses for their Grump Rating (sum of per-keyword differences / number of results), but CTR-weighted rather than a raw position delta — a #1-#2 swap counts far more than a #18-#19 swap. NOT a 0-10 Sensor score or a mood label (Semrush and MozCast do not publish their formulas, and both are calibrated against panels of hundreds of thousands to millions of keywords spanning the whole web). `volatilityIndex` is meaningful only relative to THIS tracker's own history over time, not as an absolute severity — do not describe a given value as 'high' or 'low' without comparing it to this same tracker's other points.
| Argument | Type | Description |
|---|---|---|
| device | string | On every free SERP source today this narrows nothing — there is no device dimension, so every reading folds to one series regardless of what is passed. Defaults to desktop. |
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| runLimit | integer | How many recent completed runs to compare consecutively, 2-26. Defaults to 12. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
Gets a report definition's name and ordered widget types. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| reportDefinitionId* | string | Report definition ID. Use list_report_definitions to find it. |
{
"type": "object",
"required": [
"projectId",
"reportDefinitionId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"reportDefinitionId": {
"type": "string",
"minLength": 1,
"description": "Report definition ID. Use list_report_definitions to find it."
}
}
}Gets a report definition's schedule, if it has one. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| reportDefinitionId* | string | Report definition ID. Use list_report_definitions to find it. |
{
"type": "object",
"required": [
"projectId",
"reportDefinitionId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"reportDefinitionId": {
"type": "string",
"minLength": 1,
"description": "Report definition ID. Use list_report_definitions to find it."
}
}
}MisarSEO's own index-coverage report for the connected property: which URLs are indexed, excluded, or errored, and why. FREE (no credits) and makes NO call to Google — unlike get_search_console_performance, this reads `gscUrlCoverage`, populated by a nightly scan built on the URL Inspection API's ~2000/day quota, so coverage fills in over days rather than in one pass. `summary` is the headline count per coverageState. `rows` is one page (default 200, max 1000) of the URLs themselves, newest-inspected first — filter to one state with `coverageState`. An empty summary/rows with ok: true means the nightly scan has not populated this property yet, not that every page is fine.
| Argument | Type | Description |
|---|---|---|
| limit | integer | |
| offset | integer | |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| coverageState | string | Filter to one state, e.g. "Submitted and indexed". Free text, matching Google's own strings verbatim. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 1000,
"minimum": 1
},
"offset": {
"type": "integer",
"minimum": 0
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"coverageState": {
"type": "string",
"minLength": 1,
"description": "Filter to one state, e.g. \"Submitted and indexed\". Free text, matching Google's own strings verbatim."
}
}
}MisarSEO's own retained HOURLY Search Console totals for the connected property — the hourly counterpart to get_search_console_performance's daily archive. FREE (no credits) and makes NO call to Google: reads MisarSEO's own Postgres. Unlike the daily archive, a wider date range does not mean more history — Google itself only offers roughly the trailing 8-10 days of hourly data, and the nightly sync only ever fetches that same window, so a startDate earlier than it simply returns nothing for the days outside it. Each row's `hour` is 0-23 in Search Console's own America/Los_Angeles reporting hour.
| Argument | Type | Description |
|---|---|---|
| endDate* | string | YYYY-MM-DD, required, not before startDate. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | YYYY-MM-DD, required. |
{
"type": "object",
"required": [
"projectId",
"startDate",
"endDate"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required, not before startDate."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required."
}
}
}Query the connected Search Console property's Search Analytics: clicks, impressions, CTR, and average position by query/page/country/device/date. First-party data — use it for what already ranks, near-ranking queries, and pages with real demand. ctr is a 0-1 fraction; position is a 1-based average; dates are Pacific Time; the last ~3 days may be incomplete. Read-only and FREE (no credits) — Search Console is first-party data and meters nothing. It does spend Google's per-property request quota. Google omits rows it will not disclose (anonymised queries) and returns at most `rowLimit` of the rest, so what it withholds is not measured rather than absent: zero rows is not zero demand, and a total summed from these rows is a floor, never the property's real total. When `hasMore` is true you have seen only part of the match set — page with `startRow` before reporting any sum or count as complete.
| Argument | Type | Description |
|---|---|---|
| type | string | Search type (default web). |
| endDate | string | Explicit end (YYYY-MM-DD, Pacific Time). Use with startDate. |
| filters | array | AND-combined filters. To get the queries for one page: [{dimension:'page',operator:'equals',expression:'https://example.com/post'}] with dimensions ['query']. |
| rowLimit | integer | Rows per call (default 1000, max 1000). GSC sorts by clicks desc and can't filter by position — filter 'striking distance' positions client-side, and paginate with startRow when hasMore is true. |
| startRow |
Search Console sitemaps.list/sitemaps.get: every sitemap submitted for the connected property and its processing status (pending, last downloaded, warning/error counts, per-content-type submitted counts), or one named sitemap. Read-only — needs only the `webmasters.readonly` scope this deployment's Search Console connection already requests. Submit/delete need the broader `webmasters` scope and are deliberately not offered. Free (no credits) but spends one call of Google's per-property quota. Omit `feedpath` to list every sitemap; pass one (a sitemap's own URL) to fetch just that one instead — either way the response is `sitemaps`, a one-entry array for `feedpath`. `lastSubmitted`/`lastDownloaded`/`warnings`/`errors` are absent, not zero, when Google has nothing to report for that field.
| Argument | Type | Description |
|---|---|---|
| feedpath | string | A specific sitemap's own URL (e.g. another entry's own `path`). Omitted lists every sitemap Search Console has on record for the property. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"feedpath": {
"type": "string",
"minLength": 1,
"description": "A specific sitemap's own URL (e.g. another entry's own `path`). Omitted lists every sitemap Search Console has on record for the property."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Progress of a site audit started by start_site_audit: `{ status, pagesCrawled, pagesTotal, lighthouseTotal, lighthouseCompleted, lighthouseFailed, currentPhase, startedAt, completedAt }`. `status` is 'running', 'completed' or 'failed' — poll until it is 'completed' or 'failed'; there is no 'complete' status, so waiting for that string never ends. It does NOT return crawled pages or individual issues. Once completed, read it with get_audit_comparison, get_audit_core_web_vitals, get_audit_https_report, get_audit_internal_links and get_audit_structured_data. FREE.
| Argument | Type | Description |
|---|---|---|
| auditId* | string | The auditId (or jobId — the same value) returned by start_site_audit. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"auditId"
],
"properties": {
"auditId": {
"type": "string",
"minLength": 1,
"description": "The auditId (or jobId — the same value) returned by start_site_audit."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Content ideas for a seed keyword, composed from three sources this codebase already fetches for other features: the SERP for the seed (titles of pages already ranking for it), PAA-style questions (real autocomplete completions reframed as questions), and autocomplete keyword suggestions. Grouped into topic clusters by shared phrasing, using the same phrase-clustering engine the saved-keyword Topic Clusters feature uses. FREE — none of the three sources is billable in this codebase today. Three real upstream calls per request, so it is rate limited. NOT a Keywords-Explorer-style overlap report: this codebase has no keyword-to-URL index, so `clusters` group the REAL text of three OBSERVED sources rather than a fabricated 'what else these pages rank for'. `coverage.serpUnavailable`/`suggestionsUnavailable` mean that source could not be asked at all — distinct from that source genuinely returning nothing — and one source being unavailable never sinks the other two. Every idea not assigned to a cluster is still returned in `ungrouped`, never discarded.
| Argument | Type | Description |
|---|---|---|
| keyword* | string | The seed keyword to research. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code. Defaults to the project's stored market, else 'en'. |
| locationCode | integer | Numeric location code. Defaults to the project's stored market, else 2840 (United States). |
Observed history for one tracked prompt — one point per real scheduled check (`mentionedByCount`/`citedByCount` out of `successfulModelCount`), oldest first. Empty until checks accumulate; never interpolated or back-filled. FREE — reads recorded checks.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| trackedPromptId* | string | Tracked prompt ID, from list_tracked_prompts. |
{
"type": "object",
"required": [
"projectId",
"trackedPromptId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"trackedPromptId": {
"type": "string",
"minLength": 1,
"description": "Tracked prompt ID, from list_tracked_prompts."
}
}
}Unlinked brand mentions: searches the free SERP backend for `brand` as a QUERY, then checks whether each result page already links to `trackedDomain`. Free: one SERP fetch plus up to 10 live third-party page fetches, no AI-gateway spend. ALWAYS narrow on `state` first: `serp_unavailable` means no free SERP provider could be reached — never treat it as "zero mentions found". `analysed` carries `candidates[]`, each `status: "linked" | "unlinked" | "unreachable"`. Bounded by SERP result count (the first page or two of free-SERP results), never an exhaustive web crawl or a full content index — the Common Crawl-based version of this gap is a decided wont-fix; `scopeNote` states this on every response. `candidates[].platform` reuses the AI-citation platform classification (YouTube/TikTok/Reddit/Wikipedia/LinkedIn/Quora, or null) at no extra cost.
| Argument | Type | Description |
|---|---|---|
| brand* | string | Brand name or bare domain to search for as a SERP QUERY (not a URL to fetch). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code. Defaults to the project's stored market, else 'en'. |
| locationCode | integer | Numeric location code. Defaults to the project's stored market, else 2840 (United States). |
| trackedDomain* | string |
Plan tier, credits remaining, and every quota/count dimension tracked against it for the caller's organization. FREE: reads counters this codebase already maintains for its own gating — nothing here calls a paid provider or spends a credit. `gatingActive` is false on a self-hosted deployment with no billing provider configured: `tier`, `creditsRemaining` and `quotas` are all null there, not a fabricated tier. `counts` (projects/rank-tracked keywords/API keys) is always populated. Inside a quota dimension, `used: null` means NOT TRACKED — an unlimited tier's counter is never incremented in the first place — never a real zero; `used: 0` IS a real measured zero. `aiGatewayOps` is the shared platform AI-gateway key's own monthly ceiling, reported separately because it is a platform-abuse ceiling, not a plan entitlement.
No arguments.
{
"type": "object",
"properties": {}
}Device/browser/OS breakdown for a project's own tracking snippet. Every field is derived server-side from the visitor's User-Agent header at collection time. FREE, first-party.
| Argument | Type | Description |
|---|---|---|
| endDate* | string | YYYY-MM-DD, required, not before startDate. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | YYYY-MM-DD, required. |
{
"type": "object",
"required": [
"projectId",
"startDate",
"endDate"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required, not before startDate."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required."
}
}
}Referrer/channel/UTM breakdown for a project's own tracking snippet, including AI referral attribution: aiReferral.bySource breaks the ai-referral channel out by assistant (chatgpt/claude/perplexity/gemini/copilot/you/poe). aiReferral.undercountNote is not decoration — industry measurement puts a large share of real AI-assistant referrals arriving with no referrer header at all, landing in the direct channel instead. This number is a measurable floor, never a complete count. FREE, first-party.
| Argument | Type | Description |
|---|---|---|
| endDate* | string | YYYY-MM-DD, required, not before startDate. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | YYYY-MM-DD, required. |
| referrerLimit | integer | Rows in topReferrers, 1-200, default 50. |
{
"type": "object",
"required": [
"projectId",
"startDate",
"endDate"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required, not before startDate."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required."
},
"referrerLimit": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Rows in topReferrers, 1-200, default 50."
}
}
}Sessions/pageviews/bounce-rate time series for a project's own tracking snippet. FREE — first-party data collected by MisarSEO's own snippet (/api/analytics/snippet.js), never a paid or rate-limited third-party call. `totals.bounceRate` is null (not 0) when there were no sessions in the range.
| Argument | Type | Description |
|---|---|---|
| endDate* | string | YYYY-MM-DD, required, not before startDate. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | YYYY-MM-DD, required. |
{
"type": "object",
"required": [
"projectId",
"startDate",
"endDate"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required, not before startDate."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required."
}
}
}Top pages by view count, plus separate entry-page and exit-page breakdowns, for a project's own tracking snippet. FREE, first-party.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Rows per list (topPages/entryPages/exitPages), 1-200, default 50. |
| endDate* | string | YYYY-MM-DD, required, not before startDate. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate* | string | YYYY-MM-DD, required. |
{
"type": "object",
"required": [
"projectId",
"startDate",
"endDate"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Rows per list (topPages/entryPages/exitPages), 1-200, default 50."
},
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required, not before startDate."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD, required."
}
}
}Yandex's own view of a project's domain: the indexing summary — including the Site Quality Index (SQI), a third independent authority signal alongside OpenPageRank and Tranco — and popular search queries, read straight from the Yandex Webmaster API. FREE (no credits). Bring-your-own-token: requires the deployment operator to set YANDEX_WEBMASTER_OAUTH_TOKEN (one token covers every host verified on that Yandex account — no per-project connect step, and MisarSEO never drives the OAuth flow itself). Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_host' when the project's domain has no verified match on the account. Yandex publishes no backlink-lookup endpoint at all, so unlike get_bing_site_overview this has no backlinks section. `diagnostics` lists individually named issues (e.g. NO_SITEMAPS, DNS_ERROR) that `summary.siteProblems` only counts by severity.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| queryLimit | integer | Max popular queries to return (1-500). |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"queryLimit": {
"type": "integer",
"maximum": 500,
"minimum": 1,
"description": "Max popular queries to return (1-500)."
}
}
}Link Building Tool: bulk-add up to 500 prospects at once from already-parsed rows (each with at least a domain). FREE (no credits), but this is a MUTATION. One bad or duplicate row never aborts the rest — the response reports added/duplicates/errors per row.
| Argument | Type | Description |
|---|---|---|
| rows* | array | Rows to import, each with at least a domain. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"rows"
],
"properties": {
"rows": {
"type": "array",
"items": {
"type": "object",
"required": [
"domain"
],
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"label": {
"type": "string",
"maxLength": 200
},
"domain": {
"type": "string",
"maxLength": 253,
"minLength": 1
},
"targetUrl": {
"type": "string",
"format": "uri"
},
"contactName": {
"type": "string",
"maxLength": 300
},
"contactEmail": {
"type": "string",
"maxLength": 300
},
"contactNotes": {
"type": "string",
"maxLength": 1000
}
}
},
"maxItems": 500,
"minItems": 1,
"description": "Rows to import, each with at least a domain."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Run Google Search Console's URL Inspection on up to 10 URLs of the connected property: index/coverage state, last crawl time, Google-selected vs declared canonical, and mobile/rich-results verdicts. Use it to answer 'is this page indexed? why not?'. Read-only and FREE (no credits) — Search Console is first-party data and meters nothing. Every submitted URL comes back as a row, but a row carrying `error` was NOT inspected: Google refused or failed on it, so its index state was not measured rather than found to be bad. Count the rows without an `error` when saying how many URLs you checked. A verdict of UNKNOWN is Google's own 'no determination', not a page that is missing from the index.
| Argument | Type | Description |
|---|---|---|
| urls* | array | 1–10 absolute URLs to inspect. Each must belong to the connected property. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | BCP-47 language for the inspection result (e.g. 'en-US'). |
{
"type": "object",
"required": [
"projectId",
"urls"
],
"properties": {
"urls": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"maxItems": 10,
"minItems": 1,
"description": "1–10 absolute URLs to inspect. Each must belong to the connected property."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"description": "BCP-47 language for the inspection result (e.g. 'en-US')."
}
}
}Every Rank Tracker AND Site Audit project the caller's connected Ahrefs Connect grant can access — the data source for picking which project(s) to bind to a MisarSEO project (see set_ahrefs_project). Live call to the Ahrefs API v3 under the caller's OWN OAuth token. Read-only and FREE (no credits). Check `requiresReconnect` before treating both lists as 'no projects': true means the stored grant can no longer reach Ahrefs.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Every site the caller's connected Bing Webmaster OAuth grant can access — the data source for picking which site to bind to a project (see set_bing_site). Live call to the Bing Webmaster API (GetUserSites) under the caller's OWN OAuth token, not the operator key. Read-only and FREE (no credits). Check `requiresReconnect` before treating an empty `sites` list as 'no sites': true means the stored grant can no longer reach Bing Webmaster.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Lists a project's brand monitors — targets re-scanned by Brand Radar on a schedule rather than only when run_brand_radar_scan is called. FREE: database only, no credits spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Competitor page change detection: the specific competitor URLs monitored for a project, with `lastCheckedAt` (null until the first scheduled check reaches it). FREE — database only, no credits spent. Use get_competitor_monitor_changes for the actual detected-change timeline of one URL.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Disavow file management: every disavow entry for a project, oldest first. FREE (no credits). Use export_disavow_file to get the actual Google-format .txt content.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Every GA4 property the caller's connected Google Analytics grant can access — the data source for picking which property to bind to a project (see set_ga4_property). Live call to the Analytics Admin API (accountSummaries.list), not an archive read. Read-only and FREE (no credits). Check `requiresReconnect` before treating an empty `properties` list as 'no properties': true means the stored grant can no longer reach Google Analytics.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Every Google Business Profile account and location this deployment's operator credential can manage — the picker data for set_gbp_connection. Not project-scoped data (the answer does not depend on the project passed; projectId only satisfies the ownership check). Returns `{ accounts: [{ name, accountName, locations: [...] }] }`. Live Google API calls.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Posts published on the project's linked Google Business Profile location. A 404 means no location is linked — use set_gbp_connection first.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Link Building Tool: a prospect's activity log — every manually-typed note plus every auto-recorded status change, newest first. FREE (no credits).
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| prospectId* | string | Prospect id, from list_link_prospects. |
{
"type": "object",
"required": [
"projectId",
"prospectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"prospectId": {
"type": "string",
"minLength": 1,
"description": "Prospect id, from list_link_prospects."
}
}
}Link Building Tool: every prospect for a project, newest first, each enriched with a LIVE domain-authority read (never cached, never an invented score). FREE (no credits). White-hat scope: research + outreach assistance only.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Patches: every issue type this project auto-publishes a freshly AI-drafted patch for, immediately on draft (capped at 20 auto-publishes per project per day). FREE (no credits), read-only.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Patches: every title/metaDescription/canonical/redirect fix drafted for a project, newest first, one active row per (page, field). FREE (no credits), read-only.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Lists the caller organization's portfolios, each carrying `projectCount`. FREE: database only, no SEO data provider is called and no credits are spent.
No arguments.
{
"type": "object",
"properties": {}
}Lists the active projects in the user's organization. FREE — database only, charges no credits. Use this whenever you need a `projectId` for another MisarSEO tool. Returns `{id, name, domain, url}` per project; pass the `id` value as `projectId`. `domain` is null for a project that has never had one set, which is not a missing lookup — get_keyword_gap and get_link_intersect read the project's own domain from this record and refuse to run without it. Archived projects are not listed.
No arguments.
{
"type": "object",
"properties": {}
}Chart notes on a rank tracker's own timeline — 'deployed new title tags', 'algorithm update' — oldest first. FREE: database only, no SEO data provider is called and no credits are spent. Config-level, not per-keyword or per-device: a deploy or algorithm change is a fact about the date, not about any one reading.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Tracked keywords whose latest ranking URL is not the target URL you declared for them (see set_rank_keyword_target_url). FREE: database only, no SEO data provider is called and no credits are spent. NOT the same detector as get_cannibalisation_report (Search Console-based), which finds two-or-more of the site's OWN pages splitting Search Console impressions for one query. A rank check records only the single best-ranking URL per keyword per run, so this can only tell you the winning page was not the one you intended — not that a second own page also appeared in the SERP.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Lists the tags in use on one rank tracker, plus the project's whole tag vocabulary. Use it to find tag IDs before calling update_rank_keyword_tag, and to see how a tracker with hundreds of keywords is already organised. FREE: database only. No SEO data provider is called and no credits are spent, so it is safe to call as often as needed. The two lists mean different things and are deliberately separate. `tagsInUse` is what THIS tracker's keywords actually carry, and its `keywordCount` counts TRACKED keywords carrying the tag — not the saved-keyword count the same tag carries elsewhere, which would never match the rows it filters. It is empty for a tracker that has never used a tag. `projectTags` is the project's full vocabulary including tags unused here, so a tag can be reused rather than re-coined. The vocabulary is the project's EXISTING saved-keyword tags: one namespace shared with the saved-keyword list, not a second set.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Lists the keywords tracked by ONE rank tracker: `{ keywords, totalCount, page, pageSize }`, each row `{ id, configId, keyword, searchVolume, keywordDifficulty, cpc, metricsFetchedAt, createdAt }`. It returns NO positions and runs no check — read positions with get_rank_tracker. The row `id` is the tracking-keyword ID that remove_rank_tracking_keywords, update_rank_keyword_tag and set_rank_keyword_target_url take. FREE: database only.
| Argument | Type | Description |
|---|---|---|
| page | integer | 1-based page number. Defaults to 1. |
| configId* | string | Rank tracker config ID. |
| pageSize | integer | Rows per page. Defaults to 50. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"page": {
"type": "integer",
"minimum": 1,
"description": "1-based page number. Defaults to 1."
},
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID."
},
"pageSize": {
"enum": [
50,
100,
250
],
"type": "integer",
"description": "Rows per page. Defaults to 50."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Lists a project's report definitions, each carrying `widgetCount`. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Lists keywords saved to a project, with whatever metrics have been cached for them (search volume, difficulty, CPC) and their tags. FREE — reads MisarSEO's own database, runs no provider lookup and charges no credits. Filter by keyword text with `search`; page through with `page` and `limit` (50, 100 or 250 rows per page — any other value is rounded down to one of those), ordered by `sort`/`order`. A metric is null when nothing has ever fetched it for that keyword — saving a keyword does not measure it. Null is not zero volume and not zero difficulty; run research_keywords to fill them. `totalCount` is how many keywords match the filter, and `rows` is only the current page of them, so report both.
| Argument | Type | Description |
|---|---|---|
| page | integer | 1-based page number (default 1). |
| sort | string | Sort column (default createdAt). |
| limit | any | Maximum rows to return. Defaults to 100. |
| order | string | Sort direction (default desc). |
| search | string | Optional keyword text filter. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
Lists a project's tracked prompts — prompts re-run through explore_ai_prompt's real answer pipeline on a schedule and diffed against the previous check for mention/citation drops. FREE: database only, no credits spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Every host the caller's connected Yandex Webmaster OAuth grant can access — the data source for picking which host to bind to a project (see set_yandex_host). Read-only and FREE (no credits). Check `requiresReconnect` before treating an empty `hosts` list as 'no hosts': true means the stored grant can no longer reach Yandex Webmaster.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Bot hit counts per (day, bot category, bot label, status code, content-type bucket) across every log upload in the date range — powers a time-series, status-code, or content-type view of bot traffic. Rows are `{ day, botCategory, botLabel, statusCode, contentType, hitCount }`; botCategory is ai_crawler/search_engine/other_bot. Dates are ISO (YYYY-MM-DD), UTC. An empty result means nothing was measured in the range — no bot hits were recorded from the logs uploaded for those days, which is not the same as no bots visiting. FREE (no credits).
| Argument | Type | Description |
|---|---|---|
| sinceDay* | string | Inclusive start day, ISO date, UTC. |
| untilDay* | string | Inclusive end day, ISO date, UTC. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"sinceDay",
"untilDay"
],
"properties": {
"sinceDay": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Inclusive start day, ISO date, UTC."
},
"untilDay": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Inclusive end day, ISO date, UTC."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}A project's server-log uploads and their parse status — the Log File Analyzer / Bot Analytics feature. Each row is `{ id, fileName, sizeBytes, status, lineCount, parsedLineCount, skippedLineCount, errorMessage, createdAt, completedAt }`. `status` is one of queued/parsing/completed/failed. lineCount/parsedLineCount/skippedLineCount are null until parsing completes — an absent count is not zero. FREE (no credits). Pair with log_analyzer_upload to add a log, log_analyzer_daily_summary / log_analyzer_top_paths for the report.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}The paths bots hit most, per (bot category, bot label, path, status code) — the busiest-paths table of Bot Analytics. Rows are `{ botCategory, botLabel, path, statusCode, hitCount, lastSeenAt }`, ordered by hitCount descending, capped at 200. Optionally filter to one bot category (ai_crawler/search_engine/other_bot). An empty result means nothing was measured — no bot path hits were recorded from uploaded logs. FREE (no credits).
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| botCategory | string | Filter to one bot category; omit for all. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"botCategory": {
"enum": [
"ai_crawler",
"search_engine",
"other_bot"
],
"type": "string",
"description": "Filter to one bot category; omit for all."
}
}
}Upload a server access log for background parsing (Log File Analyzer / Bot Analytics). Only Apache/Nginx Combined Log Format text is supported. Pass the raw log text in `content`; parsing runs in the background, so this returns once the upload is stored, before it is fully processed — poll log_analyzer_list_uploads for status. Requires the `admin` role. Plan-tier gated: the deployment's tier caps lines processed per upload and uploads per month. Practical limit: tool calls carrying megabytes of text are impractical — prefer the web UI for large logs. FREE (no credits).
| Argument | Type | Description |
|---|---|---|
| content* | string | Raw Combined Log Format text, one line per request. |
| fileName* | string | The log file's name, e.g. 'access.log'. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"fileName",
"content"
],
"properties": {
"content": {
"type": "string",
"minLength": 1,
"description": "Raw Combined Log Format text, one line per request."
},
"fileName": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "The log file's name, e.g. 'access.log'."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Patches: makes a drafted fix live — visible in the project's public manifest, which the embedded snippet (or, for redirects, an operator-deployed Worker) reads from the visitor's browser. The audited site's own origin content is never touched. FREE (no credits), but this is a MUTATION. Requires an org admin.
| Argument | Type | Description |
|---|---|---|
| patchId* | string | The patch id, from list_patches or draft_patch. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"patchId"
],
"properties": {
"patchId": {
"type": "string",
"minLength": 1,
"description": "The patch id, from list_patches or draft_patch."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Asks Yandex Webmaster to re-crawl one specific URL on a project's matched host. FREE (no credits), but this is a MUTATION — unlike get_yandex_site_overview, it queues real work on the operator's live Yandex Webmaster account and spends that account's daily recrawl quota. Bring-your-own-token: requires YANDEX_WEBMASTER_OAUTH_TOKEN. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_host' when the project's domain has no verified match on the account. Yandex's own documented failure cases — the URL already queued, or the account's recrawl quota already spent — surface as a normal tool error, not a 200 with ok:false.
| Argument | Type | Description |
|---|---|---|
| url* | string | Absolute URL to re-crawl. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "Absolute URL to re-crawl."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Stops monitoring one competitor URL. Its historical snapshots and detected changes are deleted along with it (cascading delete) — unlike rank tracking's competitor removal, there is no separate archive kept.
| Argument | Type | Description |
|---|---|---|
| targetId* | string | Monitored URL's target ID, from list_competitor_monitors or add_competitor_monitor. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"targetId"
],
"properties": {
"targetId": {
"type": "string",
"minLength": 1,
"description": "Monitored URL's target ID, from list_competitor_monitors or add_competitor_monitor."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Disavow file management: remove one disavow entry by id (from list_disavow_entries). FREE (no credits), but this is a MUTATION. Succeeds whether or not entryId existed.
| Argument | Type | Description |
|---|---|---|
| entryId* | string | Entry id, from list_disavow_entries. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"entryId"
],
"properties": {
"entryId": {
"type": "string",
"minLength": 1,
"description": "Entry id, from list_disavow_entries."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Link Building Tool: remove one prospect by id (from list_link_prospects), along with its activity log. FREE (no credits), but this is a MUTATION. Succeeds whether or not prospectId existed.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| prospectId* | string | Prospect id, from list_link_prospects. |
{
"type": "object",
"required": [
"projectId",
"prospectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"prospectId": {
"type": "string",
"minLength": 1,
"description": "Prospect id, from list_link_prospects."
}
}
}Removes projects from a portfolio. Succeeds whether or not an id was actually a member. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectIds* | array | MisarSEO project IDs. Use list_projects to find them. |
| portfolioId* | string | Portfolio ID. Use list_portfolios to find it. |
{
"type": "object",
"required": [
"portfolioId",
"projectIds"
],
"properties": {
"projectIds": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 100,
"minItems": 1,
"description": "MisarSEO project IDs. Use list_projects to find them."
},
"portfolioId": {
"type": "string",
"minLength": 1,
"description": "Portfolio ID. Use list_portfolios to find it."
}
}
}Removes a chart note from a rank tracker's own timeline. FREE: database only, no SEO data provider is called and no credits are spent. Succeeds whether or not `id` matched an existing note.
| Argument | Type | Description |
|---|---|---|
| id* | string | Note ID, from list_rank_annotations. |
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId",
"id"
],
"properties": {
"id": {
"type": "string",
"minLength": 1,
"description": "Note ID, from list_rank_annotations."
},
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Removes keywords from a rank tracker by their tracking-keyword IDs (from list_rank_tracking_keywords). The keywords stop being checked; their past position snapshots are deliberately kept, so history survives. FREE: database only.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Rank tracker config ID. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| keywordIds* | array | Tracking-keyword IDs to remove. |
{
"type": "object",
"required": [
"projectId",
"configId",
"keywordIds"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"keywordIds": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 1000,
"minItems": 1,
"description": "Tracking-keyword IDs to remove."
}
}
}Renames a portfolio. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| name* | string | Portfolio name, 1-100 characters. |
| portfolioId* | string | Portfolio ID. Use list_portfolios to find it. |
{
"type": "object",
"required": [
"portfolioId",
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Portfolio name, 1-100 characters."
},
"portfolioId": {
"type": "string",
"minLength": 1,
"description": "Portfolio ID. Use list_portfolios to find it."
}
}
}Renames a report definition. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| name* | string | Report name, 1-100 characters. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| reportDefinitionId* | string | Report definition ID. Use list_report_definitions to find it. |
{
"type": "object",
"required": [
"projectId",
"reportDefinitionId",
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Report name, 1-100 characters."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"reportDefinitionId": {
"type": "string",
"minLength": 1,
"description": "Report definition ID. Use list_report_definitions to find it."
}
}
}Renders a report definition — fetches ONLY its selected widgets, not a fixed set. FREE: every widget is a local Postgres read, no upstream call, so this never spends a paid-provider credit or a live Search Console quota unit. `rank_volatility` and `share_of_voice` read the project's FIRST active rank tracker only; `rank_movers` sums across every active tracker.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| periodDays | any | 7, 28 or 90 days. Defaults to 28. |
| reportDefinitionId* | string | Report definition ID. Use list_report_definitions to find it. |
{
"type": "object",
"required": [
"projectId",
"reportDefinitionId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"periodDays": {
"anyOf": [
{
"type": "number",
"const": 7
},
{
"type": "number",
"const": 28
},
{
"type": "number",
"const": 90
}
],
"description": "7, 28 or 90 days. Defaults to 28."
},
"reportDefinitionId": {
"type": "string",
"minLength": 1,
"description": "Report definition ID. Use list_report_definitions to find it."
}
}
}Reply to (or overwrite the reply on) one review, by reviewId from get_gbp_reviews. Google's endpoint has no separate create/update — this always overwrites. Publishes immediately; there is no draft/preview step.
| Argument | Type | Description |
|---|---|---|
| comment* | string | |
| reviewId* | string | |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"reviewId",
"comment"
],
"properties": {
"comment": {
"type": "string",
"maxLength": 4096,
"minLength": 1
},
"reviewId": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Turns a seed topic into candidate prompts worth tracking (real Google/Bing autocomplete completions reframed as questions) plus observed mention-rate signals from this project's own already-tracked prompts. This is NOT prompt search volume — no honest corpus of 'how many people ask this' exists anywhere this codebase can reach, so no volume or difficulty number is invented. FREE: no AI-gateway budget, no paid-plan gate, no credits spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| seedTopic* | string | Topic to derive trackable prompt candidates from. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
{
"type": "object",
"required": [
"projectId",
"seedTopic"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"seedTopic": {
"type": "string",
"minLength": 1,
"description": "Topic to derive trackable prompt candidates from."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
}
}
}Research keyword data (search volume, difficulty, CPC, related ideas) for 1-5 seed keywords in one call — one research run per seed. Returns `results`, one entry per seed with `ok` — a single bad seed won't fail the batch. CHARGES CREDITS ON THE PAID DATA PROVIDER: per seed, ~30-100 credits each depending on the source, or a flat ~96 for countries served from Google Ads data (where difficulty and intent are unavailable). On the default free provider the sources are autocomplete plus the operator's own Keyword Planner account and NOTHING IS CHARGED — but volume/CPC/competition are null unless the operator configured a Keyword Planner, and null there means not measured, never zero demand. Keyword difficulty is derived for the SEED keyword only — every other returned row has a null difficulty because it was never scored, not because it is easy. Each seed returns `rowCount` (every row found) but only `topRows`, the first 20, are included in the response; say 20 when reporting what you actually saw, and use list_saved_keywords or the dashboard for the rest.
| Argument | Type | Description |
|---|---|---|
| seeds* | array | 1-5 seed keywords. Each seed is researched independently and returns related keywords with volume/difficulty/CPC. Bulk-friendly — prefer this over multiple single-seed calls. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| resultLimit | any | Cap on the data provider's own rows per seed. Defaults to 150. It is NOT a cap on `rowCount`: free Google Suggest ideas are appended after the cap is applied, so a seed can legitimately report more rows than this number. |
| includeClickstreamData | boolean |
Patches: stops serving a published fix — instant, since publishing only ever controlled visibility in the public manifest, never the audited site's own origin content. FREE (no credits), but this is a MUTATION. Requires an org admin.
| Argument | Type | Description |
|---|---|---|
| patchId* | string | The patch id, from list_patches. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"patchId"
],
"properties": {
"patchId": {
"type": "string",
"minLength": 1,
"description": "The patch id, from list_patches."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Scans real AI-answer engines and search citations for brand mentions, Share of Voice against named competitors, and cited sources. REQUIRES A PAID PLAN — a free-tier organization gets a plan-locked error, not an empty result — and CONSUMES ONE of the organization's monthly Brand Radar scan allowance PLUS one shared AI-gateway operation, both charged BEFORE the scan runs. The allowance is spent when the scan is ACCEPTED, before any answer exists — every call submits a new scan, so ask before re-running one. The scan runs on a background queue: this tool waits up to ~45 seconds for it and returns the result when it completes; if it is still running it returns the `jobId` instead — collect the result with get_brand_radar_scan_status (free to poll) rather than calling this tool again, which would spend another scan. Mentions and AI search volumes are nullable: a null is 'not reported by the platform', never 0.
| Argument | Type | Description |
|---|---|---|
| brand* | string | Brand, domain, or product name to scan. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| competitors | array | Up to 5 competitor brands/domains to compare Share of Voice against. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
Compare a claimed business Name/Address/Phone against listings the free OpenStreetMap business search finds near the claimed location. PARTIAL scope: checks ONE open source, not a multi-directory citation crawl (a multi-directory crawl needs licensed directory-API access this deployment does not have). A found field with no value is reported notComparable, never scored as a mismatch. `overallScore` on each match is a mean of comparable-field similarities (0-1), null when nothing was comparable.
| Argument | Type | Description |
|---|---|---|
| name* | string | Claimed business name. |
| phone* | string | Claimed phone number, any format. |
| address* | string | Claimed address, any format. |
| latitude* | number | |
| radiusKm* | number | Search radius around the claimed location, in km. |
| longitude* | number | |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
Runs a local pack rank check now — one live scrape of Google's public local search results (not the paid Places API). Returns "found" with a real 1-based position, "not_found" (parsed cleanly, business absent), or "unavailable" (scraper disabled, blocked, or unparsable) — never a fabricated position. Fails with SEO_DATA_UNAVAILABLE where the deployment has not enabled the scraper. Tightly rate limited, so do not loop it.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Config ID, from the matching get_* tool called without configId. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Config ID, from the matching get_* tool called without configId."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Runs a geo-grid rank check now — up to 9 live SERP calls plus up to 9 reverse-geocode calls. Reports ORGANIC rank at each grid point, never local-pack position. Fails with SEO_DATA_UNAVAILABLE where the SERP provider cannot override location per point. Tightly rate limited, so do not loop it.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Config ID, from the matching get_* tool called without configId. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Config ID, from the matching get_* tool called without configId."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}SaveCrawlSettings: overwrites a project's matched Bing site's crawl-rate/AJAX-crawling config. FREE (no credits), but this is a MUTATION — unlike get_bing_site_overview's GetCrawlSettings read, it writes to the operator's live Bing Webmaster account. crawlRate must have exactly 24 hourly values, per Bing's own documented CrawlSettings JSON sample. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| crawlRate* | array | Exactly 24 hourly crawl-rate values, one per hour of the day. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| ajaxEnabled* | boolean | Whether Bing should crawl AJAX content on this site. |
{
"type": "object",
"required": [
"projectId",
"ajaxEnabled",
"crawlRate"
],
"properties": {
"crawlRate": {
"type": "array",
"items": {
"type": "number"
},
"maxItems": 24,
"minItems": 24,
"description": "Exactly 24 hourly crawl-rate values, one per hour of the day."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"ajaxEnabled": {
"type": "boolean",
"description": "Whether Bing should crawl AJAX content on this site."
}
}
}Save keywords to a project's saved-keywords list. FREE — database only, charges no credits. Idempotent: re-saving an existing keyword is a no-op. If tags are provided, missing tags may be created. Tags are appended to whatever tags a keyword already carries. Ask the user for confirmation before applying or replacing tags broadly. Keywords are normalised and de-duplicated before they are written, so `savedCount` is the number of DISTINCT keywords now saved by this call — it can be lower than the number of entries submitted, and that is the figure to report. Saving does not fetch metrics: a keyword saved here has null volume/difficulty/CPC until research_keywords has run for it.
| Argument | Type | Description |
|---|---|---|
| tags | array | Optional tags to attach to every saved keyword. Ask the user for explicit confirmation before using this, especially when saving many keywords or creating new tag names. |
| keywords* | array | Keywords to save (1-100). |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
Searches local business listings near a coordinate. Use this to find local business candidates or nearby competitors; it does not run Maps rank checks or Q&A. CHARGES CREDITS ONLY WHEN APIFY IS CONFIGURED AND ITS SHARED MONTHLY BUDGET HAS ROOM: results then come from a real, independently-run scrape of Google Places (`dataSource: "apify_google_places"`) — real ratings, since OpenStreetMap never has one. Otherwise (Apify unconfigured, over budget, or the actor call failed) NOTHING IS CHARGED and this falls back automatically to OpenStreetMap (Overpass, then Nominatim) — real point-of-interest data, no key (`dataSource: "openstreetmap"`) — whose coverage is uneven and carries none of a commercial directory's ranking, review or category metadata. Do not present an OpenStreetMap result set as the complete set of businesses in the area; the response's own `dataSource` field on each row says which source actually answered it, never assume. `categories` values are common category names (e.g. 'restaurant', 'dentist'); OpenStreetMap matches them on a best-effort basis, so a category that returns nothing on that path may simply be unmapped there rather than absent from the area — an empty result is not measured coverage of the area, and a listing's missing rating or review count is likewise not measured, never zero.
| Argument | Type | Description |
|---|---|---|
| near* | object | Coordinate and radius to search around. |
| limit | integer | Maximum businesses to return (1-50). Defaults to 20. |
| query | string | Business name or title text to match. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
Bind (or repoint) ONE of a project's two independent Ahrefs project types — `projectType` selects `rank_tracker` or `site_audit`. `ahrefsProjectId` is re-validated against the caller's LIVE project list on every call — fails if the project fell out of the connected account's access since it was last listed via list_ahrefs_projects.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| projectType* | string | Which of the two independent Ahrefs project types to bind. |
| ahrefsProjectId* | string | Ahrefs' own project id, taken verbatim from list_ahrefs_projects. |
{
"type": "object",
"required": [
"projectId",
"projectType",
"ahrefsProjectId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"projectType": {
"enum": [
"rank_tracker",
"site_audit"
],
"type": "string",
"description": "Which of the two independent Ahrefs project types to bind."
},
"ahrefsProjectId": {
"type": "string",
"minLength": 1,
"description": "Ahrefs' own project id, taken verbatim from list_ahrefs_projects."
}
}
}Set (or replace) a project's Baidu Webmaster site + push token. Baidu exposes no API to list or verify sites on an account, unlike Bing's GetUserSites — this stores the pair as given, with no live re-validation against Baidu. Requires the operator/user to have already verified the site on ziyuan.baidu.com and minted a push token there (Resource Submission -> Standard Inclusion -> API Submission).
| Argument | Type | Description |
|---|---|---|
| token* | string | The site-specific push token minted on ziyuan.baidu.com AFTER completing Baidu's own site-ownership verification there — a real, external, one-time setup step this tool cannot automate. |
| siteUrl* | string | Baidu's own `site` value, taken verbatim from ziyuan.baidu.com. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"siteUrl",
"token"
],
"properties": {
"token": {
"type": "string",
"maxLength": 128,
"minLength": 6,
"description": "The site-specific push token minted on ziyuan.baidu.com AFTER completing Baidu's own site-ownership verification there — a real, external, one-time setup step this tool cannot automate."
},
"siteUrl": {
"type": "string",
"minLength": 1,
"description": "Baidu's own `site` value, taken verbatim from ziyuan.baidu.com."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Link (or repoint) a project's Bing Webmaster site. `siteUrl` is re-validated against the caller's LIVE site list on every call — fails if the site fell out of the connected account's access since it was last listed via list_bing_sites.
| Argument | Type | Description |
|---|---|---|
| siteUrl* | string | Bing's own site URL string, taken verbatim from list_bing_sites. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"siteUrl"
],
"properties": {
"siteUrl": {
"type": "string",
"minLength": 1,
"description": "Bing's own site URL string, taken verbatim from list_bing_sites."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Link (or repoint) a project's Google Analytics property. `propertyId` is re-validated against the caller's LIVE property list on every call — fails if the property fell out of the connected account's access since it was last listed via list_ga4_properties.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| propertyId* | string | Bare numeric GA4 property id (no 'properties/' prefix), taken verbatim from list_ga4_properties. |
{
"type": "object",
"required": [
"projectId",
"propertyId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"propertyId": {
"type": "string",
"minLength": 1,
"description": "Bare numeric GA4 property id (no 'properties/' prefix), taken verbatim from list_ga4_properties."
}
}
}Links (or repoints) a project to a Google Business Profile location. Take googleAccountId and googleLocationId verbatim from list_gbp_locations — both are Google's own resource names (e.g. "accounts/123", "locations/456"), not free text. Repointing replaces the existing link.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| googleAccountId* | string | Google account resource name, e.g. accounts/123. |
| googleLocationId* | string | Google location resource name, e.g. locations/456. |
{
"type": "object",
"required": [
"projectId",
"googleAccountId",
"googleLocationId"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"googleAccountId": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Google account resource name, e.g. accounts/123."
},
"googleLocationId": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Google location resource name, e.g. locations/456."
}
}
}Sets or clears the URL you intend to rank for a tracked keyword. Feeds list_rank_cannibalisation — set this for the keywords you care about, then check for a mismatch after the next run. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| configId* | string | Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| targetUrl* | any | Absolute http(s) URL you intend to rank for this keyword, or null to clear a previously-set one. |
| trackingKeywordId* | string | The tracked keyword's own row ID (from the rank tracker's keyword rows), not the keyword text. |
{
"type": "object",
"required": [
"projectId",
"configId",
"trackingKeywordId",
"targetUrl"
],
"properties": {
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"targetUrl": {
"anyOf": [
{
"type": "string",
"maxLength": 2048,
"minLength": 1
},
{
"type": "null"
}
],
"description": "Absolute http(s) URL you intend to rank for this keyword, or null to clear a previously-set one."
},
"trackingKeywordId": {
"type": "string",
"minLength": 1,
"description": "The tracked keyword's own row ID (from the rank tracker's keyword rows), not the keyword text."
}
}
}Replaces a report definition's whole widget list, in print order. Returns `{ widgetCount }`. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| widgetTypes* | array | Widget types, in print order. Catalogue: site_health (Crawl findings and issue counts from the latest audit.); internal_linking (Orphan pages, dead ends and link depth from the latest crawl.); rank_tracking (Rank-tracker scorecards per tracked domain.); keywords (Saved keywords with volume, difficulty and CPC.); search_console (Search Console totals and top queries/pages.); web_analytics (Sessions, pageviews and bounce rate from the tracking snippet.); gsc_coverage (How many of your pages Search Console has inspected, and their most common state.); rank_movers (Keywords that newly started ranking or climbed fast.); rank_volatility (How much your own positions churned between the last two checks.); share_of_voice (Your tracked domain's share of visibility against its competitors.); log_analyzer (Total server hits and the AI-crawler share of them.) |
| reportDefinitionId* | string | Report definition ID. Use list_report_definitions to find it. |
{
"type": "object",
"required": [
"projectId",
"reportDefinitionId",
"widgetTypes"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"widgetTypes": {
"type": "array",
"items": {
"enum": [
"site_health",
"internal_linking",
"rank_tracking",
"keywords",
"search_console",
"web_analytics",
"gsc_coverage",
"rank_movers",
"rank_volatility",
"share_of_voice",
"log_analyzer"
],
"type": "string"
},
"maxItems": 11,
"description": "Widget types, in print order. Catalogue: site_health (Crawl findings and issue counts from the latest audit.); internal_linking (Orphan pages, dead ends and link depth from the latest crawl.); rank_tracking (Rank-tracker scorecards per tracked domain.); keywords (Saved keywords with volume, difficulty and CPC.); search_console (Search Console totals and top queries/pages.); web_analytics (Sessions, pageviews and bounce rate from the tracking snippet.); gsc_coverage (How many of your pages Search Console has inspected, and their most common state.); rank_movers (Keywords that newly started ranking or climbed fast.); rank_volatility (How much your own positions churned between the last two checks.); share_of_voice (Your tracked domain's share of visibility against its competitors.); log_analyzer (Total server hits and the AI-crawler share of them.)"
},
"reportDefinitionId": {
"type": "string",
"minLength": 1,
"description": "Report definition ID. Use list_report_definitions to find it."
}
}
}Link (or repoint) a project's Yandex Webmaster host. `hostId` is re-validated against the caller's LIVE host list on every call.
| Argument | Type | Description |
|---|---|---|
| hostId* | string | Yandex's own composite protocol:domain:port host id, taken verbatim from list_yandex_hosts. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"hostId"
],
"properties": {
"hostId": {
"type": "string",
"minLength": 1,
"description": "Yandex's own composite protocol:domain:port host id, taken verbatim from list_yandex_hosts."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Starts a MisarSEO site crawl/audit from startUrl. Returns `{ jobId, auditId }` (the same id twice) with HTTP 202; the crawl then runs in the background — poll it with get_site_audit_status. FREE of SEO credits, but it CONSUMES ONE of the organization's monthly audits from its plan quota (a spent quota is a plan-limit error), so confirm with the user before starting one and never start one speculatively.
| Argument | Type | Description |
|---|---|---|
| maxPages | integer | Most pages to crawl (at most 5000). Omit for the service default of 50. |
| renderJs | boolean | Run each page's JavaScript before analysing it — needed for client-rendered sites. Defaults to false. |
| startUrl* | string | http(s) URL the crawl starts from, usually the site's home page. |
| strategy | string | Which pages get a Lighthouse run. Defaults to auto. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"startUrl"
],
"properties": {
"maxPages": {
"type": "integer",
"maximum": 5000,
"minimum": 1,
"description": "Most pages to crawl (at most 5000). Omit for the service default of 50."
},
"renderJs": {
"type": "boolean",
"description": "Run each page's JavaScript before analysing it — needed for client-rendered sites. Defaults to false."
},
"startUrl": {
"type": "string",
"minLength": 1,
"description": "http(s) URL the crawl starts from, usually the site's home page."
},
"strategy": {
"enum": [
"auto",
"all",
"manual",
"none"
],
"type": "string",
"description": "Which pages get a Lighthouse run. Defaults to auto."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Push a project's own URLs to Baidu (data.zz.baidu.com/urls) for expedited crawling. FREE (no credits), but this is a MUTATION that spends the project's real, Baidu-computed daily quota. Answers { ok: false, reason: 'not_connected' } when the project has no Baidu connection, 'quota_exceeded' (with the last-known remain) when today's quota is already spent, or 'site_not_verified' when Baidu reports the site/token pair as unverified — all as a normal result, never a thrown error, since these are expected operational states.
| Argument | Type | Description |
|---|---|---|
| urls* | array | Absolute URLs to push to Baidu, up to 2000 per call. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"urls"
],
"properties": {
"urls": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"maxItems": 2000,
"minItems": 1,
"description": "Absolute URLs to push to Baidu, up to 2000 per call."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}SubmitContent: pushes a page's full content (a base64-encoded HTTP message) directly to Bing for a project's matched site, bypassing a crawl. FREE (no credits), but this is a MUTATION — unlike get_bing_site_overview, it queues real work on the operator's live Bing Webmaster account. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| url* | string | Absolute URL of the page being submitted. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| httpMessage* | string | The page's full HTTP response, already base64-encoded by the caller — this tool does not encode it. |
| dynamicServing | string | Optional dynamic-serving indicator Bing accepts alongside the content. |
| structuredData | string | Optional structured-data payload Bing accepts alongside the content. |
{
"type": "object",
"required": [
"projectId",
"url",
"httpMessage"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "Absolute URL of the page being submitted."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"httpMessage": {
"type": "string",
"minLength": 1,
"description": "The page's full HTTP response, already base64-encoded by the caller — this tool does not encode it."
},
"dynamicServing": {
"type": "string",
"description": "Optional dynamic-serving indicator Bing accepts alongside the content."
},
"structuredData": {
"type": "string",
"description": "Optional structured-data payload Bing accepts alongside the content."
}
}
}SubmitFeed: submits a sitemap/feed URL to Bing for a project's matched site. FREE (no credits), but this is a MUTATION — unlike get_bing_site_overview, it queues real work on the operator's live Bing Webmaster account. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| feedUrl* | string | Absolute URL of the sitemap/feed to submit. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"feedUrl"
],
"properties": {
"feedUrl": {
"type": "string",
"format": "uri",
"description": "Absolute URL of the sitemap/feed to submit."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Submit up to 10,000 of a project's own URLs to IndexNow (relayed to Bing, Yandex, Seznam and Naver) so they are re-crawled without waiting for the next scheduled crawl. FREE — no credits, no OAuth. Requires the project to have an IndexNow key set (PATCH /api/seo/projects/:projectId with { indexNowKey }) — the key it hosts at https://<domain>/<key>.txt. Every completed site-audit crawl also submits its own crawled URLs automatically, so this tool is for resubmitting specific URLs on demand (e.g. right after a manual publish), not the primary path. Every URL must be on the project's own domain — a mixed-host batch is rejected before any request reaches IndexNow.
| Argument | Type | Description |
|---|---|---|
| urls* | array | Absolute URLs to submit, all on the project's own domain. |
| projectId* | string | Project id from list_projects. |
{
"type": "object",
"required": [
"projectId",
"urls"
],
"properties": {
"urls": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"maxItems": 10000,
"minItems": 1,
"description": "Absolute URLs to submit, all on the project's own domain."
},
"projectId": {
"type": "string",
"description": "Project id from list_projects."
}
}
}AddBlockedUrl + RemoveBlockedUrl: tell Bing to stop, or resume, showing one URL in search results for a project's matched site. FREE (no credits), but this is a MUTATION. There is no verified GetBlockedUrls to list current blocks with — add/remove-only. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| url* | string | The URL (or directory) to block/unblock. |
| date | string | ISO-8601 date for the block. Defaults to now. |
| action* | string | add: block this URL from search results. remove: undo a previous block. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| entityType* | any | 0 = a single page, 1 = a directory. |
| requestType* | any | 0 = CacheOnly (keep out of the cache, still crawlable), 1 = full block. |
AddQueryParameter + RemoveQueryParameter + EnableDisableQueryParameter: add, remove, or toggle one query-string parameter Bing is told to ignore when canonicalizing URLs for a project's matched site. FREE (no credits), but this is a MUTATION. get_bing_site_overview's queryParameters field (GetQueryParameters) is the read half of this family. Bring-your-own-key: requires BING_WEBMASTER_API_KEY. Answers { ok: false, reason: 'not_configured' } when unset, or 'no_matching_site' when the project's domain has no verified match on the account.
| Argument | Type | Description |
|---|---|---|
| action* | string | add: start ignoring this parameter. remove: stop ignoring it entirely. enable/disable: toggle an already-added rule without removing it. |
| parameter* | string | The query-string parameter name, e.g. 'utm_source'. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId",
"action",
"parameter"
],
"properties": {
"action": {
"enum": [
"add",
"remove",
"enable",
"disable"
],
"type": "string",
"description": "add: start ignoring this parameter. remove: stop ignoring it entirely. enable/disable: toggle an already-added rule without removing it."
},
"parameter": {
"type": "string",
"minLength": 1,
"description": "The query-string parameter name, e.g. 'utm_source'."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}Link Building Tool: edit a prospect's editable fields (url/label/targetUrl/contact fields) — only fields given here are changed. FREE (no credits), but this is a MUTATION. `domain` and `status` are not editable here — use update_link_prospect_status for the pipeline stage.
| Argument | Type | Description |
|---|---|---|
| url | string | |
| label | string | |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| targetUrl | string | |
| prospectId* | string | Prospect id, from list_link_prospects. |
| contactName | string | |
| contactEmail | string | |
| contactNotes | string |
{
"type": "object",
"required": [
"projectId",
"prospectId"
],
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"label": {
"type": "string",
"maxLength": 200
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"targetUrl": {
"type": "string",
"format": "uri"
},
"prospectId": {
"type": "string",
"minLength": 1,
"description": "Prospect id, from list_link_prospects."
},
"contactName": {
"type": "string",
"maxLength": 300
},
"contactEmail": {
"type": "string",
"maxLength": 300
},
"contactNotes": {
"type": "string",
"maxLength": 1000
}
}
}Link Building Tool: move a prospect through the outreach-status pipeline — prospected -> contacted -> replied -> link_placed/declined/no_response ('declined'/'no_response' can loop back to 'contacted' for a re-approach). FREE (no credits), but this is a MUTATION. Throws on an illegal jump (e.g. prospected straight to link_placed) — the error names the allowed next stage(s). Always appends an activity-log entry describing the change.
| Argument | Type | Description |
|---|---|---|
| note | string | Optional extra context appended to the auto-generated activity-log entry, e.g. 'sent follow-up #2'. |
| status* | string | |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| prospectId* | string | Prospect id, from list_link_prospects. |
{
"type": "object",
"required": [
"projectId",
"prospectId",
"status"
],
"properties": {
"note": {
"type": "string",
"maxLength": 2000,
"description": "Optional extra context appended to the auto-generated activity-log entry, e.g. 'sent follow-up #2'."
},
"status": {
"enum": [
"prospected",
"contacted",
"replied",
"link_placed",
"declined",
"no_response"
],
"type": "string"
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"prospectId": {
"type": "string",
"minLength": 1,
"description": "Prospect id, from list_link_prospects."
}
}
}Attaches or detaches ONE tag across a selection of tracked keywords (up to 500 keyword IDs per call). One tag per call — applying three tags takes three calls. FREE: database only. No SEO data provider is called and no credits are spent. The tag is the project's EXISTING saved-keyword vocabulary, shared with the saved-keyword list. Pass `tagId` for a tag that already exists (find it with list_rank_keyword_tags), or `tagName` to match on the normalised name. On `action: "add"` an unknown `tagName` CREATES that tag for the whole project, so check the vocabulary first rather than coining near-duplicates; on `action: "remove"` an unknown tag is NOT_FOUND, because there is nothing to detach. DO NOT REPORT `changed` AS A COUNT OF NEW LINKS ON AN ADD. `action: "add"` returns `changed: null` because the insert is idempotent and how many links were actually new is not knowable without a second read — `null` means "not measured". It is not zero, and it is NOT the number of keyword IDs submitted. `action: "remove"` returns a real `changed` count. When asked how many keywords were touched, report `keywordCount`: the tracked keywords the action was applied to, after de-duplication. Both actions return `keywordCount: 0` with `changed: 0` when none of the submitted IDs belong to this tracker — a no-op, not a tagging worth reporting as done.
| Argument | Type | Description |
|---|---|---|
| tagId | string | Existing tag ID from list_rank_keyword_tags. Pass this OR `tagName`, never both. |
| action* | string | 'add' attaches the tag, creating it from `tagName` when it does not exist yet; 'remove' detaches it. |
| tagName | string | Tag name, matched on its normalised form — 'Branded', 'branded' and ' branded ' are one tag. Created on 'add' when it does not exist; NOT_FOUND on 'remove'. Pass this OR `tagId`, never both. |
Creates or replaces a brand monitor — the whole schedule and competitor list, not single fields. Requires the admin role in the calling organization, because it starts UNATTENDED RECURRING spend from the SAME monthly Brand Radar scan quota that run_brand_radar_scan uses, which can leave less quota for ad hoc scans that month. Confirm the schedule with the user before calling.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| brandTarget* | string | Brand, domain or product name to monitor. |
| competitors* | array | Up to 5 competitor brands/domains to compare against. Send [] for none. |
| scheduleInterval* | string | How often the monitor re-scans. |
{
"type": "object",
"required": [
"projectId",
"brandTarget",
"scheduleInterval",
"competitors"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"brandTarget": {
"type": "string",
"maxLength": 250,
"minLength": 1,
"description": "Brand, domain or product name to monitor."
},
"competitors": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 5,
"description": "Up to 5 competitor brands/domains to compare against. Send [] for none."
},
"scheduleInterval": {
"enum": [
"daily",
"weekly",
"monthly"
],
"type": "string",
"description": "How often the monitor re-scans."
}
}
}Creates or replaces a report definition's schedule — the whole interval + recipient list, not individual fields. Requires the admin role in the calling organization, the same gate patch drafting/publishing uses, since this starts unattended recurring email delivery. FREE: database only, no SEO data provider is called and no credits are spent.
| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| recipientUserIds* | array | Platform user IDs to email. Must be members of the calling organization — a non-member id is silently dropped. There is no arbitrary external-email recipient in this version; use list_team_members-style tooling to find teammate ids. |
| scheduleInterval* | string | How often the report is re-sent. |
| reportDefinitionId* | string | Report definition ID. Use list_report_definitions to find it. |
{
"type": "object",
"required": [
"projectId",
"reportDefinitionId",
"scheduleInterval",
"recipientUserIds"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"recipientUserIds": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 20,
"description": "Platform user IDs to email. Must be members of the calling organization — a non-member id is silently dropped. There is no arbitrary external-email recipient in this version; use list_team_members-style tooling to find teammate ids."
},
"scheduleInterval": {
"enum": [
"daily",
"weekly",
"monthly"
],
"type": "string",
"description": "How often the report is re-sent."
},
"reportDefinitionId": {
"type": "string",
"minLength": 1,
"description": "Report definition ID. Use list_report_definitions to find it."
}
}
}Creates or replaces a tracked prompt — the whole model list and schedule, not single fields. On every scheduled check the SAME prompt is re-run (grounded, for real citations) and diffed against the previous check. Requires the admin role in the calling organization, because it starts UNATTENDED RECURRING spend from the shared AI-gateway monthly budget. Confirm the schedule and models with the user before calling.
| Argument | Type | Description |
|---|---|---|
| models* | array | 1-4 model personas, as for explore_ai_prompt. |
| prompt* | string | The prompt to re-run on every check. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| brandTarget* | string | The brand whose mentions and citations are tracked. |
| scheduleInterval* | string | How often the prompt is re-checked. |
{
"type": "object",
"required": [
"projectId",
"prompt",
"brandTarget",
"models",
"scheduleInterval"
],
"properties": {
"models": {
"type": "array",
"items": {
"enum": [
"chat_gpt",
"claude",
"gemini",
"perplexity"
],
"type": "string"
},
"maxItems": 4,
"minItems": 1,
"description": "1-4 model personas, as for explore_ai_prompt."
},
"prompt": {
"type": "string",
"maxLength": 500,
"minLength": 1,
"description": "The prompt to re-run on every check."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"brandTarget": {
"type": "string",
"maxLength": 250,
"minLength": 1,
"description": "The brand whose mentions and citations are tracked."
},
"scheduleInterval": {
"enum": [
"daily",
"weekly",
"monthly"
],
"type": "string",
"description": "How often the prompt is re-checked."
}
}
}{
"type": "object",
"required": [
"projectId",
"domain"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "Optional specific page on domain, a full absolute URL."
},
"note": {
"type": "string",
"maxLength": 2000,
"description": "Optional first activity-log entry, e.g. 'Found via competitor backlink profile.'"
},
"label": {
"type": "string",
"maxLength": 200
},
"domain": {
"type": "string",
"minLength": 1,
"description": "Bare domain, e.g. example.com."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"targetUrl": {
"type": "string",
"format": "uri",
"description": "The project's OWN page this outreach is trying to get linked."
},
"contactName": {
"type": "string",
"maxLength": 300
},
"contactEmail": {
"type": "string",
"maxLength": 300
},
"contactNotes": {
"type": "string",
"maxLength": 1000
}
}
}| competitors* |
| array |
| 1-5 competitor brands/domains checked against the same answers. |
| webSearchCountryCode | string | Two-letter country the retrieval pass searches from. |
{
"type": "object",
"required": [
"projectId",
"prompt",
"brand",
"competitors",
"models"
],
"properties": {
"brand": {
"type": "string",
"maxLength": 250,
"minLength": 1,
"description": "The target brand, domain or product checked in each answer."
},
"models": {
"type": "array",
"items": {
"enum": [
"chat_gpt",
"claude",
"gemini",
"perplexity"
],
"type": "string"
},
"maxItems": 4,
"minItems": 1,
"description": "1-4 model personas, as for explore_ai_prompt (persona labels, not vendor-verified models)."
},
"prompt": {
"type": "string",
"maxLength": 500,
"minLength": 1,
"description": "The one prompt every requested model answers."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"webSearch": {
"type": "boolean",
"description": "Ground the answers in a real retrieval pass with cited sources. Defaults to true."
},
"competitors": {
"type": "array",
"items": {
"type": "string",
"maxLength": 250,
"minLength": 1
},
"maxItems": 5,
"minItems": 1,
"description": "1-5 competitor brands/domains checked against the same answers."
},
"webSearchCountryCode": {
"enum": [
"US",
"GB",
"CA",
"AU",
"IE",
"DE",
"FR",
"ES",
"IT",
"NL",
"PT",
"PL",
"SE",
"NO",
"DK",
"BR",
"MX",
"IN",
"JP",
"KR",
"SG",
"HK",
"TW",
"ZA"
],
"type": "string",
"description": "Two-letter country the retrieval pass searches from."
}
}
}| string |
| Search language code. |
{
"type": "object",
"required": [
"projectId",
"domain",
"keyword",
"centerLat",
"centerLng",
"centerLabel",
"spacingKm"
],
"properties": {
"domain": {
"type": "string",
"maxLength": 253,
"minLength": 1,
"description": "Domain whose organic rank is checked."
},
"keyword": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "The search keyword."
},
"centerLat": {
"type": "number",
"maximum": 90,
"minimum": -90,
"description": "Latitude of the grid centre."
},
"centerLng": {
"type": "number",
"maximum": 180,
"minimum": -180,
"description": "Longitude of the grid centre."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"spacingKm": {
"type": "number",
"maximum": 25,
"minimum": 0.5,
"description": "Distance between grid points, 0.5-25 km."
},
"centerLabel": {
"type": "string",
"maxLength": 300,
"minLength": 1,
"description": "What the centre point is called, e.g. 'Manchester city centre'."
},
"languageCode": {
"type": "string",
"maxLength": 10,
"minLength": 2,
"description": "Search language code."
}
}
}| scheduleInterval | string | How often the scheduled check runs. |
| newKeywordAlertEnabled | boolean | Notify when the domain starts ranking for a keyword it did not rank for before. |
| rankDropAlertThreshold | integer | null | Notify when a keyword falls by at least this many positions. null turns it off. |
| competitorMoveAlertThreshold | integer | null | Notify when a tracked competitor gains at least this many positions. null turns it off. |
{
"type": "object",
"required": [
"projectId",
"domain",
"serpDepth"
],
"properties": {
"domain": {
"type": "string",
"minLength": 1,
"description": "Domain whose positions are tracked, e.g. example.com."
},
"devices": {
"enum": [
"both",
"desktop",
"mobile"
],
"type": "string",
"description": "Devices to track."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"serpDepth": {
"type": "integer",
"maximum": 100,
"minimum": 10,
"multipleOf": 10,
"description": "How deep to search, 10-100 in steps of 10."
},
"languageCode": {
"type": "string",
"maxLength": 10,
"description": "Language code (e.g. 'en'). Omit it to use the project's default market language."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market.",
"exclusiveMinimum": 0
},
"locationHint": {
"type": [
"string",
"null"
],
"maxLength": 200,
"description": "Free-text city/region hint, honoured only where the SERP provider supports it."
},
"scheduleInterval": {
"enum": [
"daily",
"weekly",
"manual"
],
"type": "string",
"description": "How often the scheduled check runs."
},
"newKeywordAlertEnabled": {
"type": "boolean",
"description": "Notify when the domain starts ranking for a keyword it did not rank for before."
},
"rankDropAlertThreshold": {
"type": [
"integer",
"null"
],
"maximum": 100,
"minimum": 3,
"description": "Notify when a keyword falls by at least this many positions. null turns it off."
},
"competitorMoveAlertThreshold": {
"type": [
"integer",
"null"
],
"maximum": 100,
"minimum": 3,
"description": "Notify when a tracked competitor gains at least this many positions. null turns it off."
}
}
}| webSearchCountryCode | string |
{
"type": "object",
"required": [
"projectId",
"prompt",
"models"
],
"properties": {
"models": {
"type": "array",
"items": {
"enum": [
"chat_gpt",
"claude",
"gemini",
"perplexity"
],
"type": "string"
},
"maxItems": 4,
"minItems": 1,
"description": "1-4 model personas to answer the prompt (chat_gpt, claude, gemini, perplexity). See this tool's own description for what these labels do and do not guarantee."
},
"prompt": {
"type": "string",
"maxLength": 500,
"minLength": 1,
"description": "The prompt to send to each requested model."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"webSearch": {
"type": "boolean",
"description": "Ground the answer in a real retrieval pass with cited sources, shared across every requested persona. Defaults to true."
},
"highlightBrand": {
"type": "string",
"maxLength": 250,
"minLength": 1,
"description": "A brand/entity name to highlight mentions of in each answer. Required for analyzeSentiment."
},
"analyzeSentiment": {
"type": "boolean",
"description": "Classify sentiment toward highlightBrand in each answer. Requires highlightBrand. Spends one EXTRA AI-gateway operation on top of the base call — never charged unless both this and highlightBrand are set."
},
"webSearchCountryCode": {
"enum": [
"US",
"GB",
"CA",
"AU",
"IE",
"DE",
"FR",
"ES",
"IT",
"NL",
"PT",
"PL",
"SE",
"NO",
"DK",
"BR",
"MX",
"IN",
"JP",
"KR",
"SG",
"HK",
"TW",
"ZA"
],
"type": "string"
}
}
}| string |
| Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| resultTypes | array | SERP result types to include. Defaults to organic and local_pack. |
| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
| excludeDomains | array | Domains to exclude from results (e.g. the user's own site). |
| includeSubdomains | boolean | Count subdomains as part of the same competitor domain. |
{
"type": "object",
"required": [
"projectId",
"keywords"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Maximum competitors to return (1-100). Defaults to 50."
},
"offset": {
"type": "integer",
"maximum": 1000,
"minimum": 0,
"description": "Rows to skip for pagination."
},
"sortBy": {
"enum": [
"visibility",
"traffic_estimate",
"avg_position",
"keyword_count"
],
"type": "string",
"description": "Sort order for returned competitors. Defaults to visibility. Only `keyword_count` and `avg_position` are measured on every provider — `visibility` and `traffic_estimate` are unmeasured on the free provider, and rows without the chosen field sort last rather than as zero. Prefer `keyword_count` when you need an order that always means something."
},
"keywords": {
"type": "array",
"items": {
"type": "string",
"maxLength": 120,
"minLength": 1
},
"maxItems": 100,
"minItems": 1,
"description": "Keywords whose SERPs are compared (1-100)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"resultTypes": {
"type": "array",
"items": {
"enum": [
"organic",
"paid",
"featured_snippet",
"local_pack"
],
"type": "string"
},
"maxItems": 4,
"minItems": 1,
"description": "SERP result types to include. Defaults to organic and local_pack."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
},
"excludeDomains": {
"type": "array",
"items": {
"type": "string",
"maxLength": 255,
"minLength": 1
},
"maxItems": 50,
"minItems": 1,
"description": "Domains to exclude from results (e.g. the user's own site)."
},
"includeSubdomains": {
"type": "boolean",
"description": "Count subdomains as part of the same competitor domain."
}
}
}{
"type": "object",
"required": [
"projectId",
"target"
],
"properties": {
"page": {
"type": "integer",
"minimum": 1,
"description": "1-based page number. Defaults to 1."
},
"scope": {
"enum": [
"domain",
"page"
],
"type": "string",
"description": "Whether target is a whole domain or one page. Defaults to domain."
},
"target": {
"type": "string",
"maxLength": 2048,
"minLength": 1,
"description": "Domain or URL whose inbound anchor texts to list."
},
"pageSize": {
"enum": [
50,
100,
200
],
"type": "integer",
"description": "Rows per page. Defaults to 100."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"sortField": {
"enum": [
"anchor",
"backlinks",
"referringDomains",
"referringPages",
"dofollow",
"firstSeen"
],
"type": "string",
"description": "Sort column. Defaults to backlinks."
},
"sortOrder": {
"enum": [
"asc",
"desc"
],
"type": "string",
"description": "Sort direction. Defaults to desc."
}
}
}| ISO 639-1 language code Brave uses to filter results, e.g. 'en'. |
| targetDomain | string | Domain or URL to check. Defaults to the project's own domain when omitted. |
| competitorDomains | array | Competitor domains to also resolve positions for, free within the same fetched result set. |
{
"type": "object",
"required": [
"projectId",
"keyword"
],
"properties": {
"depth": {
"type": "integer",
"maximum": 20,
"minimum": 1,
"description": "Results to fetch, 1-20 (Brave's own per-request ceiling). Default 20."
},
"country": {
"type": "string",
"maxLength": 2,
"minLength": 2,
"description": "2-letter country code Brave uses to target results, e.g. 'US'."
},
"keyword": {
"type": "string",
"minLength": 1,
"description": "Keyword to check."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"searchLang": {
"type": "string",
"maxLength": 8,
"minLength": 2,
"description": "ISO 639-1 language code Brave uses to filter results, e.g. 'en'."
},
"targetDomain": {
"type": "string",
"minLength": 1,
"description": "Domain or URL to check. Defaults to the project's own domain when omitted."
},
"competitorDomains": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 20,
"description": "Competitor domains to also resolve positions for, free within the same fetched result set."
}
}
}| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"sort": {
"enum": [
"impressions",
"position_spread"
],
"type": "string",
"description": "Finding order (default impressions). 'impressions' = most affected search demand first. 'position_spread' = smallest gap between the competing pages' average positions first, i.e. the pages Google rated most interchangeable. Both order by one measured quantity; there is deliberately no composite severity score."
},
"window": {
"enum": [
"last_7_days",
"last_28_days",
"last_3_months"
],
"type": "string",
"description": "Analysis window (default last_28_days). Capped at three months on purpose — Search Console's longer ranges are NOT offered, because over a long window 'two pages received impressions' also describes a page migration, and calling that cannibalisation sends someone to consolidate pages that are already consolidated. The window end is backed off ~3 days for Search Console's data lag."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}| integer |
| A page must have had at least this many clicks in the earlier window to be considered at all (default 10) — a noise floor, not part of the decay definition. |
| declineThresholdPct | integer | How big a decline (percent) is required to be reported (default 20). 0 disables reporting entirely. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"window": {
"enum": [
"last_28_days",
"last_90_days"
],
"type": "string",
"description": "The RECENT period length (default last_28_days). The EARLIER period compared against it is the same length immediately before it — a period-over-period comparison, not a calendar-month split."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"minEarlierClicks": {
"type": "integer",
"maximum": 100000,
"minimum": 0,
"description": "A page must have had at least this many clicks in the earlier window to be considered at all (default 10) — a noise floor, not part of the decay definition."
},
"declineThresholdPct": {
"type": "integer",
"maximum": 100,
"minimum": 0,
"description": "How big a decline (percent) is required to be reported (default 20). 0 disables reporting entirely."
}
}
}| locationCode |
| integer |
| Numeric location code. Defaults to the project's stored market, else 2840 (United States). |
{
"type": "object",
"required": [
"projectId",
"url",
"keyword"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "The page to grade (any http(s) URL)."
},
"keyword": {
"type": "string",
"minLength": 1,
"description": "The keyword this page is meant to rank for."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"maxLength": 8,
"minLength": 2,
"description": "Language code. Defaults to the project's stored market, else 'en'."
},
"locationCode": {
"type": "integer",
"description": "Numeric location code. Defaults to the project's stored market, else 2840 (United States).",
"exclusiveMinimum": 0
}
}
}{
"type": "object",
"required": [
"projectId",
"prompt",
"models"
],
"properties": {
"models": {
"type": "array",
"items": {
"enum": [
"you_com",
"deepseek",
"grok",
"meta_ai",
"minimax"
],
"type": "string"
},
"maxItems": 5,
"minItems": 1,
"description": "1-5 vendor models to ask."
},
"prompt": {
"type": "string",
"minLength": 1,
"description": "The prompt to send to each requested vendor model."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"webSearch": {
"type": "boolean",
"description": "Ask for grounded answers where the vendor supports it (see the tool description). Defaults to false."
},
"highlightBrand": {
"type": "string",
"description": "A brand/entity name to highlight mentions of in each answer."
}
}
}| includeSubdomains | boolean | Include subdomains in the domain's metrics. Defaults to false. |
{
"type": "object",
"required": [
"projectId",
"domain"
],
"properties": {
"domain": {
"type": "string",
"minLength": 1,
"description": "Domain to analyze (e.g. 'example.com')."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
},
"includeSubdomains": {
"type": "boolean",
"default": false,
"description": "Include subdomains in the domain's metrics. Defaults to false."
}
}
}| string |
| Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
{
"type": "object",
"required": [
"projectId",
"keyword",
"near"
],
"properties": {
"near": {
"type": "object",
"required": [
"latitude",
"longitude",
"radiusKm"
],
"properties": {
"latitude": {
"type": "number",
"maximum": 90,
"minimum": -90,
"description": "Latitude of the search center."
},
"radiusKm": {
"type": "number",
"maximum": 100000,
"minimum": 1,
"description": "Search radius around the center, in kilometers. get_google_business_questions clamps it to roughly 0.2-200 km and ignores it entirely on the free provider — see that tool's description before reporting a radius as applied."
},
"longitude": {
"type": "number",
"maximum": 180,
"minimum": -180,
"description": "Longitude of the search center."
}
},
"description": "Coordinate and radius to search around."
},
"depth": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Maximum Q&A rows to fetch (1-100). Defaults to 20."
},
"keyword": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Business name or search phrase identifying the Google Business Profile."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
}
}
}{
"type": "object",
"required": [
"projectId"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Explicit end date (YYYY-MM-DD). Use with startDate."
},
"dateRange": {
"enum": [
"last_7_days",
"last_28_days",
"last_3_months",
"last_6_months",
"last_12_months",
"last_16_months"
],
"type": "string",
"description": "Convenience window (default 'last_28_days'). This DOES call Google, so Search Console's usual ~16-month clamp and ~3-day data lag both apply."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Explicit start date (YYYY-MM-DD). Use with endDate."
}
}
}{
"type": "object",
"required": [
"projectId"
],
"properties": {
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Explicit end date (YYYY-MM-DD). Use with startDate."
},
"dateRange": {
"enum": [
"last_7_days",
"last_28_days",
"last_3_months",
"last_6_months",
"last_12_months",
"last_16_months"
],
"type": "string",
"description": "Convenience window over MisarSEO's own archive (default 'last_28_days'). No Google clamp applies — this never calls Google."
},
"dimension": {
"enum": [
"query",
"page"
],
"type": "string",
"description": "Bucket by query or page position (default 'query')."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Explicit start date (YYYY-MM-DD). Use with endDate."
}
}
}| languageCode | string | Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'. |
| locationCode | integer | DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable. |
| classifications | array | Restrict rows to these classifications. Omit for all five — omitting never hides `unknown`. The filter is applied before the row cap, and `totalRows` reports the pre-cap match count. |
| competitorDomains* | array | 1–3 competitor domains (bare domain or full URL). Each one is a separate billable ranked-keywords lookup, so pick the rivals actually being chased. The project's own domain is added automatically and is ignored if it appears here. |
| includeSubdomains | boolean | Count subdomains as part of a domain. Defaults to true, and applies to every compared domain including ours. |
{
"type": "object",
"required": [
"projectId",
"competitorDomains"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"sortField": {
"enum": [
"volume",
"theirPosition",
"ourPosition"
],
"type": "string",
"description": "Sort column: 'volume' (default), 'theirPosition' or 'ourPosition'. All three are measured values — there is deliberately no composite opportunity score. Nulls sort last in both directions."
},
"sortOrder": {
"enum": [
"asc",
"desc"
],
"type": "string",
"description": "Sort direction. Defaults to 'desc'."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
},
"classifications": {
"type": "array",
"items": {
"enum": [
"missing",
"behind",
"tied",
"ahead",
"unknown"
],
"type": "string"
},
"maxItems": 5,
"minItems": 1,
"description": "Restrict rows to these classifications. Omit for all five — omitting never hides `unknown`. The filter is applied before the row cap, and `totalRows` reports the pre-cap match count."
},
"competitorDomains": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 3,
"minItems": 1,
"description": "1–3 competitor domains (bare domain or full URL). Each one is a separate billable ranked-keywords lookup, so pick the rivals actually being chased. The project's own domain is added automatically and is ignored if it appears here."
},
"includeSubdomains": {
"type": "boolean",
"description": "Count subdomains as part of a domain. Defaults to true, and applies to every compared domain including ours."
}
}
}| competitorDomains | array | 0-3 competitor domains (bare domain or full URL). |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"tagId": {
"type": "string",
"minLength": 1,
"description": "The saved-keyword tag ID to treat as 'the list'. Omit with tagName to mean every saved keyword in the project."
},
"tagName": {
"type": "string",
"minLength": 1,
"description": "Alternative to tagId — the tag's name."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
},
"competitorDomains": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 3,
"description": "0-3 competitor domains (bare domain or full URL)."
}
}
}{
"type": "object",
"required": [
"projectId",
"keyword"
],
"properties": {
"keyword": {
"type": "string",
"minLength": 1,
"description": "The keyword to analyze."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
}
}
}{
"type": "object",
"required": [
"projectId",
"keyword"
],
"properties": {
"keyword": {
"type": "string",
"minLength": 1,
"description": "The keyword to look up."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"sinceDays": {
"type": "integer",
"maximum": 730,
"description": "How many days of tracked history to return, 1-730 (default 365). Only meaningful when the keyword is tracked.",
"exclusiveMinimum": 0
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
}
}
}{
"type": "object",
"required": [
"projectId",
"keywords"
],
"properties": {
"keywords": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 500,
"minItems": 1,
"description": "1-500 keywords to analyze (e.g. from a bulk research run or an export)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
}
}
}| Argument | Type | Description |
|---|---|---|
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| sortField | string | Sort column: 'competitors' (default), 'authority' or 'linkingPages'. All three are values the provider measured — there is deliberately no composite opportunity score. Re-sorting a run reuses the cached lookups and costs nothing. |
| sortOrder | string | Sort direction. Defaults to 'desc'. |
| minCompetitors | integer | Keep only domains linking to at least this many of the compared competitors. Defaults to 2, and is clamped to the number of competitors that actually resolved — compare `minCompetitors` against `minCompetitorsRequested` on the result before calling a run empty. |
| includeUnproven | boolean | Include rows whose absence from our own profile could not be proven. Defaults to true — leave it true: unproven rows are labelled rather than hidden, and excluding them silently drops real candidates. |
| competitorDomains* | array | 2–4 competitor domains (bare domain or full URL). Each one is a separate billable referring-domains lookup, so pick the rivals actually being chased. 2 is the floor because with a single competitor this degenerates into a one-sided referring-domain diff, not an intersect. The project's own domain is added automatically and is dropped from this list if it appears here. |
{
"type": "object",
"required": [
"projectId",
"competitorDomains"
],
"properties": {
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"sortField": {
"enum": [
"competitors",
"authority",
"linkingPages"
],
"type": "string",
"description": "Sort column: 'competitors' (default), 'authority' or 'linkingPages'. All three are values the provider measured — there is deliberately no composite opportunity score. Re-sorting a run reuses the cached lookups and costs nothing."
},
"sortOrder": {
"enum": [
"asc",
"desc"
],
"type": "string",
"description": "Sort direction. Defaults to 'desc'."
},
"minCompetitors": {
"type": "integer",
"maximum": 4,
"minimum": 1,
"description": "Keep only domains linking to at least this many of the compared competitors. Defaults to 2, and is clamped to the number of competitors that actually resolved — compare `minCompetitors` against `minCompetitorsRequested` on the result before calling a run empty."
},
"includeUnproven": {
"type": "boolean",
"description": "Include rows whose absence from our own profile could not be proven. Defaults to true — leave it true: unproven rows are labelled rather than hidden, and excluding them silently drops real candidates."
},
"competitorDomains": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 4,
"minItems": 2,
"description": "2–4 competitor domains (bare domain or full URL). Each one is a separate billable referring-domains lookup, so pick the rivals actually being chased. 2 is the floor because with a single competitor this degenerates into a one-sided referring-domain diff, not an intersect. The project's own domain is added automatically and is dropped from this list if it appears here."
}
}
}| Numeric location code. Defaults to the project's stored market, else 2840 (United States). |
| trackedDomain* | string | Bare domain each result page is checked for a link to, e.g. 'acme.com'. |
{
"type": "object",
"required": [
"projectId",
"brand",
"trackedDomain"
],
"properties": {
"brand": {
"type": "string",
"minLength": 1,
"description": "Brand name or bare domain to search for as a SERP QUERY (not a URL to fetch)."
},
"citations": {
"type": "array",
"items": {
"type": "object",
"required": [
"url",
"domain"
],
"properties": {
"url": {
"type": "string"
},
"domain": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
}
},
"maxItems": 100,
"description": "Citations from an ALREADY-RUN Prompt Explorer scan for this brand (map its `citations` to {url, domain}). Never fetched here — omitting it leaves `aiCitationPlatforms: null`."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"maxLength": 8,
"minLength": 2,
"description": "Language code. Defaults to the project's stored market, else 'en'."
},
"locationCode": {
"type": "integer",
"description": "Numeric location code. Defaults to the project's stored market, else 2840 (United States).",
"exclusiveMinimum": 0
},
"trackedDomain": {
"type": "string",
"minLength": 1,
"description": "Bare domain each result page is checked for a link to, e.g. 'acme.com'."
}
}
}{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"minGain": {
"type": "integer",
"maximum": 100,
"description": "Positions a keyword must have gained to count as a fast climber, 1–100. Defaults to 5. Does not affect newlyRanking, which has no earlier position to measure a gain from.",
"exclusiveMinimum": 0
},
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"sinceDays": {
"type": "integer",
"maximum": 730,
"description": "How many days back to look, 1–730. Defaults to 365.",
"exclusiveMinimum": 0
}
}
}{
"type": "object",
"required": [
"projectId",
"configId"
],
"properties": {
"device": {
"enum": [
"desktop",
"mobile"
],
"type": "string",
"description": "On every free SERP source today this narrows nothing — there is no device dimension, so every reading folds to one series regardless of what is passed. Defaults to desktop."
},
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"runLimit": {
"type": "integer",
"maximum": 26,
"minimum": 2,
"description": "How many recent completed runs to compare consecutively, 2-26. Defaults to 12."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}| integer |
| Pagination offset. |
| dataState | string | 'all' (default) includes fresh/incomplete recent data. |
| dateRange | string | Convenience window (default last_28_days). End is set ~3 days back for GSC data lag. Ignored if startDate+endDate are given. Max lookback is 16 months. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| startDate | string | Explicit start (YYYY-MM-DD, Pacific Time). Use with endDate. |
| dimensions | array | Group rows by these dimensions. Default ['query']. Use ['page'] for top pages, ['query','page'] to map queries to pages / spot cannibalization, ['date'] for a time series. |
{
"type": "object",
"required": [
"projectId"
],
"properties": {
"type": {
"enum": [
"web",
"image",
"video",
"news",
"googleNews",
"discover"
],
"type": "string",
"description": "Search type (default web)."
},
"endDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Explicit end (YYYY-MM-DD, Pacific Time). Use with startDate."
},
"filters": {
"type": "array",
"items": {
"type": "object",
"required": [
"dimension",
"expression"
],
"properties": {
"operator": {
"enum": [
"equals",
"notEquals",
"contains",
"notContains"
],
"type": "string",
"default": "equals"
},
"dimension": {
"enum": [
"query",
"page",
"country",
"device",
"date",
"searchAppearance",
"hour"
],
"type": "string"
},
"expression": {
"type": "string",
"minLength": 1
}
}
},
"maxItems": 5,
"description": "AND-combined filters. To get the queries for one page: [{dimension:'page',operator:'equals',expression:'https://example.com/post'}] with dimensions ['query']."
},
"rowLimit": {
"type": "integer",
"maximum": 1000,
"minimum": 1,
"description": "Rows per call (default 1000, max 1000). GSC sorts by clicks desc and can't filter by position — filter 'striking distance' positions client-side, and paginate with startRow when hasMore is true."
},
"startRow": {
"type": "integer",
"minimum": 0,
"description": "Pagination offset."
},
"dataState": {
"enum": [
"all",
"final"
],
"type": "string",
"description": "'all' (default) includes fresh/incomplete recent data."
},
"dateRange": {
"enum": [
"last_7_days",
"last_28_days",
"last_3_months",
"last_6_months",
"last_12_months",
"last_16_months"
],
"type": "string",
"description": "Convenience window (default last_28_days). End is set ~3 days back for GSC data lag. Ignored if startDate+endDate are given. Max lookback is 16 months."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"startDate": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "Explicit start (YYYY-MM-DD, Pacific Time). Use with endDate."
},
"dimensions": {
"type": "array",
"items": {
"enum": [
"query",
"page",
"country",
"device",
"date",
"searchAppearance",
"hour"
],
"type": "string"
},
"maxItems": 4,
"minItems": 1,
"description": "Group rows by these dimensions. Default ['query']. Use ['page'] for top pages, ['query','page'] to map queries to pages / spot cannibalization, ['date'] for a time series."
}
}
}{
"type": "object",
"required": [
"projectId",
"keyword"
],
"properties": {
"keyword": {
"type": "string",
"minLength": 1,
"description": "The seed keyword to research."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"maxLength": 8,
"minLength": 2,
"description": "Language code. Defaults to the project's stored market, else 'en'."
},
"locationCode": {
"type": "integer",
"description": "Numeric location code. Defaults to the project's stored market, else 2840 (United States).",
"exclusiveMinimum": 0
}
}
}| Bare domain each result page is checked for a link to, e.g. 'acme.com'. |
{
"type": "object",
"required": [
"projectId",
"brand",
"trackedDomain"
],
"properties": {
"brand": {
"type": "string",
"minLength": 1,
"description": "Brand name or bare domain to search for as a SERP QUERY (not a URL to fetch)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"maxLength": 8,
"minLength": 2,
"description": "Language code. Defaults to the project's stored market, else 'en'."
},
"locationCode": {
"type": "integer",
"description": "Numeric location code. Defaults to the project's stored market, else 2840 (United States).",
"exclusiveMinimum": 0
},
"trackedDomain": {
"type": "string",
"minLength": 1,
"description": "Bare domain each result page is checked for a link to, e.g. 'acme.com'."
}
}
}{
"type": "object",
"required": [
"projectId"
],
"properties": {
"page": {
"type": "integer",
"minimum": 1,
"description": "1-based page number (default 1)."
},
"sort": {
"enum": [
"createdAt",
"keyword",
"searchVolume",
"cpc",
"competition",
"keywordDifficulty",
"fetchedAt"
],
"type": "string",
"description": "Sort column (default createdAt)."
},
"limit": {
"anyOf": [
{
"type": "number",
"const": 50
},
{
"type": "number",
"const": 100
},
{
"type": "number",
"const": 250
}
],
"description": "Maximum rows to return. Defaults to 100."
},
"order": {
"enum": [
"asc",
"desc"
],
"type": "string",
"description": "Sort direction (default desc)."
},
"search": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Optional keyword text filter."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}| Refine search volumes with clickstream data, which disaggregates Google Ads' grouped close-variant volumes (plurals/misspellings). On the paid DataForSEO provider this DOUBLES the credit cost of each seed. Default false (standard Google-Ads-derived volumes). Ignored — no cost, no effect — for countries served from Google Ads data, and on the free data provider, whose autocomplete + Keyword Planner sources have no clickstream refinement at all. |
{
"type": "object",
"required": [
"projectId",
"seeds"
],
"properties": {
"seeds": {
"type": "array",
"items": {
"type": "object",
"required": [
"seed"
],
"properties": {
"seed": {
"type": "string",
"minLength": 1,
"description": "Seed keyword to research."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
}
}
},
"maxItems": 5,
"minItems": 1,
"description": "1-5 seed keywords. Each seed is researched independently and returns related keywords with volume/difficulty/CPC. Bulk-friendly — prefer this over multiple single-seed calls."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"resultLimit": {
"anyOf": [
{
"type": "number",
"const": 150
},
{
"type": "number",
"const": 300
},
{
"type": "number",
"const": 500
}
],
"description": "Cap on the data provider's own rows per seed. Defaults to 150. It is NOT a cap on `rowCount`: free Google Suggest ideas are appended after the cap is applied, so a seed can legitimately report more rows than this number."
},
"includeClickstreamData": {
"type": "boolean",
"description": "Refine search volumes with clickstream data, which disaggregates Google Ads' grouped close-variant volumes (plurals/misspellings). On the paid DataForSEO provider this DOUBLES the credit cost of each seed. Default false (standard Google-Ads-derived volumes). Ignored — no cost, no effect — for countries served from Google Ads data, and on the free data provider, whose autocomplete + Keyword Planner sources have no clickstream refinement at all."
}
}
}{
"type": "object",
"required": [
"projectId",
"brand"
],
"properties": {
"brand": {
"type": "string",
"maxLength": 250,
"minLength": 1,
"description": "Brand, domain, or product name to scan."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"competitors": {
"type": "array",
"items": {
"type": "string",
"maxLength": 250,
"minLength": 1
},
"maxItems": 5,
"description": "Up to 5 competitor brands/domains to compare Share of Voice against."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
}
}
}{
"type": "object",
"required": [
"projectId",
"name",
"address",
"phone",
"latitude",
"longitude",
"radiusKm"
],
"properties": {
"name": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Claimed business name."
},
"phone": {
"type": "string",
"maxLength": 50,
"minLength": 1,
"description": "Claimed phone number, any format."
},
"address": {
"type": "string",
"maxLength": 500,
"minLength": 1,
"description": "Claimed address, any format."
},
"latitude": {
"type": "number",
"maximum": 90,
"minimum": -90
},
"radiusKm": {
"type": "number",
"maximum": 100000,
"minimum": 1,
"description": "Search radius around the claimed location, in km."
},
"longitude": {
"type": "number",
"maximum": 180,
"minimum": -180
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
}
}
}{
"type": "object",
"required": [
"projectId",
"keywords"
],
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string",
"maxLength": 64,
"minLength": 1
},
"maxItems": 20,
"description": "Optional tags to attach to every saved keyword. Ask the user for explicit confirmation before using this, especially when saving many keywords or creating new tag names."
},
"keywords": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 100,
"minItems": 1,
"description": "Keywords to save (1-100)."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"languageCode": {
"type": "string",
"minLength": 2,
"description": "Language code (e.g. 'en', 'es', 'fr'). Omit it to use the project's default market language, which falls back to 'en'."
},
"locationCode": {
"type": "integer",
"description": "DataForSEO location code. Omit it to use the project's default market, which falls back to 2840 (United States) for a project that has not set one. See dataforseo.com/help-center/locations. Some countries (e.g. Iceland, 2352) are served from Google Ads data: keyword volume/CPC/trends work, but keyword difficulty, search intent, and domain analytics are unavailable.",
"exclusiveMinimum": 0
}
}
}| categories | array | Business categories to filter by (e.g. 'pizza_restaurant'). |
{
"type": "object",
"required": [
"projectId",
"near"
],
"properties": {
"near": {
"type": "object",
"required": [
"latitude",
"longitude",
"radiusKm"
],
"properties": {
"latitude": {
"type": "number",
"maximum": 90,
"minimum": -90,
"description": "Latitude of the search center."
},
"radiusKm": {
"type": "number",
"maximum": 100000,
"minimum": 1,
"description": "Search radius around the center, in kilometers. get_google_business_questions clamps it to roughly 0.2-200 km and ignores it entirely on the free provider — see that tool's description before reporting a radius as applied."
},
"longitude": {
"type": "number",
"maximum": 180,
"minimum": -180,
"description": "Longitude of the search center."
}
},
"description": "Coordinate and radius to search around."
},
"limit": {
"type": "integer",
"maximum": 50,
"minimum": 1,
"description": "Maximum businesses to return (1-50). Defaults to 20."
},
"query": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "Business name or title text to match."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"categories": {
"type": "array",
"items": {
"type": "string",
"maxLength": 120,
"minLength": 1
},
"maxItems": 10,
"minItems": 1,
"description": "Business categories to filter by (e.g. 'pizza_restaurant')."
}
}
}{
"type": "object",
"required": [
"projectId",
"action",
"url",
"entityType",
"requestType"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "The URL (or directory) to block/unblock."
},
"date": {
"type": "string",
"description": "ISO-8601 date for the block. Defaults to now."
},
"action": {
"enum": [
"add",
"remove"
],
"type": "string",
"description": "add: block this URL from search results. remove: undo a previous block."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"entityType": {
"anyOf": [
{
"type": "number",
"const": 0
},
{
"type": "number",
"const": 1
}
],
"description": "0 = a single page, 1 = a directory."
},
"requestType": {
"anyOf": [
{
"type": "number",
"const": 0
},
{
"type": "number",
"const": 1
}
],
"description": "0 = CacheOnly (keep out of the cache, still crawlable), 1 = full block."
}
}
}| configId* |
| string |
| Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs. |
| projectId* | string | Required. The MisarSEO project ID to scope this call to. Get one from list_projects. |
| trackingKeywordIds* | array | Tracking-keyword IDs to tag or untag (1–500 per call). These are the rank tracker's own keyword-row IDs — not saved-keyword IDs, and not keyword text. Duplicates are collapsed, and IDs belonging to another tracker are ignored rather than errored. |
{
"type": "object",
"required": [
"projectId",
"configId",
"action",
"trackingKeywordIds"
],
"properties": {
"tagId": {
"type": "string",
"minLength": 1,
"description": "Existing tag ID from list_rank_keyword_tags. Pass this OR `tagName`, never both."
},
"action": {
"enum": [
"add",
"remove"
],
"type": "string",
"description": "'add' attaches the tag, creating it from `tagName` when it does not exist yet; 'remove' detaches it."
},
"tagName": {
"type": "string",
"maxLength": 64,
"minLength": 1,
"description": "Tag name, matched on its normalised form — 'Branded', 'branded' and ' branded ' are one tag. Created on 'add' when it does not exist; NOT_FOUND on 'remove'. Pass this OR `tagId`, never both."
},
"configId": {
"type": "string",
"minLength": 1,
"description": "Rank tracker config ID. Call get_rank_tracker without a trackerId to list the project's trackers and their IDs."
},
"projectId": {
"type": "string",
"minLength": 1,
"description": "Required. The MisarSEO project ID to scope this call to. Get one from list_projects."
},
"trackingKeywordIds": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 500,
"minItems": 1,
"description": "Tracking-keyword IDs to tag or untag (1–500 per call). These are the rank tracker's own keyword-row IDs — not saved-keyword IDs, and not keyword text. Duplicates are collapsed, and IDs belonging to another tracker are ignored rather than errored."
}
}
}