DataForSEO MCP Server

DataForSEO MCP Server

A production-ready MCP server that gives Claude access to the entire DataForSEO API v3 through 51 tools, covering SERP, Labs, Keywords, Backlinks, OnPage, Business, and more, with cost controls and response normalization.

Category
Visit Server

README

DataForSEO MCP Server

A production-ready remote MCP server that exposes the DataForSEO API v3 to Claude over the Streamable HTTP transport, designed to run on Railway.

It gives Claude access to essentially the whole DataForSEO surface — SERP, DataForSEO Labs, Keywords Data, Backlinks, OnPage, Business Data, Content Analysis, Merchant, App Data, Domain Analytics, Content Generation and AI Optimization — through 51 tools rather than several hundred, with cost controls, response normalisation and a strictly bounded generic request layer.

  • Endpoint: POST https://<your-app>.up.railway.app/mcp
  • Transport: Streamable HTTP (@modelcontextprotocol/sdk 1.30, stateless by default)
  • Stack: Node 20+, TypeScript, Express 5, Zod 4

Contents


Architecture

src/
  server.ts              entry point: listen, graceful shutdown
  app.ts                 Express app: /, /health, /mcp, limits, error handling
  config.ts              env parsing; the only place credentials live

  mcp/
    server.ts            McpServer factory + model-facing instructions
    transport.ts         Streamable HTTP wiring, auth gate, origin gate, sessions
    context.ts           ToolContext (config, client, metadata, result builder)
    tools/               tool definitions, one module per domain
      discovery.ts keywords.ts domain.ts serp.ts backlinks.ts
      onpage.ts business.ts content.ts account.ts generic.ts helpers.ts

  dataforseo/
    client.ts            the single outbound HTTP path (auth, retries, logging)
    auth.ts              HTTP Basic header construction, forbidden headers
    endpoints.ts         family registry, allow-list, endpoint catalog, metadata sources
    errors.ts            DataForSEO status-code model → typed errors
    types.ts             response envelope types

  services/              per-family request building + normalisation
    serp.ts labs.ts keywords.ts backlinks.ts onpage.ts business.ts
    content.ts account.ts generic.ts metadata.ts types.ts

  normalizers/           DataForSEO response → concise SEO objects
    serp.ts keywords.ts labs.ts backlinks.ts onpage.ts business.ts content.ts common.ts

  schemas/common.ts      shared Zod fields, filter/sort validation, target normalisation
  utils/                 logger (redacting), redaction, TTL cache, semaphore, result builder

tests/                   vitest suite; all DataForSEO calls are mocked
scripts/
  verify-mcp.mjs         local Streamable HTTP + tools/list verification
  smoke.mjs              MANUAL smoke test against real DataForSEO credentials

Data flows in one direction: tool → service → client → DataForSEO, and back client → normaliser → result builder → tool. Only dataforseo/client.ts performs network I/O, and every request it makes passes validateEndpointPath() first.

Tool layers

Layer 1 — discovery. dataforseo_capabilities, dataforseo_locations, dataforseo_languages, dataforseo_endpoint_info. These let Claude learn what exists, resolve identifiers and confirm a path before spending money. dataforseo_capabilities and dataforseo_endpoint_info make no DataForSEO call at all.

Layer 2 — curated tools. 46 tools covering the common SEO workflows with strict Zod schemas, friendly parameters, resolved locations/languages and normalised output.

Layer 3 — the generic request. dataforseo_request reaches any documented endpoint under an allowed /v3/<family>/ prefix. New DataForSEO endpoints are usable the day they ship, without changing this server.

Available tools

