Skip to main content

What is MCP?

The Model Context Protocol (MCP) is an open standard that lets AI assistants connect to external tools and data sources. Searchable’s MCP server lets you ask AI assistants questions about your AI visibility, site audits, and content — without leaving the assistant. Compatible clients include:
  • Claude (Claude.ai, Claude Desktop and mobile) and Claude Code — add as a connector
  • ChatGPT, Codex and dots — via ChatGPT’s plugin directory; Codex also from its CLI
  • Gemini — via custom Connected Apps; Gemini CLI — via the Searchable extension
  • Grok — via custom MCP connectors
  • Cursor, VS Code, Windsurf / Devin and Muse Code
  • Microsoft 365 Copilot agents and Microsoft Copilot Studio
  • Slack and Microsoft Teams agents — through Claude Tag, Slackbot, Copilot Studio and Microsoft 365 Copilot; see Use Searchable in Slack and Microsoft Teams
  • Perplexity Enterprise, Mistral, and other MCP-compatible clients
The assistant controls whether custom MCP servers are available for your account, plan, and region. Gemini custom apps currently require a personal Google Account in the US, age 18 or over, English, and Keep Activity turned on. For Microsoft, this compatibility applies to Microsoft 365 Copilot agents and Copilot Studio — not the consumer Microsoft Copilot chat product, which does not currently document a bring-your-own MCP server flow.
Ask questions like “What’s my brand’s visibility score on ChatGPT?” or “What critical AEO issues does my site have?” and the assistant will query Searchable for live data.
Plan requirement: MCP integration requires a paid Searchable plan. Both connection methods — signing in with OAuth and API keys — are unavailable on the Free plan.

How authentication works

