> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended** (the `-y` flags skip prompts, so the command runs without an interactive terminal):
> ```bash
> npx -y @scalekit-inc/cli setup -y
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup -y
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for other Agent Skills-compatible agents in `~/.agents/skills`.
> Skills: integrate-agentkit, implement-saaskit, add-mcp-oauth, implement-sso, implement-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Connect AI agents to the OpenSEO MCP server

The OpenSEO MCP connector routes your AI agent's tool calls to OpenSEO's own MCP server through Scalekit. Each user signs in to OpenSEO once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 46 tools.

**Authentication:** OAuth 2.1/DCR
**Categories:** Marketing, Analytics, Search
**Tools:** 46: 21 read, 22 write, 3 destructive
**Users sign in with:** OAuth
**OAuth app:** Scalekit's or your own
**Built by:** Vendor MCP
**Try it:** [Playground in the Scalekit dashboard](https://app.scalekit.com/ws/signup?sk_intent=playground&provider=OPENSEOMCP)

## Setup

1. ### Install the SDK

   **Node.js**

   ```bash
   npm install @scalekit-sdk/node dotenv
   ```

   **Python**

   ```bash
   pip install scalekit-sdk-python python-dotenv
   ```

2. ### Set your credentials

   Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**.

   ```sh title=".env"
   SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
   SCALEKIT_CLIENT_ID=<your-client-id>
   SCALEKIT_CLIENT_SECRET=<your-client-secret>
   ```

3. ### Create the OpenSEO MCP connection

   In **AgentKit > Connections**, create an OpenSEO MCP connection. The name you give it is the `connection_name` your code passes. See [Configure connections](/agentkit/connections/).

   Scalekit credentials are available for OpenSEO MCP server, so you don't need to register an OAuth app.

4. ### Authorize a user and make your first call

   **Node.js** (`quickstart.mts`)

   ```typescript
   import { ScalekitClient } from '@scalekit-sdk/node'
   import 'dotenv/config'
   import { createInterface } from 'node:readline/promises'

   const scalekit = new ScalekitClient(
     process.env.SCALEKIT_ENVIRONMENT_URL,
     process.env.SCALEKIT_CLIENT_ID,
     process.env.SCALEKIT_CLIENT_SECRET,
   )
   const actions = scalekit.actions

   const connector = 'openseomcp'
   const identifier = 'user_123'

   // Generate an authorization link for the user
   const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
   console.log('Authorize OpenSEO MCP:', link)
   const rl = createInterface({ input: process.stdin, output: process.stdout })
   await rl.question('Press Enter after authorizing...')
   rl.close()

   // Make your first call
   const result = await actions.executeTool({
     connector,
     identifier,
     toolName: 'openseomcp_whoami',
     toolInput: {},
   })
   console.log(result)
   ```

   ```bash
   npx tsx quickstart.mts
   ```

   **Python** (`quickstart.py`)

   ```python
   import os
   from scalekit import ScalekitClient
   from dotenv import load_dotenv
   load_dotenv()

   scalekit_client = ScalekitClient(
       env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
       client_id=os.getenv("SCALEKIT_CLIENT_ID"),
       client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
   )
   actions = scalekit_client.actions

   connection_name = "openseomcp"
   identifier = "user_123"

   # Generate an authorization link for the user
   link_response = actions.get_authorization_link(
       connection_name=connection_name,
       identifier=identifier,
   )
   print("Authorize OpenSEO MCP:", link_response.link)
   input("Press Enter after authorizing...")

   # Make your first call
   result = actions.execute_tool(
       tool_input={},
       tool_name="openseomcp_whoami",
       connection_name=connection_name,
       identifier=identifier,
   )
   print(result)
   ```

   ```bash
   python quickstart.py
   ```

   Each user signs in once. See [Authorize a user](/agentkit/tools/authorize/) for the full flow and statuses.

## Tools

Pass the exact name to `execute_tool`, with an input like each tool's example. To ask for a tool that's missing, use the [request form](https://scalekitsupport.portal.usepylon.com/forms/request-a-connector-tool).

**Node.js**

```typescript
const result = await actions.executeTool({
  toolName: 'openseomcp_estimate_rank_tracker_cost',
  toolInput: {
    projectId: '<projectId>',
    trackerId: '<trackerId>',
  },
  connector: 'openseomcp',
  identifier: 'user_123',
})
```

**Python**

```python
result = actions.execute_tool(
    tool_name="openseomcp_estimate_rank_tracker_cost",
    tool_input={
        "projectId": "<projectId>",
        "trackerId": "<trackerId>",
    },
    connection_name="openseomcp",
    identifier="user_123",
)
```

`result.data` is the app's response as JSON, and `result.execution_id` (`executionId` in Node.js) is the ID of the call.

### `openseomcp_estimate_rank_tracker_cost`

Estimate Rank Tracker Cost · Read-only

Estimate rank tracker cost without spending credits or starting a check. The live estimate covers one explicit run_rank_tracker check. For a scheduled tracker, the response also includes nominal queued per-check and approximate monthly recurring cost. Pass additionalKeywordCount before adding keywords to project the post-add cost. Scheduled estimates are not runtime caps; rejected, failed, or timed-out queued tasks may use additional separately billed live fallback.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `trackerId` (`string`, required): Rank tracker ID from get_rank_tracker.
- `additionalKeywordCount` (`integer`, optional): Number of keywords you plan to add. Include this before adding to a scheduled tracker so the response projects its recurring per-check and monthly cost.

Example input: `{"projectId":"<projectId>","trackerId":"<trackerId>"}`

### `openseomcp_get_audit_issues`

Get site audit issues · Read-only

Read the prioritized issue report from a completed site audit. Every issue carries a how_to_fix with concrete remediation steps an agent can act on. Free — reads OpenSEO state. Omit auditId for the most recent audit.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.
- `auditId` (`string`, optional): ID of the audit to read issues from. If omitted, uses the project's most recent audit. Example: audit_5f3a1b2c.
- `issueType` (`string`, optional): Only return issues of this type, such as broken-internal-link, duplicate-title, thin-content, or redirect-chain. See the enum for the full list of recognized issue types. Leave blank to return every type. One of: `blocked-page`, `rate-limited-page`, `crawl-rate-limited`, `server-error`, `broken-internal-link`, `missing-title`, `broken-page`, `duplicate-title`, `duplicate-meta-description`, `duplicate-content`, `missing-meta-description`, `missing-h1`, `multiple-h1`, `redirect-chain`, `redirect-loop`, `canonical-conflict`, `thin-content`, `images-missing-alt`, `orphan-page`, `no-outgoing-links`, `title-too-long`, `title-too-short`, `meta-description-too-long`, `meta-description-too-short`, `heading-order-skip`, `slow-response`, `noindex-page`, `canonicalized-page`, `deep-page`.
- `limit` (`integer`, optional): Maximum number of issues to return, between 1 and 1000. Defaults to 200. Default: `200`.
- `severity` (`string`, optional): Only return issues at this severity level: critical, warning, or info. Leave blank to return issues of every severity. One of: `critical`, `warning`, `info`.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_audit_pages`