Tool What it does DataForSEO endpoint
dataforseo_capabilities Families, coverage, limits, workflow hints (local)
dataforseo_locations Search location codes by name */locations, dataforseo_labs/locations_and_languages
dataforseo_languages Search language codes */languages
dataforseo_endpoint_info Explain/validate an endpoint, live vs async (local)
account_status Balance, limits, prices appendix/user_data
keyword_overview Volume, CPC, competition, difficulty, intent dataforseo_labs/google/keyword_overview/live
keyword_suggestions Long-tail keywords containing a seed dataforseo_labs/google/keyword_suggestions/live
related_keywords "Searches related to" expansion (depth 0–4) dataforseo_labs/google/related_keywords/live
keyword_ideas Category-based keyword ideas dataforseo_labs/google/keyword_ideas/live
search_intent Informational/commercial/etc. classification dataforseo_labs/google/search_intent/live
keyword_historical_volume Monthly history per keyword dataforseo_labs/google/historical_keyword_data/live
keywords_search_volume Google Ads / Bing Ads volumes keywords_data/{google_ads,bing}/search_volume/live
keyword_gap Keywords a competitor has and you don't dataforseo_labs/google/domain_intersection/live (intersections:false)
domain_overview Organic/paid metrics for a domain dataforseo_labs/google/domain_rank_overview/live
domain_ranked_keywords Every keyword a domain ranks for dataforseo_labs/google/ranked_keywords/live
page_ranked_keywords Keywords for one URL dataforseo_labs/google/ranked_keywords/live
domain_competitors Organic competitors dataforseo_labs/google/competitors_domain/live
domain_intersection Keywords two domains share dataforseo_labs/google/domain_intersection/live
page_intersection Keywords where pages rank together dataforseo_labs/google/page_intersection/live
relevant_pages Best pages of a domain dataforseo_labs/google/relevant_pages/live
serp_competitors Domains ranking for a keyword set dataforseo_labs/google/serp_competitors/live
bulk_traffic_estimation Traffic for up to 1000 targets dataforseo_labs/google/bulk_traffic_estimation/live
live_serp Live SERP, 7 engines, many search types serp/{engine}/{type}/live/advanced
rank_check Where a domain/URL ranks for a keyword serp/{engine}/organic/live/advanced
maps_serp Google Maps / Local Finder results serp/google/{maps,local_finder}/live/advanced
serp_task_post Queue a SERP task (cheaper) serp/{engine}/{type}/task_post
serp_task_get Collect a SERP task serp/{engine}/{type}/task_get/advanced/{id}
serp_tasks_ready List finished SERP tasks serp/{engine}/organic/tasks_ready
backlinks_summary Backlink profile summary backlinks/summary/live
backlinks Individual backlinks backlinks/backlinks/live
referring_domains Referring domains backlinks/referring_domains/live
backlink_anchors Anchor text distribution backlinks/anchors/live
backlink_competitors Shared-referrer competitors backlinks/competitors/live
backlinks_history Monthly link history backlinks/history/live
backlinks_bulk_metrics Bulk rank/backlinks/spam/refdomains backlinks/bulk_*/live
onpage_start_audit Start a crawl (async) on_page/task_post
onpage_audit_status Crawl progress / finished crawls on_page/summary/{id}, on_page/tasks_ready
onpage_summary Site-level technical SEO results on_page/summary/{id}
onpage_pages Crawled pages + per-page checks on_page/pages
onpage_issues Duplicate tags/content, non-indexable, redirects, links on_page/{issue_type}
onpage_resources Images/scripts/stylesheets on_page/resources
onpage_instant_page Analyse one URL immediately on_page/instant_pages
onpage_lighthouse Lighthouse scores on_page/lighthouse/live/json
onpage_force_stop Stop a running crawl on_page/force_stop
business_search Search local business listings business_data/business_listings/search/live
business_info Google Business Profile (async) business_data/google/my_business_info/task_*
business_reviews Google reviews (async) business_data/google/reviews/task_*
content_mentions Brand/keyword citations content_analysis/search/live
content_summary Mention volume aggregation content_analysis/summary/live
content_sentiment Sentiment of mentions content_analysis/sentiment_analysis/live
dataforseo_request Any allowed /v3/ endpoint (generic)

API family coverage