The Searchable MCP server uses OAuth 2.1 with PKCE. It supports the current MCP client-metadata discovery flow, plus Dynamic Client Registration for clients that still require it. Public clients use PKCE; compatible enterprise clients can use a dynamically issued client secret. This means a new standards-compatible assistant can connect without Searchable adding a provider-specific OAuth integration first. You do not paste an API key into your MCP client’s config — you sign in and approve access, just like connecting any other app:
  1. You add the server URL to your client (https://app.searchable.com/api/mcp-server/mcp).
  2. On first use, the client opens Searchable in your browser and asks you to log in (or reuses your existing session).
  3. A consent screen shows which application is connecting and lets you choose:
    • Which projects it can access — only the projects you can open in Searchable are listed; pick specific ones, or leave all selected (all also covers projects you create later).
    • Whether to turn on Allow write access. It is off by default, so the connection stays read-only until you switch it on. Projects you can only view are marked View only, and when every selected project is view-only the switch is not offered at all: the connection is read-only.
  4. You click Authorize. Your client receives an OAuth access token scoped to exactly what you approved and uses it for subsequent requests.
The token can never exceed what you granted on the consent screen — a read-only, single-project grant stays read-only and single-project.
Only connect the Searchable MCP from trusted clients. Grant the narrowest access you need — read-only, and only the projects the assistant should see.

Setup

Server URL: https://app.searchable.com/api/mcp-server/mcp — the same address for every client below.

Step 1: Add the server to your client

Chat assistants

Works in Claude.ai and the Claude Desktop app:
  1. Open Customize → Connectors.
  2. Click + Add, then Add custom connector.
  3. Name it Searchable and enter the URL: https://app.searchable.com/api/mcp-server/mcp
  4. Click Continue, keep the recommended sign-in options, and click Add. Claude opens the Searchable sign-in and consent page — continue with Step 2.
On a Team or Enterprise plan, an Owner first adds the connector under Organization settings → Connectors; each member then connects it under Customize → Connectors.

Coding tools

Add the server from your terminal:
Then inside a session run /mcp, pick searchable → Authenticate — your browser opens the Searchable sign-in and consent page. Continue with Step 2.Or install the Searchable plugin, which adds the same server:
A Searchable connector you added in Claude.ai also appears in Claude Code when you sign in to Claude Code with your Claude account.
Slack and Teams agents (Claude Tag, Slackbot, Copilot Studio and Microsoft 365 Copilot) have their own steps in Use Searchable in Slack and Microsoft Teams. For Perplexity, Mistral, or another hosted assistant, or if a client’s menus no longer match the steps above, create a custom MCP server or connector in that product and use https://app.searchable.com/api/mcp-server/mcp. Searchable publishes OAuth discovery metadata, so choose OAuth or automatic authentication when the host asks. The exact menu name and availability are controlled by the host product.

Step 2: Log in and authorize

The first time your client connects, Searchable opens in your browser:
  1. Log in to Searchable (or continue with your existing session).
  2. On the consent screen, choose which projects the client may access. Leave Allow write access off (the default) for read-only access, or switch it on to allow write tools.
  3. Click Authorize. Your browser returns to the client, now connected — no API key to copy or paste.
Your login is the identity for this flow. To connect a script or a client that only accepts a static bearer token, use the legacy API-key path below instead.

Step 3: Verify the connection

Once connected, ask:
You should see a list of your projects with their names and domains.

Connect with an API key (legacy)

The OAuth login flow above is the recommended path for interactive MCP clients. For scripts, headless clients, or any client that only accepts a static bearer token, a Searchable API key works as a bearer token directly — the same sea_ key the REST API accepts.
1

Create an API key

In Manage Account → Integrations → MCP → Access tokens, click Generate access token, name it, and copy the token (starts with sea_). Store it securely — you won’t see it again. A token can cover all your projects or be limited to a single project. It’s read-only unless you turn on Allow write access, which lets it use every write tool, including the ones that need a capability. A token’s access can’t be changed later, so create a new one to add write access.
2

Send it as a bearer token

Configure your client to send Authorization: Bearer sea_... on every request to https://app.searchable.com/api/mcp-server/mcp. No login or consent screen is involved on this path.

Available tools

The Searchable MCP exposes 49 primary tools — 30 read-only and 19 write (51 with the Associated Sources feature enabled), plus 4 deprecated names kept callable through their sunset window (see Deprecated tool names). Every read tool is annotated readOnlyHint: true. Every tool that operates on a project takes a required projectId (get IDs from list_projects), and most read tools also accept an optional response_format — "concise" (default, summary + capped rows) or "detailed" (fuller rows, same shape).
Two kinds of write tool. The original three (Write actions below) need at most the write scope from your connection (generate_report only read). The write tools under Project and prompt management, and import_prompts, each need a connection minted with a specific named capability instead (e.g. prompt:draft, knowledge:manage) — a plain read-and-write connection does not automatically reach any of them. An API key carries exactly the scopes it was created with: write and every capability when Allow write access was on, read-only otherwise. An OAuth connection gets a capability only when the connecting client asked for that exact capability at authorization and you granted it on the consent screen: Allow write access grants every capability the client asked for, and Customise permissions picks them one by one. Calling one without the right capability returns missing_scope, distinct from forbidden (the connection has the capability, but your own role in the workspace doesn’t allow the action) and not_found (the project isn’t visible to this connection at all, or the feature isn’t enabled for it) — the three read differently on purpose, so you know which of “wrong grant,” “wrong role,” or “wrong project” to fix.
The server also publishes two resources — searchable://glossary (compact index) and searchable://glossary/full (complete definitions) — metric and concept definitions with the tools that read each one, for hosts that attach resources as reference context. get_visibility, get_share_of_voice, and get_sentiment additionally accept compare: "previous_period" | "previous_year" — the response gains a comparison block with the previous window’s metrics and current-minus-previous deltas, so period-over-period questions are one tool call.
Absolute date ranges. The core analytics reads accept from and to (inclusive, UTC) as an alternative to the relative days — from: "2026-03-01", to: "2026-03-31" answers “how did March go” without arithmetic against today. Both bounds are required together, and they are mutually exclusive with days: sending both is an invalid_argument error rather than a silent precedence rule. Bare YYYY-MM-DD and full ISO timestamps are both accepted (the UTC day is what counts). An over-long days is clamped to the tool’s maximum, but an over-long explicit range is rejected — narrowing a range you spelled out would answer a different question than the one you asked. Supported on get_visibility (every group_by), get_share_of_voice, get_sentiment, get_topic_analysis, get_ai_traffic (every report), and investigate, plus the matching REST endpoints. Tools not in that list — get_competitors, get_shopping_visibility, get_ads, get_page_metrics, get_query_fanout, get_prompt_answers, and the source reads — still take days only. (get_gsc_performance has always had its own startDate / endDate.)
Filter echo. Every filterable read echoes an appliedFilters object: the platform / brand / topic / location filters that call actually narrowed by, or {} when it was unfiltered. That covers get_visibility, get_share_of_voice, get_competitors, get_topic_analysis, list_prompts, get_sentiment (every view), get_source_trends, and get_shopping_visibility (summary view). All of them except get_topic_analysis also return availableFilters (the project’s topics and countries) so a client can offer the next filter without a second round-trip — on get_visibility that rides along on the summary and platform views, on get_share_of_voice on the summary view, and on get_competitors on the list view. Use appliedFilters to state what a number covers rather than presenting a filtered figure as the whole picture.
Write actions are MCP tools too: trigger_audit and refresh_sitemap are available to keys/grants with the write scope, and generate_report to any connection, read-only included, as sharing a report is in the app. generate_report and refresh_sitemap require an explicit confirm: true argument — without it they return a dry-run preview and change nothing. trigger_audit starts straight away, as the app’s button does; pass dryRun: true to preview it first. The REST equivalents (e.g. POST /api/mcp/projects/{projectId}/reports) remain available; see Advanced API Usage.

Orientation

Four tools exist purely to make assistants reliable, and are worth calling early in a session. None of them takes a projectId.

get_current_date

The server’s current UTC date and time (utcDate, utcDateTime, dayOfWeek, isoWeek) plus pre-computed window starts — yesterday, last7Days, last30Days, last90Days, last365Days. Call it before interpreting relative time like “last month” so date math is never guessed. Needs no scope.Parameters: none

whoami

Who this connection is authenticated as, its scopes, whether it is limited to specific projects, the workspaces it can reach, and the project roster it can see. Each workspace lists your role, whether you can create projects there (canCreateProjects), and the ids of its projects. Call it when unsure what the connection can access, and before create_project, which needs one of those workspace ids. Over REST, GET /api/mcp/workspaces returns the same list.Parameters: none

search_docs

Keyword search over this documentation, so questions like “how is the visibility score calculated?” are answered in-chat, from the source. Returns each hit’s slug, title, description and a snippet — follow up with read_doc. Needs no scope.Parameters: query (required), limit (default 8, max 25)

read_doc

One documentation page by its slug (from search_docs, e.g. recipes/visibility-drop), optionally narrowed to one heading with section. A section that doesn’t match returns the whole page with sectionNotFound and the page’s availableSections; an unknown slug returns not_found. Needs no scope.Parameters: slug (required), section (optional)

Diagnostics

investigate

Answers a whole diagnostic question in one call, by running its multi-read chain server-side and in parallel. Prefer it over chaining the granular tools yourself for “why did X change” and “where do we stand” questions.question="visibility_drop" (default) returns the score trend with its largest report-to-report fall (spanDays says how many days that covers when reports aren’t daily), per-platform movement against the previous window, the share-of-voice shift with which competitors gained, and the five weakest of the ten topics you’re mentioned in most. "competitive_position" returns your standing with ranks and deltas, your score trend, and up to five topics — of the 15 you’re mentioned in most — where a rival currently leads. "citation_gap" returns your citation trend, the domains AI answers actually cite in your space, and per-engine citation share.Takes the usual narrowing arguments — days or from/to, platform, topicId, unbranded / branded, country / locationId — but not every part of the answer honors all of them: the score trend takes only unbranded / branded, and the topic figures ignore topicId and location.It reports measurements, never a cause. It will tell you ChatGPT fell 38 points while Gemini held; it will not tell you why, because the reason is usually outside the data — a rebrand, a campaign ending, a competitor launch. Every response carries an interpretation list naming what the numbers cannot settle. A signal whose upstream failed appears in unavailable with a reason rather than as a zero, so missing data is never mistaken for a finding.Each question has a companion recipe explaining how to read the result and when it misleads: visibility drop, competitive position, citation gap. Load one in-chat with read_doc("recipes/visibility-drop").Parameters: projectId (required), question ("visibility_drop" by default, "competitive_position", or "citation_gap"), days (default 30, max 365), from/to, platform, topicId, unbranded, branded, country, locationId (all optional except projectId)

Projects

list_projects

List all projects this connection can access — names, domains, IDs, and dataAvailableFrom (the earliest date with visibility data, null while a project has none yet). Archived projects are listed too, marked “(archived)”. Call this FIRST: every project-scoped tool needs a projectId, and dataAvailableFrom tells you how wide a window is worth requesting.Parameters: response_format (optional)

Visibility & share of voice

get_visibility

AI visibility for a project. group_by selects the view: summary (overall visibility score), platform (per ChatGPT/Claude/Gemini/Perplexity breakdown), prompt (per-prompt breakdown, paginated), location (per country/city), or date (the per-report time series — trend, uplift, before/after analysis; default 90-day window). The unbranded/branded, topicId, country and locationId filters work on summary, platform, prompt and location, and date accepts only unbranded/branded (anything else is an invalid_argument). The platform filter narrows only group_by=prompt|location — on summary/platform the figures stay all-platform. An interactive Visibility Snapshot card also renders automatically beside the answer on Apps-capable clients — see below.
group_by="topic" was removed. Per-topic visibility now lives on its own tool, get_topic_analysis (next card), which returns the competitive rank, strengths, and gaps the old view never had. Passing group_by="topic" is now a validation error — switch the call to get_topic_analysis.
include attaches optional extra blocks (comma-separated): industry_ranking adds the competitive leaderboard — the top 25 entities by visibility, with your own rank, share of voice, and change — to group_by=summary|platform (hasMore flags a longer field; brandRankStatus distinguishes ranking below the cutoff from being unranked, and totalEntities is null when truncated); annotations adds the dated markers (campaign launches, migrations) overlapping the window to group_by=date, so a spike comes back with its explanation attached. An include a view cannot attach is an invalid_argument error on every view — including group_by=prompt and group_by=location, which attach no blocks at all — never a response that quietly lacks the block.Parameters: projectId (required), group_by ("summary" | "platform" | "prompt" | "location" | "date", default summary), days (default 30, or 90 for group_by=date; max 365, or 180 for group_by=prompt), from/to (same maximums), include (industry_ranking on summary/platform, annotations on date), compare (summary/platform only), unbranded, branded, platform (group_by=prompt|location), topicId, limit (group_by=prompt, default 100, max 500), offset (group_by=prompt), country, locationId, response_format, render (all optional except projectId)

get_topic_analysis

Per-topic AI visibility — the brand’s mention rate in each topic, its competitive rank within that topic, how many of its tracked prompts were answered in the window, and the AI-identified attribute strengths and gaps behind the number. This is the only per-topic view: use it for any question about topics, subject areas, or categories, including where a brand is winning or losing and what to fix. Replaces the removed get_visibility (group_by:"topic"). An interactive Topic Analysis card also renders automatically beside the default view=topics answer on Apps-capable clients — see below.view="prompts" drills into ONE topic (topicId required): every prompt assigned to it with your average rank, its visibility, and which competitors outrank you — the “which specific questions are we losing” view behind a weak topic. A prompt you never appeared for reports rank: null, never 0.view="heatmap" returns average brand position per topic x AI platform — where you’re strong on one engine and absent on another. There is deliberately no platform filter on this view: the platform axis is the answer.Parameters: projectId (required), view (topics default, prompts, heatmap — platform is rejected on heatmap, whose axis IS platform), topicId (required for view=prompts, optional on heatmap; rejected on the default view, which lists every topic), days (default 30, max 365), from/to, limit (view=topics, default 15, max 50 — keeps the topics with the most brand mentions), platform, unbranded, branded, response_format, render (all optional except projectId)

get_visibility_history

Deprecated — call get_visibility with group_by: "date" instead (same data, same parameters). This name keeps answering with an identical payload until its removal date; see Deprecated tool names.Parameters: projectId (required), days (default 90, max 365), unbranded, branded, response_format (all optional except projectId)

get_share_of_voice

Brand vs competitor share of voice — mention-share percentage and rank among all tracked entities (the brand plus the top 50 competitors; totalCompetitors gives the full count), plus each entity’s day-over-day (today vs yesterday) point change. mentions/citations aren’t part of this view (mirrors the in-app Share of Voice sheet) — use get_competitors for those. An interactive Share of Voice card also renders automatically beside the answer on Apps-capable clients — see below.group_by="date" returns the daily share series instead — your share and your top 10 competitors’, one point per day. That is the “is our share growing?” view; include="annotations" adds the dated markers explaining any jump. compare is not supported there (the series already shows the movement), and the interactive card hides itself on it.Parameters: projectId (required), group_by (summary default, or date), days (default 30, max 365), from/to, include ("annotations", group_by=date only — any include on the summary is an invalid_argument), platform, topicId, unbranded, branded, country, locationId, compare (summary only), response_format, render (all optional except projectId)

Competitors

get_competitors

The project’s real/tracked competitors, ordered by rank (highest visibility first) — visibility, share of voice, sentiment score, average mention position, all-time mention count (mentions) and in-window mention count (mentionsInWindow), plus a brand block carrying the same metrics for the project itself so the two sit on one ruler. Each row’s id is what update_competitor takes. Pass competitorId for a head-to-head against one competitor instead: headToHead.brand vs headToHead.competitor, each with visibility %, share of voice %, sentiment (0–100) and average position (a rank — lower is better), plus the phrases each is known for; for the competitor it also returns its rank, per-day history, and up to 10 recent prompts it was mentioned in. competitorId accepts either the competitor’s ID or its name exactly as the list shows it (competitorId: "Nike"), so you can drill in straight from the list. A null metric means unmeasured in that window, not zero. visibility/sov/sentimentScore/avgPosition/mentionsInWindow honor the filters; the list view’s mentions (the brand block’s included) is all-time and ignores them. On the head-to-head, citations (inline citations on responses mentioning the competitor) and history[].domainSources (source URLs — pages used for generation, not inline citations — on the competitor’s own domain) are different metrics; history[] honors days only, and the competitor’s mention and citation totals cover at most its 500 most recent in-window mentions. An interactive Competitor Ranking card also renders automatically beside the answer on Apps-capable clients, in whichever shape matches the call — see below.Parameters: projectId (required), competitorId (optional — ID or name; switches to the head-to-head view), days (default 30, max 365), platform, topicId, unbranded, branded, country, locationId, limit (list view, default 20, max 100), offset (list view), response_format, render (all optional except projectId)

Sources & citations

search_sources

Top cited source domains from AI answers — rank, citation counts, usage %, models, and top content types. Paginated and sortable. Use to find which external sites AI models cite most; drill in with get_source_detail.Parameters: projectId (required), sort (citations | usage | avgCitations, with a - prefix for descending; default "-citations"), limit (default 20, max 1000), offset, days (default 30; rounded up to 7, 30, 90 or 365), platform, topicId, sourceType (comma-separated: brand, competitor, editorial, social, forum, review, institutional, third_party, other), unbranded, branded, country, locationId, response_format (all optional except projectId)

get_source_detail

Drill into cited sources. level="domain" (+ domain): domain summary, or include="urls" (cited URLs on it) / include="responses" (AI responses citing it). level="url": with url → single-page citation analytics, or include="content" → cached page markdown + entity mentions (never triggers a live scrape); without url → all cited URLs across the project (optionally filtered by contentType). A missing domain (or a missing url with include="content") returns not_found.Parameters: projectId (required), level ("domain" | "url", default domain), include ("urls" | "responses" on level=domain; "content" on level=url), domain (required for level=domain), url (level=url; with include="responses" it narrows to responses citing that one page), contentType, limit (default 20, max 1000), offset, cursor, days (default 30; rounded up to 7, 30, 90 or 365), platform, topicId, sourceType, unbranded, branded, country, locationId, response_format (all optional except projectId)

get_source_trends

How the project’s top source domains trend over time — a per-domain time-series of the external sites most used as sources in AI answers, over the window (default 30 days). An interactive Sources card renders beside the answer on Apps-capable clients: the trend line inline, expanding to the top source URLs.Parameters: projectId (required), days (window; default 30 — pass 7/90/365 for a different one), platform, topicId, sourceType, unbranded, branded, country, locationId, response_format, render (all optional except projectId)

Sentiment

get_sentiment

Brand sentiment across AI platforms. view selects: summary (default) — overall score + positive/neutral/negative split + per-platform breakdown, plus analysis (the weekly brand-perception write-up for the platform that mentions you most, or the one you filter to with platform) and analyses (every platform’s write-up, or only the filtered platform’s when platform is set); history — the daily sentiment trend (default 90-day window); competitors — head-to-head vs competitors, the brand flagged by isBrand. topicId is not accepted on competitors, compare only on summary, and include (annotations) only on history; each is an invalid_argument elsewhere. The interactive Sentiment Profile card renders on summary answers only.Parameters: projectId (required), view ("summary" | "history" | "competitors", default summary), days (default 30, or 90 for view=history; max 365), from/to, platform, topicId, limit (view=competitors, default 10, max 50), include ("annotations", view=history only), compare ("previous_period" | "previous_year", view=summary only), unbranded, branded, country, locationId, response_format, render (all optional except projectId)

get_sentiment_history

Deprecated — call get_sentiment with view: "history" instead (same data, same parameters). Keeps answering identically until its removal date; see Deprecated tool names.Parameters: projectId (required), days (default 90, max 365), platform, topicId, unbranded, branded, country, locationId, response_format (all optional except projectId)

get_sentiment_competitors

Deprecated — call get_sentiment with view: "competitors" instead (same data, same parameters). Keeps answering identically until its removal date; see Deprecated tool names.Parameters: projectId (required), days (default 30, max 365), platform, limit (default 10, max 50), unbranded, branded, country, locationId, response_format (all optional except projectId)

Query fanout

get_query_fanout

The sub-queries AI models generate when answering tracked prompts. Without prompt_id: the project aggregate (most frequent fanout queries, per-prompt counts, per-model stats; paginated). With prompt_id: every deduped sub-query for that one prompt, with per-query frequency and query type. Paid feature — returns plan_upgrade_required if not entitled.Parameters: projectId (required), prompt_id (optional — note the snake_case; the prompt’s id from list_prompts, a unique id prefix, or its text; omit for the project aggregate), days (default 30, max 180), platform, topicId, unbranded, branded, limit (aggregate, default 50, max 50), offset (aggregate; rounded down to a multiple of limit), topQueriesLimit (aggregate, default 50, max 200), response_format (all optional except projectId)

Brand profile & authority

get_brand_profile

The project’s knowledge-base brand profile (name, website, headline, category, positioning, description, topics, business type, reach, language, exact match, disambiguation note, connected entities, competitor geography/context) plus the latest batch of AI-observed brand facts — statements AI platforms actually make about the brand, each with its platform and verification status. Pass include: "domain_authority" to also get the Moz domain-authority block (latest DA/PA/spam/linking-domains snapshot + roughly-monthly 365-day history) in the same call. Call this first for brand context in any analysis. profile is null until onboarding creates one; facts come from the weekly sentiment job.Parameters: projectId (required), platform, verificationStatus ("unverified" | "pass" | "fail"), include ("domain_authority"), response_format (all optional except projectId)

get_domain_authority

Deprecated — call get_brand_profile with include: "domain_authority" instead (same data; the REST endpoint keeps custom days windows). Keeps answering identically until its removal date; see Deprecated tool names.Parameters: projectId (required), days (default 365, max 1095) (all optional except projectId)

Prompts

list_prompts

The project’s prompt catalog as configured — tracked prompts (type, intent, branded flag, topics, tracked locations, keyword volume/difficulty), paused (untracked) prompts, or the AI-suggested review queue (status="suggested" — proposals from research runs and the agent that nobody has accepted or rejected yet, each with the reason it was proposed). Configuration data, not performance — use get_visibility group_by=prompt for per-prompt visibility scores. The summary always reports all three counts, so pending suggestions surface from any call. Tracked and paused rows carry each prompt’s full id, which the prompt-scoped tools accept. An interactive Prompts card also renders automatically beside the answer on Apps-capable clients.Parameters: projectId (required), status ("tracked" | "untracked" | "suggested" | "all" — tracked + untracked, never suggested; default tracked), topicId, search (case-insensitive substring of the prompt text), limit (default 50, max 1000), offset, days (metrics window, default 30, max 365), sortBy (prompt | volume | difficulty | branded | intent | recency, default recency — performance columns like visibility/position/sentiment are computed per page and are not server-sortable), sortDir ("asc" | "desc", default desc; the default recency sort is always newest first), unbranded, branded, response_format, render (all optional except projectId; status="suggested" honors only topicId, search, limit and offset, newest first)

get_prompt_answers

Raw, per-response AI-answer data for one prompt (Profound-parity) — platform, response date, response text, whether the brand was mentioned, competitors mentioned, and the source URLs the engine consulted (sources[] — pages used for generation, not inline citations), one entry per AI response. Each answer also shows the exact wording it was asked (asked_as, location included), which can differ from the prompt’s current wording after an edit; an older answer whose wording was not recorded is marked when the prompt has been reworded since. Not an aggregate — use get_visibility or get_share_of_voice for scores/rankings. The default concise table shows a 120-character snippet per answer; response_format: "detailed" prints every answer in full (inline base64 images removed, 50,000-character safety cap). promptId takes the prompt’s id from list_prompts, a unique id prefix, or the prompt’s text (matched ignoring case, spacing and trailing punctuation, and also when cut short with a trailing … the way list_prompts and the other tables print it); text matching several prompts returns invalid_argument listing their ids, and one matching none returns not_found.Parameters: projectId (required), promptId (required — note the camelCase, unlike get_query_fanout’s prompt_id), days (default 30, max 365), platform, limit (default 10, max 50), offset (pass the previous call’s nextOffset to page), response_format (all optional except projectId and promptId)

Site health & audits

get_site_health

Technical + AEO site health. view="pages" (default) = the monitored-page inventory, one server-paginated page at a time (audited pages first) — every page (audited or not) with its latest technical/AEO scores, open-issue count, and last-audited date, plus pagination.total; page with limit/offset, filter server-side with search (URL/slug/title), reorder with sortBy, and omit the header aggregates with includeSummary=false; view="audits" = per-page technical/AEO scores for audited pages + averages; view="issues" = issues grouped by severity (filter status); view="issue_details" = open issues grouped by type with fix guidance (filter issueType; covers the first 1,000 open issues). An interactive Site Audit card renders beside the answer on Apps-capable clients: pages/audits show the inventory with a search box + Prev/Next paging (both server-side), a per-page Audit button (calls trigger_audit for that page), a per-page Issues button (that page’s open and resolved issues), and a Sync website button (calls refresh_sitemap); issues/issue_details show the findings ranked by severity, each expanding to its description and affected pages (plus fix guidance on issue_details).Parameters: projectId (required), view ("pages" | "audits" | "issues" | "issue_details", default pages), status (view=issues: "open" | "resolved" | "all", default open), pageId (view=issues / issue_details: scope to one monitored page — the card’s Issues button), issueType (view=issue_details: "technical" | "content"), includeIssues (view=audits, boolean), search (view=pages: server-side filter by URL / slug / title), sortBy (view=pages: "audited" | "technical" | "aeo" | "issues", default audited-first), includeSummary (view=pages, boolean, default true), limit (view=pages default 12, max 100; view=issues default 1000, max 5000), offset (view=pages / view=issues), response_format, render (all optional except projectId)

Content & articles

list_articles

List articles / content pieces for a project (merges content-v2 + legacy), most recently updated first. Shows title, status, type, word count, and source, plus total and per-status counts. Not paginated: one call returns at most 100 rows.Parameters: projectId (required), status ("draft" | "published" | "editing" | "all", default all — all excludes archived pieces), limit (default 50, max 100), response_format (all optional except projectId)

get_article

Full article content — HTML/markdown body, SEO metadata (meta title and description, OG image, schema markup), FAQs, internal links, and all metadata. Legacy articles also carry an outline, TL;DR, summary, alt text, and external links; content-v2 pieces return those empty.Parameters: articleId (required — the id from list_articles)

Opportunities

get_opportunities

Actionable opportunities Searchable surfaced for the project — prioritized fix / write / audit / setup suggestions derived from visibility, mentions, sentiment, sources, traffic, prompts, prompt research, site health, articles, and platform setup. Returned in the product’s priority order (status, then impact, then newest). Defaults to active opportunities; blocked for active pitch projects. Each row carries its id, which update_opportunity_status takes.Parameters: projectId (required), status ("active" | "completed" | "dismissed" | "auto_resolved", default active only), includeResolved (boolean — with no status, returns every status), source ("visibility", "mentions", "sentiment", "sources", "traffic", "prompts", "prompt_research", "site_health", "articles", "platform"), impact ("critical" | "high" | "medium" | "low"), limit (default 100, max 500), offset, response_format (all optional except projectId)

GA4 / Traffic

This tool requires a linked GA4 property. When the project has none, the backing endpoint returns a 409 error with { code: "ga4_not_connected", message, howToFix } — the howToFix field points at Settings → Integrations in Searchable, where you can connect GA4 and then retry.

get_ga4_traffic

Google Analytics 4 website traffic. report="ai_referrals" (default; per-LLM-host AI Source Visitors from ChatGPT/Copilot/Gemini/Perplexity/DeepSeek and more — use for AI-referral attribution), "trend" (daily users/sessions/pageviews), "top_pages" (top landing pages by sessions), or "sources" (channel-group + source breakdown).Parameters: projectId (required), report ("ai_referrals" | "trend" | "top_pages" | "sources", default ai_referrals), weeks (default 12, max 52), limit (top_pages/sources, default 20, max 100), response_format (all optional except projectId)

Search Console (GSC)

This tool requires a linked Google Search Console site. When the project has none, the backing endpoint returns a 409 error with { code: "gsc_not_connected", message, howToFix } — the howToFix field points at Settings → Integrations in Searchable, where you can connect GSC and then retry.

get_gsc_performance

Live Google Search Console organic performance. report="overview" (default): clicks/impressions/CTR/average position plus a prior-period comparison. "top_queries" / "top_pages" / "countries" / "devices": ranked rows for that dimension. "trend": daily timeseries with window totals. "opportunities": quick-win queries (ranking position 4-15, ≥100 impressions, under 5% CTR), sorted by impressions and drawn from the site’s top 250–1,000 queries. "position_buckets": query-count distribution across top 3 / page 1 / page 2 / beyond 20, over the top 1,000 queries. Pass period ("7d"|"28d"|"30d"|"90d"|"16m", default "30d"; an unrecognized value falls back to "30d") or an explicit startDate+endDate (YYYY-MM-DD, both together, start on or before end — otherwise invalid_argument). GSC data lags ~3 days: an explicit range is clamped to the last day Google has published and to its 16-month retention, and the response’s window then carries clamped/requestedTo (or truncatedStart/requestedFrom).Parameters: projectId (required), report (default overview), period, startDate, endDate, limit (top_queries/top_pages/countries/opportunities only; defaults 25/25/100/20, max 250/250/250/100), response_format (all optional except projectId)

AI Traffic

First-party AI-traffic measurement from the tracker/CDN pipeline — Searchable’s flagship differentiator, distinct from GA4 above (which reads Google’s own analytics). Six get_ai_traffic reports (overview, crawlers, referrals, top_cited_pages, human, correlation) need any connected source — crawler logs, a CDN integration, the Searchable tracker, or a linked GA4 / Search Console / Bing site; sitemap_coverage, attribution, and logs need a crawler-log or CDN source specifically. When the required source is missing, the backing endpoint returns a 409 error with { code: "traffic_not_connected", message, howToFix } — the howToFix field points at AI Traffic → Setup in Searchable, where you can connect a source and then retry. get_page_metrics degrades instead of failing: a block it can’t read comes back as available: false with a reason.

get_ai_traffic

First-party AI-crawler and AI-referral traffic. report="overview" (default): crawler + AI-referral totals by platform, plus the top crawled pages. "crawlers": per-page AI-crawler activity, capped by limit (no deep pagination at the tool layer — use the REST endpoint’s offset for that). "referrals": daily AI-referral session timeseries by platform. "top_cited_pages": top pages driving AI-referral traffic for one platform — platform is required for this report. platform is validated strictly, and an unrecognized value returns an error rather than a silent empty result: crawlers and referrals accept all eight platform IDs (openai, anthropic, google, perplexity, microsoft, deepseek, xai, meta), while top_cited_pages accepts only the first five. platform filters only crawlers, referrals, and top_cited_pages — overview always returns every platform, and the five further reports below ignore it too, so filter with crawlers or referrals instead. Long-tail vendors that can appear in output rows (e.g. amazon, you.com) are deliberately not filterable.overview, crawlers, and correlation take groupBy="platform" | "botCategory" | "botName". This extends the same tool rather than adding traffic tools. Omitting it preserves the legacy response; botCategory adds byCategory, botName adds byBot (name, ID, vendor, and category), and platform adds correlation’s per-page byPlatform crawl split. Overview and crawlers keep their existing byPlatform arrays with every grouping.The public bot categories are ai_training, ai_search, ai_assistant, ai_agent, and search_engine. ai_assistant is the distinct user-triggered class for ChatGPT-User, Claude-User, and Perplexity-User. A per-page ai_training → ai_search → ai_assistant view is therefore possible, but ai_search means retrieval/search crawling — actual answer citation remains a separate visibility metric.Five further reports cover the rest of the AI Traffic surface:
  • sitemap_coverage — per bot (named, with vendor and category): how often it fetched your sitemap, how many listed pages it discovered, and the median lag from a page appearing in the sitemap to its first crawl. It answers how fast and how completely each crawler is picking your sitemap up; it does not enumerate individual uncrawled URLs.
  • human — real human sessions that arrived FROM an AI assistant, split by platform. The conversion half of the funnel the crawler reports open. Check summarySource: site-wide totals come from the first-party tracker while the AI figures fall back to GA4 when no tracker is installed — in that split state every summary field and percentOfTotal are null (not 0), and a failed GA4 read surfaces as aiReferral.sourceError rather than as zeroes.
  • correlation — per page, crawls and AI-referred sessions over the same window, joined by path. Its optional grouping classifies the crawl side only. This is co-occurrence, not attribution: nothing establishes that a session followed a crawl or came from a named bot. Ranked by sessions, but the ranked population is the upstream union of the top-500 crawled and top-500 human-traffic pages — pagesConsidered is a floor, and populationTruncated tells you when pages are certainly missing.
  • attribution — traffic by UTM tuple, referrer domain, and bot category. The underlying query stops at 100 rows; truncated: true marks totalRows as a floor.
  • logs — the raw request feed, filterable by path, statusCode, botsOnly, and host, cursor-paginated: pass a page’s nextCursor back as both cursorTimestamp and cursorEventId (over MCP, sending only one half is ignored and page one comes back again; the REST endpoint rejects it). Row-capped (25 default, 100 max) because it is the one row-level report. ip_address and user_agent are never returned — the IP is personal data with no analytical value once country is present, and the user-agent is superseded by the resolved bot identity.
host narrows any report to specific hostnames (comma-separated) — useful on multi-domain projects where “blog versus docs” is the question.Parameters: projectId (required), report (default overview), days (default 30, max 365), from/to, host, platform (required for top_cited_pages; used only by crawlers / referrals / top_cited_pages), groupBy (overview/crawlers/correlation only), limit (default 20 for crawlers, 10 for top_cited_pages, 25 for correlation / attribution / logs; max 100), plus path / statusCode / botsOnly / cursorTimestamp / cursorEventId for report=logs, response_format (all optional except projectId)

get_page_metrics

The per-URL rollup: one page’s AI citations (inlineCitations — answers linking it in their text — and sourceUses — answers that consulted it), AI-referral sessions by platform, and AI-crawler visits by bot, as daily buckets in one call. url takes a full URL or a bare path such as /pricing, resolved against the project’s domain. Each block degrades on its own to available: false with a reason — traffic_not_connected, external_url (the host isn’t one of the project’s verified domains), or citation_lookup_failed — and a page that was never cited returns zeros, not an error. An empty or invalid URL, or a bare path on a project with no domain, returns invalid_argument.Parameters: projectId (required), url (required), days (default 90, max 365), response_format (all optional except projectId and url)

Shopping visibility

Requires a plan with Shopping Analytics — every Brand and Agency plan, plus legacy Professional and Scale; Free and Starter don’t include it. On the default summary view, get_shopping_visibility returns available: false (not an error) when the project simply has no shopping/product data yet — that’s a data-presence state, not a plan or connection problem, and needs no setup step; there the plan gate only surfaces once there’s data to fetch. The timeseries view and a productId lookup check the plan first, and with no data return an empty series or not_found rather than available: false.

get_shopping_visibility

AI shopping/product visibility (Peec-parity) — products surfaced in AI shopping/product-carousel responses. view="summary" (default): ranked product list (rank, mentions, dominant platform) plus an activation-rate summary. view="timeseries": daily activation-rate/product-count trend. Pass productId (the id from a summary row — the product’s title) for full detail on one product instead: occurrences, vendor/pricing rows, a real per-platform breakdown, and up to 50 recent responses that surfaced it — productId overrides view. The summary view accepts the brand / topic / location filters and echoes appliedFilters + availableFilters. An interactive Shopping Shelf card also renders automatically beside the answer on the summary view for Apps-capable clients — see below.Parameters: projectId (required), days (default 30, max 365), platform (comma-separated; every view), productId (optional — switches to detail view; an unknown product returns not_found), view ("summary" | "timeseries", default summary), limit (summary view, default 50, max 100), offset (summary view), topicId, unbranded, branded, country, locationId (the brand / topic / location filters apply to the summary view only), response_format, render (all optional except projectId)

AI ads

Ad data appears once tracked prompts surface sponsored ad units. An empty result means either that nothing has been captured yet or that a filter matched nothing. topicId and locationId each take one ID — a name, a list, or anything else that isn’t a well-formed ID is refused with invalid_params, while a well-formed ID that matches nothing returns an empty result (promptId also takes the prompt’s text, and text matching no prompt returns not_found rather than an empty result). platform takes a comma-separated list: a mixed list silently returns just the recognized platforms, and a list with none recognized matches nothing. Retry without filters before reporting that a project has no ad data, and state that the retried figures are unfiltered. adCount/appearances count ads we scraped in AI answers, not ad-platform impressions, and commercial ad libraries don’t publish spend, so there are no cost or impression figures.

get_ads

Sponsored ads surfaced in AI answers. view="advertisers" (default): who is running ads, ranked by ad volume with share % and a competitor flag, plus your own brand’s ad share-of-voice and rank. view="creatives": the individual ad creatives (advertiser, headline, appearances, prompts) — narrow with advertiserKey, promptId (a prompt id or the prompt’s text), or search. view="prompts": which tracked prompts surface the most ads, with ad coverage %. view="timeseries": per-advertiser ad-frequency trend.Parameters: projectId (required), view ("advertisers" | "creatives" | "prompts" | "timeseries", default advertisers), days (default 30, max 365), filter ("all" | "competitors", view=advertisers), advertiserKey (view=creatives/timeseries), promptId + search (view=creatives), sortBy (promptText|adUnits|coveragePct|engines|totalResponses, default adUnits) + sortDir (view=prompts), limit (advertisers default 20/max 100, creatives 25/200, prompts 50/500), platform, topicId, locationId, unbranded, branded, response_format (all optional except projectId)

Write actions

trigger_audit and refresh_sitemap require a grant/key with the write scope; generate_report works on any connection. generate_report and refresh_sitemap don’t execute without confirm: true — omit it and the tool returns a dry-run preview (what would happen; for generate_report, whether your plan allows sharing and white-label) with no side effect. trigger_audit starts straight away; pass dryRun: true to preview it first (page count and audit quota).

generate_report

Generate + publish a shareable report and return its public URL. reportType=sentiment|visibility|combined (default sentiment). Works on a read-only connection, as sharing does in the app; the workspace’s sharing policy and a plan with report sharing still apply. Note the Only suffix on the brand filters here — unbrandedOnly/brandedOnly — unlike every read tool’s unbranded/branded.Parameters: projectId (required), reportType, timeRange ("7d"|"30d"|"90d"|"365d", default 30d), platforms, topicIds, locationIds, unbrandedOnly, brandedOnly, title, status ("published"|"draft", default published), whiteLabel (requires a white-label-entitled plan), confirm (all optional except projectId)

trigger_audit

Start a technical + AEO site audit for the project’s pages (all tracked pages by default, or the given pageIds); returns the job id. Results land on get_site_health (view="audits"). Needs write access to the project, as starting an audit in the app does (a workspace viewer gets forbidden). Consumes the monthly audit quota (one unit per page) and is capped per plan in pages per audit — over the cap returns bulk_limit_exceeded, so pass a smaller pageIds list. Unknown pageIds are skipped (no_pages when none remain). Pages already being audited are skipped too and counted in skippedInFlightCount; when every page is already being audited, nothing starts and it returns alreadyInProgress: true with pageCount: 0 and no job id. If another audit of the project is starting at that moment, it waits briefly, then returns an audit_lock_busy error and starts nothing; retry after a few seconds. The plan cap counts every requested page; the quota counts only the pages it starts. Blocked for pitch projects; an archived project returns project_not_runnable.Parameters: projectId (required), pageIds (optional — omit to audit every tracked page), dryRun (optional)

refresh_sitemap

Re-sync the project’s configured sitemap — re-discovers it and reconciles monitored pages (add/update/remove) in the background; returns the job id. Read the result with get_site_health (view="pages"). Needs write access to the project (a workspace viewer gets forbidden). Blocked for pitch projects; an archived project returns project_not_runnable.Parameters: projectId (required), confirm (optional)

Project and prompt management

Every write tool in this section needs a connection minted with the specific capability named on its card — see the note under Available tools above. create_project needs confirm: true: without it the call returns a preview of what would happen (and current quota state) and changes nothing. Every other write here acts straight away, as the app’s own controls do; pass dryRun: true to preview one first.

create_project

Create a project and set it up as the app’s Add project flow does: the project, its competitors, brand profile, default location and onboarding record. It counts toward the workspace’s project limit. The assistant interviews you first, one question at a time, each with an answer it suggests from the brand’s website or a web search: the brand name and summary, where the brand sells, its competitors (list your own or pick from its suggestions), other names it goes by, and whether to generate prompts. Say “use your suggestions” to skip the rest. The preview lists everything that will be saved. generatePrompts: true writes up to 50 tracked prompts in the background, as the app does, within your prompt allowance. Needs project:provision. A connection scoped to specific project(s) can’t use this tool even with that capability — creating a project is a workspace-level action outside what a project-scoped grant or key covers — and is refused with forbidden.Parameters: workspaceId (from whoami’s workspaces, one with canCreateProjects: true), domain, brandName, brandSummary, reach (global, national-global, nationwide, regional or local), country (ISO code, e.g. GB), competitors (1–20 website domains, or { domain, name } entries), generatePrompts (all required), city, language, brandNameDisambiguation, brandAliases, name (defaults to the brand name), description, confirm

archive_project / reactivate_project

Archive or reactivate a project, as the app’s Archive and Reactivate actions do. An archived project stops running its scheduled checks and keeps its data; list_projects marks it “(archived)”. A workspace keeps at least one active project, so archiving its only one is refused with last_domain, and a pitch is refused with pitch_not_supported. Both need project:manage and write access to the project (a workspace viewer gets forbidden).Parameters: projectId (required), expectedRevision (fails with stale_revision if the project has changed since), dryRun

add_prompts

Add up to 10 prompts in one call. They’re tracked straight away, as when you add prompts in the app: the project’s next scheduled run checks them, and they count toward your prompt allowance. The whole batch is checked against the allowance first; if it doesn’t fit, nothing is added (quota_exceeded), so archive some prompts first or add fewer. The prompts are then saved one at a time, so a later prompt that fails (for example on a topic id that isn’t one of the project’s) leaves the earlier ones added. Needs prompt:draft.Parameters: projectId (required), prompts (required — array of { text, intentCategory?, isBranded?, topicIds? }, 1–10; text up to 2,000 characters, intentCategory one of informational, commercial, transactional, navigational, post_purchase, topicIds up to 20 existing topic ids), dryRun

update_prompt

Edit a prompt’s wording, intent, branded flag, or keyword metrics. Does not archive or restore it — use archive_prompt / restore_prompt for that. Needs prompt:draft. Pass expectedRevision to fail with stale_revision instead of overwriting a concurrent edit; the REST endpoint behind this tool accepts the same guard as an If-Match header instead.Parameters: projectId, promptId (both required), text, intentCategory, isBranded, keywordSearchVolume, keywordDifficulty, expectedRevision, dryRun

update_prompts

Bulk-edit existing prompts in place: read them with list_prompts (each row carries its id and currentRevision), change what you need, and send up to 500 rows back. Each row names a prompt by promptId and gives the end state for any of text, isBranded, intentCategory, topics and locations; a field you leave out is left alone, and a row that changes nothing writes nothing. A reworded prompt keeps its id and past answers, and its old wording stays as a revision. Pass each prompt’s expectedRevision so one edited since you read it comes back as conflict instead of being overwritten. A dry run plans the edit but does not check the prompt allowance; adding locations to tracked prompts can still be refused with quota_exceeded when you apply. Result entries carry each prompt id trimmed and lower-cased, so match ids case-insensitively. Needs prompt:draft.Parameters: projectId, prompts (both required — array of { promptId, expectedRevision?, text?, isBranded?, intentCategory?, topics?, locations? }, 1–500; locations items are { country, city?, state? }, at most 5 per prompt), dryRun

archive_prompt / restore_prompt

Archive or restore a prompt, as the app’s Archive and Restore actions do. archive_prompt stops tracking it; its history and metrics are kept. restore_prompt tracks it again, within your prompt allowance. Both need prompt:activate — a different capability from prompt:draft, so a connection that can only add and edit prompts can’t archive or restore existing ones.Parameters: projectId, promptId (both required), expectedRevision, dryRun

restore_prompt_wording

Restore a prompt’s wording to an earlier revision by appending a new revision carrying that text — history is never rewritten in place, so restoring revision 1 after edits to 2 and 3 produces revision 4. Only wording changes; tracking, topics, and locations are untouched. Needs prompt:draft; expectedRevision is required, not optional.Parameters: projectId, promptId, revision, expectedRevision (all required), dryRun

get_prompt_revisions

A prompt’s wording history, newest first, with its current revision number. Bare read — no capability needed, and unaffected by the pitch freeze below (reads are never blocked, only mutations are).Parameters: projectId, promptId (both required), limit (default 50, max 200), beforeRevision
An inactive pitch project (declined / expired / archived) refuses every prompt write above and every brand-profile, competitor, and topic write below with INACTIVE_PITCH_PROMPT_MUTATION (HTTP 409 over REST) — it’s a frozen sales-review snapshot, not a live project. update_opportunity_status is different: an active pitch has no opportunities feature at all (pitch_not_supported; 403 over REST), matching get_opportunities. All of them also enforce your own role in the workspace — a connection without the tool’s capability gets missing_scope, and one belonging to a workspace Viewer gets forbidden rather than a silent no-op; add_competitors and update_competitor additionally require a manage-tier role (Owner/Super Admin/Admin), matching the in-app Competitors tab exactly. update_brand_profile, like the in-app Knowledge Base, is open to Members on the project as well.

update_brand_profile

Update the project’s knowledge-base brand fields — name, headline (up to 120 characters), positioning (the app’s Brand Context), description, category, influence (the reach: global, national-global, nationwide, regional or local), language (a code the app offers, e.g. en-GB), website URL, case sensitivity (the app’s Exact match only), disambiguation text, and topics (the brand’s products or services as short labels — not the prompt topics create_topic makes). Only the fields you pass change; topics replaces the whole list. Does not touch connected entities, E-E-A-T data, or the default location. Needs knowledge:manage (a key scope name, not a role: Members on the project can use it too).Parameters: projectId (required), then any of name (up to 200 characters), headline (up to 120), positioning (up to 5,000), description (up to 5,000), category (up to 200), influence, language, websiteUrl (up to 2,000), caseSensitive, brandNameDisambiguation (up to 2,000), topics (up to 50 labels), plus dryRun

add_competitors

Add 1–20 competitors, each by website domain and optionally named, to the project’s tracked set. With source: "manual" and a single entry it also runs the duplicate check the in-app “add competitor” dialog runs, refusing an existing domain, name, or name variation with conflict. An entry matching an already-tracked competitor isn’t added twice — that competitor is updated instead — and the project’s own domain is skipped. Needs competitor:match.Parameters: projectId (required), competitors (required — 1–20 entries, each a website domain such as example.com or {domain, name}; a company name alone is refused with invalid_params), source ("manual" or "ai_discovery"; omitted acts as ai_discovery), dryRun

update_competitor

Edit a tracked competitor as the in-app competitor sheet does: rename it, change its website domain, make it a direct or a SERP competitor (isDirectCompetitor), and add or remove the other names it goes by (name variations), which is how its mentions are recognised in AI answers. Changing the domain, or making it direct, re-matches its past mentions in the background. Everything is checked before anything is written: a variation that is another competitor’s name is refused with conflict (merge the two in the app instead), and removing a name it doesn’t have, adding and removing the same name, or a domain that isn’t a website domain is refused with invalid_argument. A name confirmed on another competitor moves to this one. The result lists its name variations, so a dry run with no fields shows them. Needs competitor:match.Parameters: projectId, competitorId (both required — the id from get_competitors), name, domain, isDirectCompetitor, addVariations, removeVariations (up to 20 names each), dryRun

create_topic

Create a user-authored prompt topic — the same row the in-app Topics tab creates. Needs prompt:draft (topics organize prompts, so they share that surface’s capability). A name the project already uses (ignoring case; archived topics excepted) is refused with conflict, dry run included, and the error carries the existing topic’s id as existingTopicId.Parameters: projectId, name (both required; name up to 200 characters), description (up to 2,000), seedKeyword (up to 200), category ("blind_spot", "opportunity" or "strong"), dryRun

update_topic

Edit a prompt topic as the in-app Topics tab does: rename it, change its seed keyword, archive it (status: "archived"), or restore an archived one (status: "active"). Archiving takes the topic off every prompt it was on, and restoring it later does not put it back on them. A name another live topic uses is refused with conflict and that topic’s existingTopicId, dry run included — as is restoring a topic whose name a live topic has taken since. A call with none of name, seedKeyword, or status returns invalid_params. Needs prompt:draft.Parameters: projectId, topicId (both required — the id from get_topic_analysis), name, seedKeyword, status ("active" or "archived"), dryRun

update_opportunity_status

Mark an opportunity completed or dismissed; a dismissal can carry a reason and feedback text, which are ignored when completing. Needs opportunity:manage.Parameters: projectId, opportunityId, status (all required), dismissedReason, feedbackText (dismissals only, up to 2,000 characters each), dryRun

Prompt Universe (lightweight)

This is the cheap, deterministic half of Prompt Universe — the buyer-question catalogue and code gates, not the full research engine. You write the actual prompt text; these tools plan it, then check and persist what you wrote. Both additionally need Prompt Universe to be available to you and to the project’s workspace — an unavailable tenancy answers the same not_found as a project you can’t see, so a connection can’t tell “not offered here” from “doesn’t exist” by comparing responses.

get_prompt_brief

The buyer question types that matter at each funnel moment (first look, compare, buy, own) for a business model, which are decisive, and how many prompts each moment and kind should carry. Planning input only — calls no model, creates nothing. Bare read.Parameters: projectId (required), businessModelId (e.g. 1.2; omit it and every question type is treated as optional), stages (any of tofu, mofu, bofu, post_purchase; default all four), count (default and max 500, capped by the workspace’s remaining prompt allowance)

import_prompts

Add the prompts you wrote as tracked prompts, the same way add_prompts does: the next scheduled run checks them, within your prompt allowance. Starts no Prompt Universe run. Every candidate is held to the Prompt Universe code gates before anything is written (no em/en dash, no semicolon, the market’s spelling, no service request at the owning moment, no near-duplicate of each other or of the project’s existing prompts), and one refusal refuses the whole batch with invalid_params, naming each refused candidate and why. The whole batch is then checked against the allowance (if it doesn’t fit, nothing is added: quota_exceeded); dryRun: true runs both checks without adding anything. The writes themselves are not atomic: each prompt commits on its own and re-checks your prompt quota, so a batch can still stop partway if another write takes the room. Once writing starts, the response’s outcomes array says which candidates actually landed (created, failed, or not_attempted) — retry only those that aren’t created. Needs prompt:draft.Parameters: projectId, candidates (both required — 1–10 entries, each { text, stage, kind?, branded?, topicIds? }: text up to 2,000 characters, stage one of tofu, mofu, bofu, post_purchase, kind one of open, sentiment, competitor, topicIds up to 20 existing topic ids), market (US, the default, or GB; any other value skips the spelling check), dryRun

Removed tool names

Earlier iterations of this MCP shipped 35 individual tools. When those were consolidated, 26 of the old names stayed callable as deprecated aliases. Those aliases were removed in August 2026 — calling one of the names below now returns an unknown-tool error. Use the replacement. Nine other pre-consolidation names kept their exact original name as a primary tool at the time. Six still do — list_projects, list_articles, get_article, get_opportunities, get_sentiment, and get_query_fanout; the other three (get_visibility_history, get_sentiment_history, get_sentiment_competitors) were folded into selector views in August 2026 and are now deprecated names — see the next section.
The four get_source_* breakdown rows above are the one case where the removed name did something no MCP tool now does: breakdown (and get_source_trend’s group) were arguments only those aliases accepted. get_source_trends returns the top-source-domain trend directly and has no breakdown argument. For the content-type, source-type, or article-type breakdown, use the REST endpoint.

Deprecated tool names

The August 2026 family-grid consolidation folded four reads into selector parameters on their family’s primary tool. Unlike the removed aliases above, these four names still work — same handler, byte-identical payload — so existing scripts and scheduled jobs keep running. They are scheduled for removal after 2026-10-15 (the same ~60-day window the removed aliases got); move to the replacement call before then. The REST endpoints behind these reads (GET /visibility/history, GET /sentiment/history, GET /sentiment/competitors, GET /domain-authority) are not deprecated — the 12-month REST deprecation policy governs those, and nothing REST-side is scheduled for removal.

Interactive cards

Nine tools render an interactive card beside their answer in MCP-Apps-capable hosts (Claude web/desktop, ChatGPT, Goose, VS Code — all implement the same standard):A card hides itself on a call whose answer it can’t draw honestly: get_visibility on a platform-filtered call and on group_by=prompt|location|date, get_share_of_voice on group_by=date, get_sentiment on view=history|competitors, get_topic_analysis on view=prompts|heatmap, and list_prompts on status="untracked"|"suggested". Each of these nine tools also takes an optional render boolean: pass false when the assistant only needs the numbers (to summarize or feed into another call) and no card should be drawn; omitting it draws the card, as before. Hosts without MCP Apps support just see the normal text/JSON answer. Cards are dark/light aware and have an Expand button for a fullscreen view. Where a card has a period control you switch window inside the card rather than sending another message — the card queries the tool itself and remembers each window it has already loaded, so returning to one is instant.

Pagination

Tools that return lists are paginated so you can read complete datasets without silent truncation. The one exception is list_articles: a call returns at most 100 rows, and summary.total gives the full count.

Per-prompt visibility

get_visibility with group_by: "prompt" is fully paginated. Every response includes a pagination object that tells you whether more prompts exist and exactly how to fetch the next page:
Loop until you’ve read every prompt:
  1. Call get_visibility with group_by: "prompt" (defaults: limit 100, offset 0).
  2. While pagination.hasMore is true, call again with offset = pagination.nextOffset.
  3. Stop when pagination.hasMore is false.
limit accepts 1–500 prompts per page. The total number of prompts you can page through is unbounded — paging in fixed-size chunks scales to projects with thousands of prompts.

Other list tools

Other list-returning tools page with limit + offset too: get_opportunities (default 100, max 500), search_sources (default 20, max 1000), get_source_detail with include: "responses", get_query_fanout’s project aggregate (default 50, max 50 — the prompt_id drill-down isn’t paginated), get_prompt_answers (default 10, max 50 — pass the previous call’s nextOffset back as offset), list_prompts, get_competitors (list view), get_site_health (view=pages|issues), and get_shopping_visibility (summary view). get_source_detail’s URL lists (include: "urls", or level: "url" without a url for the flat cross-project listing) are cursor-based: pass the previous response’s nextCursor as cursor until hasMore is false.

Example prompts

Visibility analysis

Site audit

Content

Use cases

Automated issue fixing

Pair the MCP with a coding assistant to retrieve AEO/SEO issues and apply fixes in your codebase:
The assistant fetches issues from Searchable (missing meta descriptions, alt text, heading structure, etc.), locates the relevant files, and proposes or applies fixes.

For developers

  • Query AEO/SEO issues while debugging frontend code
  • Check if code changes would impact page audit scores
  • Review article content without leaving your IDE

For AEO/SEO teams

  • Pull visibility summaries during standups
  • Extract audit data for reports
  • Track issue resolution progress

For content teams

  • Review article status and metadata
  • Pull full article content for editing
  • Track content coverage across topics

Troubleshooting

  • Confirm the server URL is exactly https://app.searchable.com/api/mcp-server/mcp (browser-sent Origin headers are validated against an allowlist — hosted connectors normally call server-side without one, so they’re unaffected).
  • Check your API key starts with sea_ and is enabled for MCP in Searchable settings.
  • Restart your MCP client after config changes.
  • Confirm the key starts with sea_ and hasn’t been revoked in Manage Account → Integrations → MCP → Access tokens. - Confirm the key’s workspace is on a paid Searchable plan — MCP is unavailable on the Free plan.
  • Confirm you have projects in your Searchable account. - Check that the API key belongs to the correct workspace.
  • The project has no GA4 property linked, so the tool call returns a 409 error with code: "ga4_not_connected". - Connect Google Analytics 4 under Settings → Integrations in your Searchable dashboard (the error’s howToFix field carries the direct link), then retry the tool call.
  • The project has no Google Search Console site linked, so the tool call returns a 409 error with code: "gsc_not_connected". - Connect Google Search Console under Settings → Integrations in your Searchable dashboard (the error’s howToFix field carries the direct link), then retry the tool call.
  • The project has no crawler-log, CDN, or Searchable-tracker source connected, so the tool call returns a 409 error with code: "traffic_not_connected". - Connect a source under AI Traffic → Setup in your Searchable dashboard (the error’s howToFix field carries the direct link), then retry the tool call.
  • The workspace’s plan doesn’t include Shopping Analytics (Free and Starter don’t), so the tool call returns a 403-equivalent error with code: "plan_upgrade_required" — on the summary view only once the project has shopping data to fetch. - This is different from available: false — that’s a normal 200 response meaning the project just has no shopping data yet, not a plan restriction. If you’re seeing available: false instead of this error, no upgrade is needed. - Upgrade to a plan with Shopping Analytics in Settings → Billing, then retry.
Neither means you’ve used up a daily or monthly quota. query_timeout means the aggregation ran past the server-side limit — reduce the days window or add filters (platform, topicId, country), then retry. rate_limited has two sources. (1) The per-connection tool-call limit: its error carries retryable: true and a message naming “tool calls per minute”, and it is safe to retry after retryAfterSeconds. (2) A refresh_suggestions call to manage_associated_source (where Associated Sources is enabled) while a source scout is already running or was queued recently. Its error has no retryable field. Don’t retry that one automatically: read get_associated_sources with view=suggestions and check latestRefreshCompleted. A new scout is paid, so start one only after the error’s retryAfterSeconds have passed, and with the user’s confirmation.
The connection lacks the scope or capability the tool needs. trigger_audit and refresh_sitemap need write; the project and prompt management tools and import_prompts need the capability named on their card, which write doesn’t include. Over OAuth, reconnect and turn on Allow write access on the consent screen — it also grants the capabilities the client asked for. It’s offered only for projects you can edit, so a workspace viewer’s connection is always read-only. An API key’s scopes can’t be changed after it’s created: generate a new access token with Allow write access on, which grants write and every capability, and revoke the old one.
  • Restart your MCP client.
  • Verify the server URL and JSON config syntax.
  • Check the client’s MCP logs for errors.

Security

Read-only by default, write tools scope-gated

Every read tool is annotated readOnlyHint: true: it can only list and read data. trigger_audit and refresh_sitemap require a grant or key with the write scope — a read-only connection can’t invoke them at all (the call fails with missing_scope). generate_report publishes a shareable report link and works on any connection, as sharing a report is open to every project member in the app; the workspace’s sharing policy can switch it off. The write tools under project and prompt management, and import_prompts, each need their own named capability instead. Most write tools act straight away, as the app’s own controls do; pass dryRun: true to preview one first. The ones where a mistake costs money, exposure or a project slot — create_project, generate_report and refresh_sitemap, plus manage_associated_source where Associated Sources is enabled — still need an explicit confirm: true on the call, and without it return a no-op dry-run preview.

Token & key handling

  • The OAuth login flow issues your client an access token scoped to exactly the projects and read/write access you approved on the consent screen — no API key is created or stored for this path.
  • Every request is re-checked against the server-side authorization grant, so a revoked grant stops working immediately — the token in the client’s hands can’t outlive the consent it came from.
  • Tokens refresh silently while the connection is in use; active connections persist, and an idle connection expires about 60 days after its last use, after which you simply re-authorize.
  • On the legacy API-key path, the sea_ key is the bearer token — treat it as a secret, scope it narrowly, and revoke it in Manage Account → Integrations → MCP → Access tokens if it’s exposed.

Origin validation

The server validates browser Origin headers against an allowlist (claude.ai, claude.com, the Microsoft 365 Copilot widget origins that interactive cards render from, plus dev hosts) to prevent cross-site abuse. For an allowed origin it also answers the browser’s CORS preflight and sends the matching CORS headers. Requests without an Origin header are allowed through — that covers native clients (Claude Desktop, Cursor, curl) and hosted connectors like Claude.ai and ChatGPT, which call from their servers rather than a browser. This is defense-in-depth; bearer-token auth is the primary control.

Best practices

  • Rotate keys regularly — generate new keys periodically and revoke old ones.
  • Use per-use-case keys — separate keys for separate clients makes revocation targeted.
  • Revoke unused keys — remove keys you’re no longer using from Searchable settings.

API reference

Server details

Rate limits

Searchable limits MCP tool calls to 600 per minute per API key or OAuth connection. Every app using the same API key shares that budget, and it is separate from the REST API’s 600 requests/minute. Past the limit a tool returns rate_limited with retryable: true and retryAfterSeconds; wait that long, then retry. Two server-side controls can make a call fail retryably:
  • Tool-call limit. Over 600 tool calls in a minute returns rate_limited; the error says how many seconds to wait.
  • Query timeout. Long aggregations are bounded by a server-side timeout, returning query_timeout with a hint to narrow the days window or add filters.
Normal interactive usage from Claude, Cursor, or similar clients is not expected to hit either.

Support

All integrations

Browse the integrations directory

Contact support

Email our team