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.
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/sdk1.30, stateless by default) - Stack: Node 20+, TypeScript, Express 5, Zod 4
Contents
- Architecture
- Tool layers
- Available tools
- API family coverage
- Local development
- Environment variables
- Railway deployment
- Connecting Claude
- DataForSEO authentication
- Asynchronous DataForSEO jobs
- Cost controls
- Response modes and pagination
- Locations, languages, filters and sorting
- The generic request layer
- Security model
- Adding a new high-level wrapper
- Testing
- Sandbox mode
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
- Push this repository to GitHub and create a Railway project from it.
- Railway detects Node via Nixpacks;
railway.jsonpins the commands:- build:
npm ci && npm run build - start:
npm run start - healthcheck:
/health
- build:
- Set variables in the Railway service:
DATAFORSEO_LOGIN,DATAFORSEO_PASSWORD(required)- optionally
MCP_AUTH_TOKEN,PUBLIC_URL,LOG_LEVEL, limit overrides - do not set
PORTmanually; Railway provides it
- 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_TOKENis 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 indataforseo/auth.ts - attaches it inside
DataForSeoClient.send()immediately beforefetch - 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_statusdeliberately omits the accountlogin, and/healthreports 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
limitis 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_offsetis 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_postis 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 toMAX_RESPONSE_CHARS)limit/offset, withpagination.next_offsetreturned 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
GETandPOSTare 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.credentialsand theAuthorizationheader; 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_TOKENfor bearer auth (constant-time comparison, multiple tokens for rotation). The gate is one middleware inmcp/transport.ts, so OAuth can replace it without touching the DataForSEO integration./healthstays open for platform probes. - Origin validation (
MCP_ALLOWED_ORIGINS) protects browser-originated callers against DNS rebinding; requests with noOrigin(Claude, curl) are unaffected. - Rate limiting on
/mcpviaexpress-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
- Add the endpoint to
ENDPOINT_CATALOGinsrc/dataforseo/endpoints.ts(this powers discovery). - Add a normaliser in
src/normalizers/<family>.tsusingbyMode()sosummary/standard/fullall behave. - Add a service function in
src/services/<family>.tsthat builds the task object, callsctx.client.post(...)and returns{ data, meta: metaFrom(response), notes?, pagination? }. - Register the tool in
src/mcp/tools/<family>.tswithdefineTool()and a strict Zod schema (reuseschemas/common.tsfields). - 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.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。