Family Coverage Notes
SERP Direct + generic Google (organic, maps, local finder, news, images, jobs, events, shopping, autocomplete, ai_mode…), Bing, Yahoo, YouTube, Baidu, Naver, Seznam. Live and task-based flows are first class. Less common Google verticals (finance, ads_search, dataset_search, screenshot, search-by-image) go through dataforseo_request.
DataForSEO Labs Direct + generic 13 dedicated tools. keywords_for_site, subdomains, bulk_keyword_difficulty, historical_serps, categories, top_searches, and the Amazon / Google Play / App Store Labs datasets via dataforseo_request.
Keywords Data Direct + generic search_volume for Google Ads and Bing directly; keywords_for_site, keywords_for_keywords, ad_traffic_by_keywords, Google Trends, DataForSEO Trends and clickstream via dataforseo_request.
Backlinks Direct + generic 7 tools cover the common profile work; referring_networks, intersections, timeseries, domain_pages*, remaining bulk endpoints via dataforseo_request.
OnPage Direct + generic Full async crawl lifecycle, pages, resources, links, duplicates, non-indexable, redirects, instant pages, Lighthouse. waterfall, keyword_density, microdata, raw_html, content_parsing, page_screenshot via dataforseo_request.
Business Data Direct + generic Listings search, Business Profile, reviews. Q&A, hotel searches/info, Trustpilot, Tripadvisor, social media via dataforseo_request.
Content Analysis Direct + generic Search, summary, sentiment directly; rating distribution, phrase/category trends via dataforseo_request.
Merchant Generic Google Shopping and Amazon products/ASIN/sellers.
App Data Generic Google Play and App Store searches, info, listings, reviews.
Domain Analytics Generic Technology detection and Whois with filters.
Content Generation Generic Generate, paraphrase, grammar check, meta tags, text summary.
AI Optimization Generic LLM responses/scrapers (ChatGPT, Claude, Gemini, Perplexity), LLM mentions, AI keyword volume.
Appendix Direct + generic account_status; errors/status via dataforseo_request.

Not reachable through this server: DataForSEO features that are not v3 REST endpoints — the client dashboards, webhook delivery to this server (you can still set postback_url / pingback_url on a task, but nothing here receives them), and any endpoint outside the thirteen /v3/ families above.

Local development

npm install
cp .env.example .env        # fill in DATAFORSEO_LOGIN / DATAFORSEO_PASSWORD
npm run dev                 # tsx watch on http://localhost:3000

npm run typecheck
npm test                    # mocked, no network, no cost
npm run build && npm run verify:mcp   # boots the built server and drives real MCP calls

Point any MCP client at http://localhost:3000/mcp, or use the MCP Inspector:

npx @modelcontextprotocol/inspector

Environment variables

Required:

Variable Purpose
DATAFORSEO_LOGIN DataForSEO API login (server-side only)
DATAFORSEO_PASSWORD DataForSEO API password (server-side only)
PORT HTTP port — Railway injects this automatically

Optional:

Variable Default Purpose
DATAFORSEO_SANDBOX false Route everything to sandbox.dataforseo.com (free, dummy data)
DATAFORSEO_BASE_URL https://api.dataforseo.com Override; must be an https *.dataforseo.com host
DATAFORSEO_TIMEOUT_MS 60000 Per-request timeout (max 180000)
DATAFORSEO_MAX_RETRIES 2 Retries for transient failures (never for task_post)
DATAFORSEO_MAX_CONCURRENCY 8 Max in-flight DataForSEO requests
MCP_AUTH_TOKEN (unset) Comma-separated bearer tokens required on /mcp
MCP_SESSION_MODE stateless stateless or stateful
MCP_SESSION_IDLE_TIMEOUT_MS 1800000 Stateful mode only: reap sessions idle this long
MCP_ALLOWED_ORIGINS (unset) Comma-separated allowed Origin values
PUBLIC_URL (unset) Public base URL, echoed on /
MAX_RESULT_ITEMS 50 Default rows per tool call
MAX_RESULT_ITEMS_HARD_CAP 1000 Absolute row ceiling
MAX_RESPONSE_CHARS 100000 Character budget per tool response
MAX_BATCH_ITEMS 100 Max keywords/targets per batched call
RATE_LIMIT_WINDOW_MS / RATE_LIMIT_MAX 60000 / 120 Rate limit on this server
MAX_REQUEST_BODY_SIZE 1mb JSON body limit on /mcp
METADATA_CACHE_TTL_MS 21600000 Locations/languages cache TTL (6h)
LOG_LEVEL info debug/info/warn/error/silent
TRUST_PROXY 1 Express trust proxy setting

.env.example contains names only — never commit real values.