Get site audit pages · Read-only

List crawled pages from a site audit with per-page SEO data (status, title, description, word count, indexability, crawl depth, link counts). Free — reads OpenSEO state. Omit auditId for the most recent audit.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.
- `auditId` (`string`, optional): ID of the audit to list pages from. If omitted, uses the project's most recent audit. Example: audit_5f3a1b2c.
- `fetchClass` (`string`, optional): Filter by fetch outcome: ok, blocked (the site's bot protection challenged the crawler), rate_limited (a 429 prevented the crawler from reading the page), or error. Leave blank to return pages of every fetch outcome. One of: `ok`, `blocked`, `rate_limited`, `error`.
- `limit` (`integer`, optional): Maximum number of pages to return, between 1 and 1000. Defaults to 100. Default: `100`.
- `statusCode` (`integer`, optional): Filter to pages that returned exactly this HTTP status code. Example: 404.
- `urlContains` (`string`, optional): Filter to page URLs containing this substring (case-sensitive). Example: /blog/.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_audience_breakdown`

Get Google Analytics audience breakdown · Read-only

Read device, country, or new-versus-returning users, sessions, engagement, and key events. Previous-period comparison is available for device and new-versus-returning breakdowns. No demographic or user-level dimensions. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.
- `breakdown` (`string`, optional): How to group audience data: device (default; desktop/mobile/tablet), country, or new_vs_returning (new versus returning users). Previous-period comparison is available for device and new_vs_returning. Defaults to device. One of: `device`, `country`, `new_vs_returning`. Default: `device`.
- `channel` (`string`, optional): Which acquisition channel to scope the audience data to: organic_search (default) or all channels. One of: `organic_search`, `all`. Default: `organic_search`.
- `comparePreviousPeriod` (`boolean`, optional): When true, adds previous-period comparison values alongside the current period's metrics. Only available when Breakdown is device or new_vs_returning Defaults to false. Default: `false`.
- `endDate` (`string`, optional): Inclusive end date of the reporting window, formatted as YYYY-MM-DD. Must be provided together with startDate. Example: 2026-08-31.
- `limit` (`integer`, optional): Maximum number of rows to return, between 1 and 1000. Defaults to 100. Default: `100`.
- `offset` (`integer`, optional): Number of rows to skip before the first row returned, for paging through results beyond limit. Defaults to 0. Default: `0`.
- `startDate` (`string`, optional): Inclusive start date of the reporting window, formatted as YYYY-MM-DD. Must be provided together with endDate; when both are omitted OpenSEO falls back to its standard recent date range. Example: 2026-08-01.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_ecommerce_performance`

Get Google Analytics ecommerce performance · Read-only

Read item views, add-to-cart units, purchases, and item revenue by item, or transactions and purchase revenue by landing page. Returns a detected, none, or unknown activity state; landing pages can be limited to those with transactions. Organic Search is the default. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.
- `breakdown` (`string`, optional): How to group ecommerce performance data: item (item views, add-to-cart units, purchases, and item revenue per item) or landing_page (transactions and purchase revenue per landing page). Defaults to item. One of: `item`, `landing_page`. Default: `item`.
- `channel` (`string`, optional): Which acquisition channel to scope the ecommerce data to: organic_search (default) or all channels. One of: `organic_search`, `all`. Default: `organic_search`.
- `endDate` (`string`, optional): Inclusive end date of the reporting window, formatted as YYYY-MM-DD. Must be provided together with startDate. Example: 2026-08-31.
- `limit` (`integer`, optional): Maximum number of rows to return, between 1 and 1000. Defaults to 100. Default: `100`.
- `offset` (`integer`, optional): Number of rows to skip before the first row returned, for paging through results beyond limit. Defaults to 0. Default: `0`.
- `onlyWithTransactions` (`boolean`, optional): When true and Breakdown is landing_page, limits results to landing pages that recorded at least one transaction. Defaults to false. Default: `false`.
- `startDate` (`string`, optional): Inclusive start date of the reporting window, formatted as YYYY-MM-DD. Must be provided together with endDate; when both are omitted OpenSEO falls back to its standard recent date range. Example: 2026-08-01.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_key_events`

Get Google Analytics key events · Read-only

Read active GA4 key events with counts and users by event or organic landing page. Previous-period comparison is available for the event breakdown. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `breakdown` (`string`, optional): How to group key-event rows. Defaults to event. One of: `event`, `event_and_landing_page`. Default: `event`.
- `channel` (`string`, optional): Traffic channel to filter to. Defaults to organic_search. One of: `organic_search`, `all`. Default: `organic_search`.
- `comparePreviousPeriod` (`boolean`, optional): Include a comparison against the immediately preceding period of equal length. Only applies to the event breakdown. Defaults to false. Default: `false`.
- `endDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.
- `limit` (`integer`, optional): Maximum number of key-event rows to return. Defaults to 100. Default: `100`.
- `offset` (`integer`, optional): Number of rows to skip, for pagination. Defaults to 0. Default: `0`.
- `startDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_measurement_health`

Get Google Analytics measurement health · Read-only

Diagnose the connected property's data streams, web measurement IDs, enhanced-measurement settings, key events, and custom definitions. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_organic_landing_pages`

Get Google Analytics organic landing pages · Read-only

Read organic-search landing page sessions, engagement, key events, transactions, and revenue from the project's connected GA4 property. Defaults to the last 28 complete property days. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `endDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.
- `limit` (`integer`, optional): Maximum number of landing-page rows to return. Defaults to 100. Default: `100`.
- `offset` (`integer`, optional): Number of rows to skip, for pagination. Defaults to 0. Default: `0`.
- `startDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_organic_overview`

Get Google Analytics organic overview · Read-only

Answer whether organic traffic is improving with top-line sessions, users, engagement, key events, transactions, revenue, an equal-length previous-period comparison, and a daily or weekly trend. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `endDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.
- `startDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.
- `trend` (`string`, optional): Granularity of the trend series returned alongside the summary. Defaults to daily. One of: `daily`, `weekly`. Default: `daily`.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_page_performance`

Get Google Analytics page performance · Read-only

Read page views, users, engagement duration, and key events from the connected GA4 property. Organic Search is the default; set channel to all to include every channel. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `channel` (`string`, optional): Traffic channel to filter to. Defaults to organic_search. One of: `organic_search`, `all`. Default: `organic_search`.
- `endDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.
- `includeDate` (`boolean`, optional): Break results out by date in addition to page. Defaults to false. Default: `false`.
- `limit` (`integer`, optional): Maximum number of page rows to return. Defaults to 100. Default: `100`.
- `offset` (`integer`, optional): Number of rows to skip, for pagination. Defaults to 0. Default: `0`.
- `startDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_site_search`

Get Google Analytics site search · Read-only

Read measured internal search terms with search events, users, sessions, engaged sessions, and engagement rate. Requires GA4 site-search measurement. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.
- `endDate` (`string`, optional): Inclusive end date of the reporting window, formatted as YYYY-MM-DD. Must be provided together with startDate. Example: 2026-08-31.
- `limit` (`integer`, optional): Maximum number of rows to return, between 1 and 1000. Defaults to 100. Default: `100`.
- `offset` (`integer`, optional): Number of rows to skip before the first row returned, for paging through results beyond limit. Defaults to 0. Default: `0`.
- `startDate` (`string`, optional): Inclusive start date of the reporting window, formatted as YYYY-MM-DD. Must be provided together with endDate; when both are omitted OpenSEO falls back to its standard recent date range. Example: 2026-08-01.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_google_analytics_traffic_acquisition`

Get Google Analytics traffic acquisition · Read-only

Compare session acquisition by channel group, source/medium, or campaign, including sessions, users, engagement, key events, transactions, and revenue. Previous-period comparison is available for channel group; source/medium also reports attribution-quality diagnostics. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.
- `breakdown` (`string`, optional): How to group traffic-acquisition sessions: channel_group (default marketing channel groups), source_medium (raw source/medium pairs, plus attribution-quality diagnostics), or campaign (campaign names). Defaults to channel_group. One of: `channel_group`, `source_medium`, `campaign`. Default: `channel_group`.
- `comparePreviousPeriod` (`boolean`, optional): When true, adds previous-period comparison values alongside the current period's metrics. Only available when Breakdown is channel_group Defaults to false. Default: `false`.
- `endDate` (`string`, optional): Inclusive end date of the reporting window, formatted as YYYY-MM-DD. Must be provided together with startDate. Example: 2026-08-31.
- `limit` (`integer`, optional): Maximum number of rows to return, between 1 and 1000. Defaults to 100. Default: `100`.
- `offset` (`integer`, optional): Number of rows to skip before the first row returned, for paging through results beyond limit. Defaults to 0. Default: `0`.
- `startDate` (`string`, optional): Inclusive start date of the reporting window, formatted as YYYY-MM-DD. Must be provided together with endDate; when both are omitted OpenSEO falls back to its standard recent date range. Example: 2026-08-01.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_project_context`

Get Project Context · Read-only

Reads a project's shared memory: business overview, current goal, positioning, writing preferences, custom sections, competitors, key pages, and the recent research log. Uses no credits. Call this before SEO work to ground it in what the user already told OpenSEO, and check the research log before re-buying research. Sections listed as missing are the ones worth filling with update_project_context.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_rank_tracker`

Get Rank Tracker · Read-only

Read-only access to rank tracker configs and their latest results. With `trackerId`, returns config + latest snapshot per keyword, including `trackingKeywordId` for removals. Without it, lists all trackers in the project. Uses no credits. Use create_rank_tracker when no tracker exists; then use add_rank_tracking_keywords, remove_rank_tracking_keywords, estimate_rank_tracker_cost, or run_rank_tracker to manage it. `lastCheckedAt` shows position freshness.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `trackerId` (`string`, optional): Rank tracker config ID. If omitted, lists all rank trackers in the project.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_search_console_performance`

Get Google Search Console performance · Read-only

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 and is omitted from rows when type is 'discover' or 'googleNews' (Google does not report it there — treat it as unavailable, not a failure); dates are Pacific Time; the last ~3 days may be incomplete. Read-only; uses no credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `dataState` (`string`, optional): 'all' (default) includes fresh/incomplete recent data. One of: `all`, `final`. Default: `all`.
- `dateRange` (`string`, optional): 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. One of: `last_7_days`, `last_28_days`, `last_3_months`, `last_6_months`, `last_12_months`, `last_16_months`. Default: `last_28_days`.
- `dimensions` (`array`, optional): 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.
- `endDate` (`string`, optional): Explicit end (YYYY-MM-DD, Pacific Time). Use with startDate.
- `filters` (`array`, optional): AND-combined filters. To get the queries for one page: [{dimension:'page',operator:'equals',expression:'https://example.com/post'}] with dimensions ['query'].
- `rowLimit` (`integer`, optional): 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. Default: `1000`.
- `startDate` (`string`, optional): Explicit start (YYYY-MM-DD, Pacific Time). Use with endDate.
- `startRow` (`integer`, optional): Pagination offset. Default: `0`.
- `type` (`string`, optional): Search type (default web). One of: `web`, `image`, `video`, `news`, `googleNews`, `discover`. Default: `web`.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_search_opportunities`

Get search opportunities · Read-only

Join Search Console pages ranking in positions 4–20 with GA4 organic landing-page outcomes, then score matched opportunities by demand, business value, and reachability. Unmatched pages remain visible and unscored. Read-only and uses no OpenSEO credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `endDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.
- `limit` (`integer`, optional): Maximum number of opportunity rows to return. Defaults to 50. Default: `50`.
- `startDate` (`string`, optional): Inclusive YYYY-MM-DD date. Provide both startDate and endDate.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_inspect_urls`

Inspect URLs in Google Search Console · Read-only

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?'. Per-URL failures are reported inline. Read-only; uses no credits.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `urls` (`array`, required): 1–10 absolute URLs to inspect. Each must belong to the connected property.
- `languageCode` (`string`, optional): BCP-47 language for the inspection result (e.g. 'en-US').

Example input: `{"projectId":"<projectId>","urls":[]}`

### `openseomcp_list_business_categories`

List business categories · Read-only

Lists the Google Business categories DataForSEO recognizes, ranked by how many businesses use them. Use it to find valid category slugs for search_local_businesses (e.g. 'pizza_restaurant'). Uses no credits (the full list is cached for 7 days).

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `limit` (`integer`, optional): Maximum categories to return (1-200). Defaults to 50. Default: `50`.
- `query` (`string`, optional): Case-insensitive substring to match against category slugs (e.g. 'plumb').

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_list_projects`

List Projects · Read-only

Lists the user's projects. Uses no credits — does not call DataForSEO. Use this whenever you need a `projectId` for another OpenSEO tool. Returns an array of {id, name, domain, locationCode, languageCode}; pass the `id` value as `projectId`. locationCode/languageCode are the project's default market — tools fall back to them when a call omits location/language args. When the user belongs to several organizations, each project is labeled with its organization and organizationId (pass that to create_project).

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs: none.

Example input: `{}`

### `openseomcp_list_saved_keywords`

List Saved Keywords · Read-only

Lists keywords saved to a project (with cached metrics like search volume, difficulty, CPC, and tags if available). Uses no credits — reads from OpenSEO's database, no DataForSEO call. Use tag filters when the user asks for a saved segment; multiple tags match ANY tag.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `limit` (`integer`, optional): Maximum number of saved keyword rows to return: 50, 100, or 250. Defaults to 100. One of: `50`, `100`, `250`. Default: `100`.
- `search` (`string`, optional): Optional keyword text filter; matches keywords containing this text.
- `tags` (`array`, optional): Optional tag-name filters (up to 20). A saved keyword matches if it has ANY of the listed tags.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_whoami`

Who am I · Read-only

Confirms the connected OpenSEO account, server mode, token scopes, and current credit balance when the user asks to check their account or connection. Uses no credits — does not call DataForSEO.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs: none.

Example input: `{}`

### `openseomcp_add_rank_tracking_keywords`

Add Rank Tracking Keywords · Write

Add keywords to an existing rank tracker. The mutation itself uses no credits and does not start a check or fetch metrics, but scheduled trackers will spend credits on future recurring checks. For a scheduled tracker, call estimate_rank_tracker_cost with additionalKeywordCount, show the recurring estimate and live-fallback caveat to the user, and pass the approved nominal per-check estimate as maxEstimatedScheduledCheckCredits. This approval is not a runtime spending cap: rejected, failed, or timed-out queued tasks may use additional separately billed live fallback. Existing and repeated keywords are skipped, and `added` is the number actually inserted.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `keywords` (`array`, required): Keywords to track. Existing and repeated keywords are skipped.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `trackerId` (`string`, required): Rank tracker ID from get_rank_tracker.
- `matchCase` (`boolean`, optional): Track the keywords exactly as typed instead of lowercasing them. Defaults to false. Use for brand names where Google's results differ by capitalization; a cased keyword is tracked and billed separately from its lowercase form. Default: `false`.
- `maxEstimatedScheduledCheckCredits` (`integer`, optional): Nominal queued credits per scheduled check that the user approved after seeing estimate_rank_tracker_cost with additionalKeywordCount. Required for scheduled trackers. This is an estimate approval, not a runtime cap; live fallback may add separately billed credits.

Example input: `{"keywords":[],"projectId":"<projectId>","trackerId":"<trackerId>"}`

### `openseomcp_create_project`

Create Project · Write

Create a new project in the user's organization. Uses no credits — does not call DataForSEO. Provide a name, and optionally a domain and default market (locationCode/languageCode; a languageCode requires a locationCode). Returns the created {id, name, domain, locationCode, languageCode, url}; pass the returned `id` as `projectId` to other OpenSEO tools. Call list_projects first to avoid creating a duplicate.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `name` (`string`, required): Project name, 1-120 characters. This becomes the project's display name in OpenSEO.
- `domain` (`string`, optional): Optional root domain for the project, host only (no scheme or path), e.g. 'example.com'. Sets the default target for domain, backlink, and rank tools.
- `languageCode` (`string`, optional): Optional language code for the project's default market, e.g. 'en' or 'fr'. Requires locationCode to be set; derived from the location when omitted.
- `locationCode` (`integer`, optional): Optional DataForSEO location code for the project's default market, e.g. 2840 for United States or 2504 for Morocco. Falls back to the organization default when omitted.
- `organizationId` (`string`, optional): Organization id to create the project in. Required when the user belongs to more than one organization; omitting it in that case returns the list of organizations instead of creating a project, so confirm the choice with the user before retrying.

Example input: `{"name":"<name>"}`

### `openseomcp_create_rank_tracker`

Create Rank Tracker · Write

Create a rank tracking configuration for a project. Creating an empty tracker uses no credits and starts no check, but daily, weekly, and monthly trackers will spend credits after keywords are added. The domain defaults to the project's domain; market defaults to the project's market; devices default to mobile, search depth to 40, and schedule to manual. Use estimate_rank_tracker_cost before adding keywords to a scheduled tracker or starting a live run. Call get_rank_tracker first to avoid duplicates.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `devices` (`string`, optional): Devices to track. Defaults to mobile. One of: `desktop`, `mobile`, `both`. Default: `mobile`.
- `domain` (`string`, optional): Domain to track. Defaults to the project's domain. Accepts a hostname or URL and stores the normalized hostname.
- `languageCode` (`string`, optional): Language code (e.g. 'en', 'es', 'vi'). Defaults to the project's default market language (see list_projects).
- `locationCode` (`integer`, optional): DataForSEO location code. Defaults to the project's default market (see list_projects; editable in project settings). 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.
- `locationName` (`string`, optional): Optional city or region name for local rank tracking.
- `scheduleInterval` (`string`, optional): Check schedule. Defaults to manual so creating a tracker cannot cause future credit spend. Scheduled checks may use credits later. One of: `manual`, `daily`, `weekly`, `monthly`. Default: `manual`.
- `serpDepth` (`integer`, optional): Number of Google results to inspect. Defaults to 40. Default: `40`.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_find_serp_competitors`

Find SERP competitors · Write

Compares domains competing in Google results for a supplied keyword set in a country-level DataForSEO Labs market. Accepts location/language codes; not radius-based local SEO. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `keywords` (`array`, required): Keywords whose SERPs are compared (1-100).
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `excludeDomains` (`array`, optional): Domains to exclude from results (e.g. the user's own site).
- `includeSubdomains` (`boolean`, optional): Count subdomains as part of the same competitor domain.
- `languageCode` (`string`, optional): Language for locationCode. Defaults to that location's primary language when locationCode overrides the project market.
- `limit` (`integer`, optional): Maximum competitors to return (1-100). Defaults to 50. Default: `50`.
- `locationCode` (`integer`, optional): Country-level DataForSEO Labs location code. Defaults to the project's market; takes precedence over the legacy market object.
- `market` (`object`, optional): Legacy US selector. Prefer locationCode/languageCode for any Labs market. Explicit locationCode takes precedence; otherwise omitted = the project's default market.
- `offset` (`integer`, optional): Rows to skip for pagination. Default: `0`.
- `resultTypes` (`array`, optional): SERP result types to include. Defaults to organic and local_pack.
- `sortBy` (`string`, optional): Sort order for returned competitors. Defaults to visibility. One of: `visibility`, `traffic_estimate`, `avg_position`, `keyword_count`. Default: `visibility`.

Example input: `{"keywords":[],"projectId":"<projectId>"}`

### `openseomcp_get_audit_status`

Get site audit status · Write

Check the progress of a site audit (phase, pages crawled, Lighthouse progress). Free — reads OpenSEO state and may reconcile a dead workflow by marking its audit failed. Omit auditId for the most recent audit.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.
- `auditId` (`string`, optional): ID of the audit to check. If omitted, uses the project's most recent audit. Example: audit_5f3a1b2c.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_backlinks_overview`

Get Backlinks Overview · Write

Returns a backlinks profile summary (total backlinks, referring domains, top referring domains). Charges credits (~50 typical for a domain, ~25 for a single page). Note: bare domains default to scope 'subdomains'; pass scope 'domain' to exclude subdomains from the totals. Targets with a path default to 'subfolder', whose counts come from filtered backlink totals (no rank/trends/referring-domain breakdown). Trend data always includes subdomains (provider limitation). Self-hosted deployments need the Backlinks API enabled on their DataForSEO account.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `target` (`string`, required): Domain or URL to analyze (e.g. 'example.com' or 'https://example.com/blog').
- `hideSpam` (`boolean`, optional): Filter out spammy referring domains. Defaults to true. Default: `true`.
- `scope` (`string`, optional): Research scope: 'domain' (hostname without subdomains), 'subdomains' (hostname plus all subdomains), 'subfolder' (path and its children), or 'exact_url' (one page). Defaults to 'subdomains' for root inputs and 'subfolder' when the input has a path. 'page' is a deprecated alias of 'exact_url'. Subfolder counts are computed from filtered backlink totals; rank, trends, and the referring-domains breakdown are unavailable for subfolders. One of: `exact_url`, `subfolder`, `domain`, `subdomains`, `page`.

Example input: `{"projectId":"<projectId>","target":"example.com"}`

### `openseomcp_get_backlinks_profile`

Get Backlinks Profile · Write

Returns one bounded page of detailed backlink rows for a domain or page: linking URLs, target URLs, anchors, dofollow/nofollow, authority/spam signals, and lost/broken status. Supports filters, sorting, one_per_domain/as_is mode, and pagination. Charges credits (~30 per page typical). Self-hosted deployments need the Backlinks API enabled on their DataForSEO account.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `target` (`string`, required): Domain or URL to analyze (e.g. 'example.com' or 'https://example.com/blog').
- `filters` (`object`, optional): Backlink row filters: include/exclude source URL terms, authority/spam ranges, dofollow/nofollow, lost/broken visibility, or exact domainFrom.
- `hideSpam` (`boolean`, optional): Filter out spammy backlinks. Defaults to true. Default: `true`.
- `mode` (`string`, optional): DataForSEO backlink grouping: one_per_domain returns each referring domain's strongest link; as_is returns individual backlink rows. One of: `one_per_domain`, `as_is`. Default: `one_per_domain`.
- `page` (`integer`, optional): 1-indexed results page. Defaults to 1. Default: `1`.
- `pageSize` (`integer`, optional): Rows per page. Allowed values: 50, 100, or 200. One of: `50`, `100`, `200`. Default: `100`.
- `scope` (`string`, optional): Research scope: 'domain' (hostname without subdomains), 'subdomains' (hostname plus all subdomains), 'subfolder' (path and its children), or 'exact_url' (one page). Defaults to 'subdomains' for root inputs and 'subfolder' when the input has a path. 'page' is a deprecated alias of 'exact_url'. Subfolder counts are computed from filtered backlink totals; rank, trends, and the referring-domains breakdown are unavailable for subfolders. One of: `exact_url`, `subfolder`, `domain`, `subdomains`, `page`.
- `sortField` (`string`, optional): Backlink row sort field. One of: `rank`, `domainRank`, `spamScore`, `firstSeen`. Default: `firstSeen`.
- `sortOrder` (`string`, optional): Sort direction. One of: `asc`, `desc`. Default: `desc`.

Example input: `{"projectId":"<projectId>","target":"example.com"}`

### `openseomcp_get_business_profile`

Get business profile · Write

Reads one Google Business Profile: categories, rating and review count, rating breakdown, address, phone, website, claimed status, opening hours, and photo count. Use it to audit your own profile or to compare a competitor's. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `businessName` (`string`, optional): Business name as it appears on Google. Supply exactly one of businessName, cid, or placeId.
- `cid` (`string`, optional): Google-defined business CID (from get_local_serp_results rows). Most precise identifier.
- `languageCode` (`string`, optional): Language code (e.g. 'en', 'es', 'vi'). Defaults to the project's default market language (see list_projects).
- `locationCode` (`integer`, optional): DataForSEO location code. Ignored when `near` is set; otherwise defaults to the project's market.
- `near` (`object`, optional): Coordinate to search from. Use it when the business name is ambiguous; otherwise locationCode is enough.
- `placeId` (`string`, optional): Google Maps place_id (from get_local_serp_results rows).

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_business_reviews`

Get business reviews · Write

Collects Google reviews for a business, with rating, author, text, and whether the owner replied. Use it for review-gap analysis against competitors and to spot unanswered reviews. Usually completes within this call; if the queued job is still running you get status 'processing' plus a taskId — call again with that taskId in 30-60 seconds to collect the result at no extra cost. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `businessName` (`string`, optional): Business name as it appears on Google. Supply exactly one of businessName, cid, or placeId.
- `cid` (`string`, optional): Google-defined business CID (from get_local_serp_results rows). Most precise identifier.
- `depth` (`integer`, optional): Number of reviews to collect (10-200). Defaults to 20. Billed per 10 reviews (per 20 when includeOtherSources is true). Default: `20`.
- `includeOtherSources` (`boolean`, optional): Also collect the reviews Google shows from other sites (Yelp, Tripadvisor, Trustpilot). Defaults to false. Costs more per review and cannot be sorted. Default: `false`.
- `languageCode` (`string`, optional): Language code (e.g. 'en', 'es', 'vi'). Defaults to the project's default market language (see list_projects).
- `locationCode` (`integer`, optional): DataForSEO location code. Ignored when `near` is set; otherwise defaults to the project's market.
- `near` (`object`, optional): Coordinate to search from. Use it when the business name is ambiguous; otherwise locationCode is enough.
- `placeId` (`string`, optional): Google Maps place_id (from get_local_serp_results rows).
- `sortBy` (`string`, optional): Review sort order. Defaults to newest. Ignored when includeOtherSources is true — the extended endpoint has no sort option. One of: `newest`, `highest_rating`, `lowest_rating`, `relevant`. Default: `newest`.
- `taskId` (`string`, optional): Resume a previous call that returned status "processing". Pass back the taskId exactly as returned (format "google:<id>" or "extended:<id>"); it selects the right endpoint on its own. Resuming charges no extra credits.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_business_updates`

Get business updates · Write

Collects the posts (updates, offers, events) published on a Google Business Profile. Use it to check posting activity and recency on your profile or a competitor's. Usually completes within this call; a 'processing' response returns a taskId to call back with in 30-60 seconds at no extra cost. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `businessName` (`string`, optional): Business name as it appears on Google. Supply exactly one of businessName, cid, or placeId.
- `cid` (`string`, optional): Google-defined business CID (from get_local_serp_results rows). Most precise identifier.
- `depth` (`integer`, optional): Number of posts to collect (10-100). Defaults to 10. Default: `10`.
- `languageCode` (`string`, optional): Language code (e.g. 'en', 'es', 'vi'). Defaults to the project's default market language (see list_projects).
- `locationCode` (`integer`, optional): DataForSEO location code. Ignored when `near` is set; otherwise defaults to the project's market.
- `near` (`object`, optional): Coordinate to search from. Use it when the business name is ambiguous; otherwise locationCode is enough.
- `placeId` (`string`, optional): Google Maps place_id (from get_local_serp_results rows).
- `taskId` (`string`, optional): Resume a previous call that returned status "processing". Pass back the taskId exactly as returned. Resuming charges no extra credits.

Example input: `{"projectId":"<projectId>"}`

### `openseomcp_get_domain_keyword_suggestions`

Get Domain Keyword Opportunities · Write

Returns the organic keywords a domain ranks for, including position and available metrics. Use after get_domain_overview when you want the detailed keyword opportunity list for a competitor or reference domain. Charges credits (~100-300 typical). Cached for 12 hours.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `domain` (`string`, required): Competitor or reference domain or URL to extract ranked keywords from.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `languageCode` (`string`, optional): Language code, e.g. 'en', 'es', 'vi'. Defaults to the project's default market language.
- `locationCode` (`integer`, optional): DataForSEO location code, e.g. 2840 for United States or 2504 for Morocco. Defaults to the project's default market. Some countries (e.g. Iceland, 2352) are served from Google Ads data, where keyword difficulty, search intent, and domain analytics are unavailable.
- `scope` (`string`, optional): Research scope: 'domain' (hostname without subdomains), 'subdomains' (hostname plus all subdomains), 'subfolder' (path and its children), or 'exact_url' (one page). Defaults to 'subdomains' for root inputs and 'subfolder' when the input has a path. One of: `exact_url`, `subfolder`, `domain`, `subdomains`.

Example input: `{"domain":"example.com","projectId":"<projectId>"}`

### `openseomcp_get_domain_overview`

Get Domain Overview · Write

Returns a high-level view of a domain's organic footprint: estimated organic traffic, organic keyword count, backlinks, and referring domains. Use this first for domain research; for the detailed ranked-keyword list, call get_domain_keyword_suggestions next. Charges credits (~100-300 typical). Cached for 12 hours per domain.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `domain` (`string`, required): Domain or URL to analyze, e.g. 'example.com'.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `includeSubdomains` (`boolean`, optional): Deprecated: use scope ('subdomains' or 'domain') instead.
- `languageCode` (`string`, optional): Language code, e.g. 'en', 'es', 'vi'. Defaults to the project's default market language.
- `locationCode` (`integer`, optional): DataForSEO location code, e.g. 2840 for United States or 2504 for Morocco. Defaults to the project's default market. Some countries (e.g. Iceland, 2352) are served from Google Ads data, where keyword difficulty, search intent, and domain analytics are unavailable.
- `scope` (`string`, optional): Research scope: 'domain' (hostname without subdomains), 'subdomains' (hostname plus all subdomains), 'subfolder' (path and its children), or 'exact_url' (one page). Defaults to 'subdomains' for root inputs and 'subfolder' when the input has a path. Overview metrics always cover the hostname plus subdomains; narrower scopes are labeled accordingly — use get_ranked_keywords with a scope for scoped keyword data. One of: `exact_url`, `subfolder`, `domain`, `subdomains`.

Example input: `{"domain":"example.com","projectId":"<projectId>"}`

### `openseomcp_get_google_business_questions`

Get Google business questions · Write

Fetches Google Business Profile questions and answers for one business (by businessName, cid, or placeId) near a coordinate. Run this only when Q&A evidence is needed. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `near` (`object`, required): Coordinate and radius to search around.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `businessName` (`string`, optional): Business name as it appears on Google. Supply exactly one of businessName, cid, or placeId.
- `cid` (`string`, optional): Google-defined business CID (from get_local_serp_results rows). Most precise identifier.
- `depth` (`integer`, optional): Maximum Q&A rows to fetch (1-100). Defaults to 20. Default: `20`.
- `languageCode` (`string`, optional): Language code (e.g. 'en', 'es', 'vi'). Defaults to the project's default market language (see list_projects).
- `placeId` (`string`, optional): Google Maps place_id (from get_local_serp_results rows).

Example input: `{"near":{},"projectId":"<projectId>"}`

### `openseomcp_get_keyword_metrics`

Get keyword metrics · Write

Hydrate up to 700 known keywords with search volume, keyword difficulty (KD), search intent, CPC, competition, and monthly trends in a single call. Use it to score candidate or known keywords — including Search Console striking-distance queries — by real demand and ranking difficulty. For countries served from Google Ads data (e.g. Iceland), KD and intent are null. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `keywords` (`array`, required): Keywords to fetch metrics for (1-700).
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `includeClickstreamData` (`boolean`, optional): Refine search volumes with clickstream data, which disaggregates Google Ads' grouped close-variant volumes (plurals/misspellings). DOUBLES the credit cost of the call. Default false. No effect for countries served from Google Ads data. Default: `false`.
- `includeMonthlyTrends` (`boolean`, optional): Include monthly search-volume trend rows. Defaults to true. Default: `true`.
- `languageCode` (`string`, optional): Language code (e.g. 'en', 'es', 'vi'). Defaults to the project's default market language (see list_projects).
- `locationCode` (`integer`, optional): DataForSEO location code. Defaults to the project's default market (see list_projects; editable in project settings). 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.
- `sortBy` (`string`, optional): Sort order for returned rows. Defaults to search_volume. One of: `search_volume`, `keyword_difficulty`, `cpc`, `competition`. Default: `search_volume`.

Example input: `{"keywords":[],"projectId":"<projectId>"}`

### `openseomcp_get_local_rank_grid`

Get local rank grid · Write

Runs one Google Maps search per point of a square grid around a coordinate and reports where the target business ranks at each point — plus each point's result count and #1 business — revealing how far its Maps visibility reaches. Cost scales with the grid: gridSize squared SERP calls (3x3 = 9, the sensible default; 5x5 = 25). Charges credits per grid point.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `center` (`object`, required): Coordinate the grid is centered on (usually the storefront).
- `keyword` (`string`, required): Search query to run on Google Maps at every grid point.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `target` (`object`, required): The business to locate in each result set. Supply at least one of cid, placeId, or name.
- `device` (`string`, optional): Device the SERP is rendered for. Defaults to mobile. One of: `desktop`, `mobile`. Default: `mobile`.
- `gridSize` (`integer`, optional): Grid width: 3 (9 searches) or 5 (25 searches). Defaults to 3. One of: `3`, `5`. Default: `3`.
- `languageCode` (`string`, optional): Language code (e.g. 'en', 'es', 'vi'). Defaults to the project's default market language (see list_projects).
- `spacingKm` (`number`, optional): Distance between neighbouring grid points, in km. Defaults to 2. Default: `2`.
- `zoom` (`integer`, optional): Map zoom every point is searched at. Defaults to a zoom derived from spacingKm and latitude so each point's viewport spans the grid spacing; override only when you need a specific viewport.

Example input: `{"center":{},"keyword":"<keyword>","projectId":"<projectId>","target":{}}`

### `openseomcp_get_local_serp_results`

Get local SERP results · Write

Fetches one Google Maps or Local Finder SERP near a coordinate. Returns trimmed provider rows (identity, rank, rating, categories, hours) with rank fields intact; callers decide how to match a target business. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `keyword` (`string`, required): Search query to run on Google Maps or Local Finder.
- `near` (`object`, required): Coordinate (and optional map zoom) the SERP is fetched from.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `depth` (`integer`, optional): Number of results to fetch (1-100). Defaults to 20. Default: `20`.
- `device` (`string`, optional): Device the SERP is rendered for. Defaults to mobile, matching get_local_rank_grid. One of: `desktop`, `mobile`. Default: `mobile`.
- `languageCode` (`string`, optional): Language code (e.g. 'en', 'es', 'vi'). Defaults to the project's default market language (see list_projects).
- `searchType` (`string`, optional): Which local SERP to fetch. Defaults to maps. One of: `maps`, `local_finder`. Default: `maps`.

Example input: `{"keyword":"<keyword>","near":{},"projectId":"<projectId>"}`

### `openseomcp_get_ranked_keywords`

Get ranked keywords · Write

Returns market-specific keyword, URL, rank, search volume, CPC, intent, and traffic rows for a domain or page. Accepts country-level DataForSEO Labs location/language codes. Use this for strategy evidence; use get_domain_overview for aggregate domain footprint. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `target` (`string`, required): Domain (no protocol/www) or absolute page URL to list ranked keywords for.
- `excludeBrandTerms` (`array`, optional): Exclude keywords containing any of these brand terms.
- `includeSubdomains` (`boolean`, optional): Deprecated: use scope ('subdomains' or 'domain') instead.
- `languageCode` (`string`, optional): Language for locationCode. Defaults to that location's primary language when locationCode overrides the project market.
- `limit` (`integer`, optional): Maximum rows to return (1-100). Defaults to 50. Default: `50`.
- `locationCode` (`integer`, optional): Country-level DataForSEO Labs location code. Defaults to the project's market; takes precedence over the legacy market object.
- `market` (`object`, optional): Legacy US selector. Prefer locationCode/languageCode for any Labs market. Explicit locationCode takes precedence; otherwise omitted = the project's default market.
- `maxRank` (`integer`, optional): Only return keywords ranking at this position or better.
- `minSearchVolume` (`integer`, optional): Only return keywords with at least this monthly search volume.
- `offset` (`integer`, optional): Rows to skip for pagination. Default: `0`.
- `resultTypes` (`array`, optional): SERP result types to include. Defaults to organic and paid.
- `scope` (`string`, optional): Research scope: 'domain' (hostname without subdomains), 'subdomains' (hostname plus all subdomains), 'subfolder' (path and its children), or 'exact_url' (one page). Defaults to 'subdomains' for root inputs and 'subfolder' when the input has a path. One of: `exact_url`, `subfolder`, `domain`, `subdomains`.
- `sortBy` (`string`, optional): Sort order for returned rows. Defaults to search_volume. One of: `rank`, `search_volume`, `traffic_estimate`, `cpc`. Default: `search_volume`.

Example input: `{"projectId":"<projectId>","target":"example.com"}`

### `openseomcp_get_serp_results`

Get Google SERP Results · Write

Fetch live Google organic search results for 1-10 keywords. Use this to inspect who ranks for a query, verify competitors, compare SERPs across keywords, or gather source URLs before content planning. Returns the top `depth` result rows per keyword (default 20). Charges credits per keyword: ~5 each at the default depth 20, and each additional 10 of depth adds ~2.5. Does not save results to OpenSEO. Per-keyword errors don't fail the batch.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `queries` (`array`, required): 1-10 queries. Bulk-friendly — prefer this over multiple single-query calls.
- `depth` (`integer`, optional): How many SERP rows to crawl per keyword — a multiple of 10 from 10 to 100, default 20. Google has no offset, so a deeper crawl re-fetches the top too: each additional 10 adds ~2.5 credits per keyword. Only raise it when you need ranks past the top 20. Default: `20`.

Example input: `{"projectId":"<projectId>","queries":[]}`

### `openseomcp_research_keywords`

Research Keywords (Bulk) · Write

Research keyword data (search volume, difficulty, CPC, related ideas) for 1-5 seed keywords in one call. Charges credits per seed (~30-100 credits each, varies by source; flat ~96 for countries served from Google Ads data, where difficulty/intent are unavailable). Returns per-seed results — a single bad seed won't fail the batch.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `seeds` (`array`, required): 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.
- `includeClickstreamData` (`boolean`, optional): Refine search volumes with clickstream data, which disaggregates Google Ads' grouped close-variant volumes (plurals/misspellings). Doubles the credit cost of each seed. Defaults to false (standard Google-Ads-derived volumes). Has no effect for countries served from Google Ads data. Default: `false`.
- `resultLimit` (`integer`, optional): Maximum related keywords returned per seed: 150, 300, or 500. Defaults to 150. One of: `150`, `300`, `500`. Default: `150`.

Example input: `{"projectId":"<projectId>","seeds":[]}`

### `openseomcp_run_rank_tracker`

Run Rank Tracker · Write

Start an explicit live rank check for every keyword and configured device. This spends credits: call estimate_rank_tracker_cost, show the estimate to the user, and pass the approved credit amount as maxCostCredits. A fresh estimate above that ceiling is rejected. Hosted accounts require a paid plan, while self-hosted deployments are not plan-gated. If a run is already in progress, its blocking run ID is reported without starting or charging another check. The schedule is unchanged.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `maxCostCredits` (`integer`, required): Maximum credits the user approved after seeing estimate_rank_tracker_cost. The run is rejected if its fresh estimate is higher.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `trackerId` (`string`, required): Rank tracker ID from get_rank_tracker.

Example input: `{"maxCostCredits":1,"projectId":"<projectId>","trackerId":"<trackerId>"}`

### `openseomcp_run_site_audit`

Run site audit · Write

Start a site audit: crawls the site (robots.txt-aware, same-origin), checks every page for SEO issues (broken links, duplicate/missing titles and descriptions, redirect chains, orphan pages, canonical problems, thin content, and more), and optionally runs Lighthouse on a sample of pages. Runs in the background — poll get_audit_status, then read get_audit_issues. If the site rate limits the crawler it slows down and retries; pages it still cannot read are honestly flagged as blocked or rate-limited rather than misreported.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID that scopes this call to a specific connected property. Obtain valid project IDs from the list_projects tool. Example: proj_8f3a1c2b9d4e.
- `url` (`string`, required): The start URL to crawl. The crawl stays same-origin and honors the site's robots.txt. Example: https://www.example.com/.
- `maxPages` (`integer`, optional): Page budget for the crawl, between 10 and 10000. Defaults to 50. Default: `50`.
- `runLighthouse` (`boolean`, optional): When true, runs Lighthouse on a sample of up to 10 representative pages, adding several minutes of wall-clock time. Pass true only when performance/Core Web Vitals detail is wanted. Defaults to false. Default: `false`.

Example input: `{"projectId":"<projectId>","url":"<url>"}`

### `openseomcp_search_local_businesses`

Search local businesses · Write

Searches local business listings near a coordinate, with optional rating, review-count, and claimed-status filters. Use this to find local business candidates, nearby competitors, or unclaimed listings; it does not run Maps rank checks or Q&A. Returns a compact row per business (identity, contact, rating, claim status); use get_business_profile for one business's full profile. Charges credits.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `near` (`object`, required): Coordinate and radius to search around.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `categories` (`array`, optional): Business categories to filter by (e.g. 'pizza_restaurant').
- `isClaimed` (`boolean`, optional): Filter by whether the listing is claimed by its owner. false surfaces unclaimed listings (outreach prospects).
- `limit` (`integer`, optional): Maximum businesses to return (1-50). Defaults to 20. Default: `20`.
- `minRating` (`number`, optional): Only return businesses rated at least this (1-5).
- `minReviews` (`integer`, optional): Only return businesses with at least this many Google reviews.
- `offset` (`integer`, optional): Rows to skip for pagination. Default: `0`.
- `query` (`string`, optional): Business name or title text to match.
- `sortBy` (`string`, optional): Sort order for returned rows. Defaults to relevance. One of: `relevance`, `rating`, `reviews`. Default: `relevance`.

Example input: `{"near":{},"projectId":"<projectId>"}`

### `openseomcp_remove_rank_tracking_keywords`

Remove Rank Tracking Keywords · Destructive

Stop tracking keywords by their trackingKeywordId. Uses no credits and preserves historical snapshots. Missing, stale, foreign, and repeated IDs are ignored; `removed` is the number actually deleted from this tracker.

Read only: no. Destructive: yes. Idempotent (safe to retry): no.

Inputs:

- `keywordIds` (`array`, required): Tracking keyword IDs to remove. Use `trackingKeywordId` values returned by get_rank_tracker.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `trackerId` (`string`, required): Rank tracker ID from get_rank_tracker.

Example input: `{"keywordIds":[],"projectId":"<projectId>","trackerId":"<trackerId>"}`

### `openseomcp_save_keywords`

Save Keywords · Destructive

Save keywords to a project's saved-keywords list. Uses no credits — does not call DataForSEO. Idempotent: re-saving an existing keyword is a no-op. If tags are provided, missing tags may be created. By default tags are appended; set tagMode=replace to remove existing tags from these saved keywords before applying the provided tags, which is useful for reorganizing keywords into page/topic clusters. Ask the user for confirmation before applying or replacing tags broadly.

Read only: no. Destructive: yes. Idempotent (safe to retry): no.

Inputs:

- `keywords` (`array`, required): Keywords to save, 1-100. Saving an already-saved keyword is a no-op.
- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `languageCode` (`string`, optional): Language code, e.g. 'en', 'es', 'vi'. Defaults to the project's default market language.
- `locationCode` (`integer`, optional): DataForSEO location code, e.g. 2840 for United States or 2504 for Morocco. Defaults to the project's default market. Some countries (e.g. Iceland, 2352) are served from Google Ads data, where keyword difficulty, search intent, and domain analytics are unavailable.
- `metrics` (`array`, optional): Optional cached metrics for the saved keywords, one entry per keyword matched by its keyword field. Copy searchVolume, keywordDifficulty, cpc, competition and intent straight from the matching research_keywords rows, and map each row's trend to monthlySearches.
- `tagMode` (`string`, optional): How to apply tags: 'append' adds the given tags to any existing ones (default); 'replace' removes existing tags from these saved keywords before applying the given tags — useful for reorganizing keywords into page/topic clusters. One of: `append`, `replace`. Default: `append`.
- `tags` (`array`, optional): Optional tags to attach to every saved keyword in this call. Missing tag names may be created. Ask the user for explicit confirmation before using this, especially when saving many keywords or creating new tag names.

Example input: `{"keywords":[],"projectId":"<projectId>"}`

### `openseomcp_update_project_context`

Update Project Context · Destructive

Writes to a project's shared memory so the app, SAM, and other agents see it. Uses no credits. Send a list of patch ops; sections are prose (~4,000 chars max), competitors and key pages are curated shortlists (100 max each), and appendResearchLog records what research was bought so nobody re-buys it. Confirm facts with the user before storing them.

Read only: no. Destructive: yes. Idempotent (safe to retry): no.

Inputs:

- `projectId` (`string`, required): Required. The OpenSEO project ID to scope this call to. Get one from list_projects.
- `updates` (`array`, required): Patch ops, applied in order. Empty section content clears the section; adds upsert by domain/url; research-log entries are date-stamped by the server.

Example input: `{"projectId":"<projectId>","updates":[]}`

## Related

Other Marketing connectors ([all 59](/agentkit/connectors/?category=marketing)):

- [HubSpot](/agentkit/connectors/hubspot/): Scalekit connector, OAuth, 457 tools
- [Mailchimp](/agentkit/connectors/mailchimp/): Scalekit connector, OAuth, 73 tools
- [Twitter / X](/agentkit/connectors/twitteroauth/): Scalekit connector, OAuth, 92 tools


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