Railway deployment

  1. Push this repository to GitHub and create a Railway project from it.
  2. Railway detects Node via Nixpacks; railway.json pins the commands:
    • build: npm ci && npm run build
    • start: npm run start
    • healthcheck: /health
  3. Set variables in the Railway service:
    • DATAFORSEO_LOGIN, DATAFORSEO_PASSWORD (required)
    • optionally MCP_AUTH_TOKEN, PUBLIC_URL, LOG_LEVEL, limit overrides
    • do not set PORT manually; Railway provides it
  4. Deploy, then check https://<app>.up.railway.app/health.

Notes: /health returns 503 while credentials are missing, so a misconfigured deploy fails its healthcheck by design. trust proxy is on for Railway's proxy. SIGTERM triggers graceful shutdown (open MCP sessions closed, then the HTTP server). Nothing is written to disk — state is in memory only, so replicas and restarts are safe (stateless session mode is the default for exactly this reason). Docker is not required.

Connecting Claude

Claude.ai / Claude Desktop → Settings → Connectors → Add custom connector:

  • URL: https://<your-app>.up.railway.app/mcp
  • If MCP_AUTH_TOKEN is set, supply the token as a bearer token in the connector's authentication settings.

Claude Code:

claude mcp add --transport http dataforseo https://<your-app>.up.railway.app/mcp \
  --header "Authorization: Bearer <token>"

Ask Claude to call dataforseo_capabilities first — it returns the map of families, tools and limits.

DataForSEO authentication

DataForSEO uses HTTP Basic auth. This server:

  • builds the Authorization: Basic base64(login:password) header in dataforseo/auth.ts
  • attaches it inside DataForSeoClient.send() immediately before fetch
  • never places it on any object that is logged or serialised
  • never returns login, password, encoded credentials or authorization headers to the MCP client — account_status deliberately omits the account login, and /health reports only whether credentials are configured

Tool callers cannot supply headers at all, and dataforseo_request rejects bodies containing login, password, authorization, headers, credentials or api_key at any depth.

Asynchronous DataForSEO jobs

Task-based DataForSEO endpoints are exposed honestly — no request is ever held open waiting for a long job.

Workflow Start Poll / collect
SERP tasks serp_task_post → task_id serp_tasks_ready, then serp_task_get
Site crawls onpage_start_audit → task_id onpage_audit_status until crawl_progress: "finished", then onpage_summary / onpage_pages / onpage_issues
Business Profile / reviews business_info / business_reviews with action: "start" same tool with action: "collect" and the task_id
Anything else dataforseo_request on a task_post path dataforseo_request on the matching task_get path

While DataForSEO is still working (status codes 40601/40602) the collect tools return ready: false with a note, rather than an error or a hanging request.

Cost controls

DataForSEO is usage-priced, so the defaults are deliberately conservative:

  • default limit is 50 rows (MAX_RESULT_ITEMS), hard-capped at 1000
  • keyword/target batches are capped at MAX_BATCH_ITEMS (100) with an explicit error rather than silent truncation
  • crawls default to max_crawl_pages: 100; SERP depth defaults to 100 elements
  • nothing recursively pages: next_offset is returned and Claude must ask for the next page
  • no tool fans out into multiple DataForSEO calls; bulk endpoints are preferred and documented in the tool descriptions
  • task_post is never retried, so a task cannot be created (and billed) twice
  • concurrency is bounded by a semaphore (DATAFORSEO_MAX_CONCURRENCY)
  • every response preserves DataForSEO's own accounting:
"usage": { "dataforseo_cost": 0.0123, "tasks_count": 1, "result_count": 100, "duration_ms": 812 }

Every outbound call is logged as one structured line (dataforseo_request) with endpoint, method, status, duration, cost and result counts — and never any credential.

Response modes and pagination

Every list-returning tool accepts:

  • response_mode: summary (headline fields) · standard (default; the useful SEO fields) · full (raw DataForSEO fields, still subject to MAX_RESPONSE_CHARS)
  • limit / offset, with pagination.next_offset returned when a full page came back

If a response would still exceed the character budget, rows are dropped from the end and an output_truncated block explains exactly what was omitted and how to narrow the request.

Locations, languages, filters and sorting

Pass natural values — "United Kingdom", "London,England,United Kingdom", "Marbella, Andalusia, Spain", "English", "Spanish" — and they are resolved against the correct per-family DataForSEO metadata list (SERP, Labs and Keywords Data use different location code sets). Numeric location codes and ISO language codes are accepted verbatim and skip the lookup entirely. Ambiguous names produce an error listing the candidates; metadata lists are cached in memory for METADATA_CACHE_TTL_MS.

DataForSEO's filtering power is preserved, not removed: filters accepts documented expressions such as

[["keyword_data.keyword_info.search_volume", ">", 1000], "and", ["keyword_data.keyword_info.cpc", "<", 5]]

Field paths, operators, value types and nesting depth are validated before the request is sent. order_by accepts up to 3 rules of the form "field,asc" / "field,desc".

The generic request layer

{
  "endpoint": "/v3/domain_analytics/technologies/domain_technologies/live",
  "method": "POST",
  "body": { "target": "example.com" },
  "response_mode": "standard",
  "item_limit": 50
}

Guarantees enforced by validateEndpointPath() and services/generic.ts:

  • the host is always the configured DataForSEO base URL; callers supply a path, never a URL
  • the path must start with /v3/ and sit under one of the thirteen allowed families
  • absolute URLs, protocol-relative paths, .., query strings, fragments, @, backslashes, whitespace and control characters are rejected
  • only GET and POST are possible
  • no header input is accepted, so authentication cannot be injected or overridden
  • credential-shaped body keys are rejected at any depth
  • bodies are capped at 128 KB and 100 task objects

It is an escape hatch for DataForSEO, not an HTTP proxy.

Security model

  • Credentials stay in config.credentials and the Authorization header; they are registered with the redaction layer so any accidental appearance in logs or tool output is masked.
  • MCP authentication is optional and pluggable: set MCP_AUTH_TOKEN for bearer auth (constant-time comparison, multiple tokens for rotation). The gate is one middleware in mcp/transport.ts, so OAuth can replace it without touching the DataForSEO integration. /health stays open for platform probes.
  • Origin validation (MCP_ALLOWED_ORIGINS) protects browser-originated callers against DNS rebinding; requests with no Origin (Claude, curl) are unaffected.
  • Rate limiting on /mcp via express-rate-limit, plus a JSON body size limit.
  • Error sanitisation: DataForSEO and internal errors become structured payloads (kind, message, hint, retryable); stack traces never reach the client.
  • No filesystem persistence, no eval, no dynamic endpoint construction from caller input beyond the validated path.

Adding a new high-level wrapper

  1. Add the endpoint to ENDPOINT_CATALOG in src/dataforseo/endpoints.ts (this powers discovery).
  2. Add a normaliser in src/normalizers/<family>.ts using byMode() so summary / standard / full all behave.
  3. Add a service function in src/services/<family>.ts that builds the task object, calls ctx.client.post(...) and returns { data, meta: metaFrom(response), notes?, pagination? }.
  4. Register the tool in src/mcp/tools/<family>.ts with defineTool() and a strict Zod schema (reuse schemas/common.ts fields).
  5. Add tests: schema rejection, a mocked happy path, and the normaliser.

Until a wrapper exists, the endpoint is already usable through dataforseo_request.

Testing

npm test          # 109 tests: credentials, allow-list, error parsing, location/language
                  # resolution, generic-request protection, normalisers, tool schemas, HTTP
npm run verify:mcp # Streamable HTTP initialize + tools/list against the built server
npm run smoke      # MANUAL only — real credentials; defaults to the sandbox

The automated suite mocks every DataForSEO call and never makes a paid request. scripts/smoke.mjs is the only script that talks to DataForSEO; it uses the sandbox unless you pass --production, and only makes a billed call with --production --paid.

Sandbox mode

DataForSEO offers a free sandbox at https://sandbox.dataforseo.com that mirrors the production response structure with dummy data. Set DATAFORSEO_SANDBOX=true to route every call there — useful for development and for verifying a deployment without spending credits. Responses are tagged with "sandbox": true so Claude knows the data is not real. Production behaviour is unchanged when the flag is unset.


Built against DataForSEO API v3 documentation and the Model Context Protocol specification as published in August 2026.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured