| name | seo-research |
| description | Pull SEO metrics from the DataForSEO API v3 — keyword research (search volume, CPC, keyword difficulty, search intent, keyword ideas), competitor & domain analysis (which keywords a domain ranks for, organic traffic estimates, competitors, keyword gaps, top pages), live Google SERP & rank checking, and backlink profiles (referring domains, anchors, link quality). Use this whenever the user wants SEO data, keyword metrics, search volume, keyword difficulty, SERP positions, rankings, competitor research, traffic estimates, or backlink analysis for any website or project — and especially whenever DataForSEO is mentioned. Routes the request to the right endpoint and runs it via a generic client script using DATAFORSEO_LOGIN / DATAFORSEO_PASSWORD from the environment. Triggers on requests like "how much search volume does X get", "what keywords does competitor.com rank for", "find SEO competitors for my site", "check our Google ranking for Y", "analyze the backlinks of Z", "keyword research for this project", or any SEO metrics / keyword / ranking / backlink question. |
| user-invocable | true |
| allowed-tools | ["Bash","Read","Write","Glob","Grep","AskUserQuestion","WebFetch","Task"] |
| argument-hint | describe the SEO research you need, e.g. "keywords competitor.com ranks for in the UK" or "search volume for these 5 keywords" |
DataForSEO SEO Research
Answer SEO questions by routing them to the right DataForSEO API v3 endpoint(s), fetching the full raw response to a file, and analysing that file. DataForSEO is a paid API — every call costs money (reported in the response), so pick the right endpoint and confirm scope before running broad/expensive queries.
The core principle: fetch raw, save to /tmp, then analyse the saved file. Never try to slim or pre-extract the API response on the way in — the responses are rich and the schema changes as DataForSEO adds fields, so anything you drop at fetch time is data you can't get back. With a large context window there's no need to extract; read the whole raw file and pull what you need from it during analysis.
Prerequisites
Environment variables (set in ~/.bashrc):
If either is missing, tell the user exactly which one and how to set it (export DATAFORSEO_LOGIN=... / export DATAFORSEO_PASSWORD=... in ~/.bashrc, then restart the shell). Do not proceed without both. To test without spending money, add --sandbox to the script — it returns dummy data with the same structure, free of charge.
The model (read references/api-mechanics.md for detail)
- Uniform API: every endpoint uses HTTP Basic auth, a POST body that is a JSON array of task objects, and a response envelope
tasks[] → result[] → items[]. One script (dfs.mjs) calls any of them.
- Prefer LIVE endpoints (
.../live) for interactive research — instant, no polling. Use task-based (task_post/task_get) only for cheap, high-volume batches.
- Success = top-level
status_code 20000. The script enforces this and surfaces errors with hints.
- Cost is in every response — report it to the user after each call.
- Most endpoints need a location and language (
location_name/location_code + language_name/language_code).
Workflow
1. Locate the script
Glob: **/seo-research/scripts/dfs.mjs → store as SCRIPT_PATH
The install path varies across systems, so resolve it with Glob rather than hardcoding.
2. Plan the fetches
Map the user's question to one or more endpoints using the routing table below. A single question often needs several endpoints (e.g. "research these competitors" → ranked_keywords + competitors_domain + domain_rank_overview). For each one, read its reference file to get the exact parameters before building the payload.
Resolve location/language: if the user named a country/market, use it; otherwise ask (via AskUserQuestion) or default to the project's primary market and say so.
Present the plan as a short checklist so the user can see what will run and what it will roughly cost:
Planned fetches:
1. ranked_keywords — keywords nike.com ranks for (US/en, limit 500)
2. competitors_domain — nike.com organic competitors (US/en, limit 50)
3. domain_rank_overview — nike.com traffic/positions scorecard (US/en)
For broad or potentially expensive queries (large limit/depth, many tasks, SERP live/advanced), confirm before running.
3. Fetch — raw to /tmp
Build each payload as a JSON array of task objects per the reference. For anything beyond a trivial payload, write it to a temp file to avoid shell-quoting issues.
One endpoint → run it directly with Bash:
node "$SCRIPT_PATH" \
--endpoint "/v3/dataforseo_labs/google/ranked_keywords/live" \
--payload-file /tmp/dfs-payload-ranked.json \
--output /tmp/dfs-raw-ranked-$(date +%s).json
The script saves the full raw response to --output, prints a compact summary (status, cost, per-task result_count/items_count) to stdout, and the cost to stderr. Add --sandbox to test free, --method GET for GET utility endpoints (locations/languages, task_get).
Two or more endpoints → fetch them in parallel via subagents. Don't run them one after another — DataForSEO calls have real latency, so dispatch all of them in the same turn as parallel Task (general-purpose) subagents and let the responses come back asynchronously. Each subagent does the heavy plumbing (reads its reference, builds the payload, runs the script, handles errors) in its own context, so the main context stays clean. Give each subagent this brief:
You are fetching one DataForSEO endpoint. Do exactly this, nothing more:
- Read the reference file: <path to references/*.md for this endpoint>
- Endpoint: <path>
- Goal: <what to fetch, e.g. "keywords nike.com ranks for">
- Constraints: location=<...>, language=<...>, limit=<...>, plus any filters
- Build the JSON-array payload, write it to /tmp/dfs-payload-<slug>.json
- Run: node "<SCRIPT_PATH>" --endpoint "<path>" --payload-file <payload> --output /tmp/dfs-raw-<slug>-<unique>.json
- Return ONLY: the endpoint, the output file path, status_code, cost, and items_count.
- Do NOT read, summarise, or paste the response body. Do NOT use --raw. The raw file on disk is the deliverable.
Collect the returned file paths and costs. If a subagent reports a non-20000 status, surface its error and stop — don't retry blindly.
4. Analyse the raw files
Read the saved raw file(s) directly and answer the user's question from them — every field DataForSEO returned is there, nothing was dropped on the way in. The data lives under tasks[] → result[] → items[]; each reference describes what those items contain.
For large files, jq is a convenient way to slice out the rows you want to present (it's a tool for reading the saved raw, not for pre-extraction):
jq -r '.tasks[0].result[0].items[]
| [.keyword_data.keyword,
.ranked_serp_element.serp_item.rank_absolute,
.keyword_data.keyword_info.search_volume] | @tsv' /tmp/dfs-raw-ranked-*.json | head -50
If jq is unavailable, use a short node -e snippet or Read the file. Present results as a compact table relevant to the question, and report the total cost across all fetches.
Endpoint routing table
Pick by what the user wants to know. Paths are appended to https://api.dataforseo.com. Read the linked reference to build the payload.
Keyword research → references/labs-keyword-research.md (+ keywords-data.md)
| The user wants… | Endpoint |
|---|
| Keyword ideas from seed terms (broad topical set) | /v3/dataforseo_labs/google/keyword_ideas/live |
| Long-tail keywords containing a phrase | /v3/dataforseo_labs/google/keyword_suggestions/live |
| "Searches related to" / topic clusters | /v3/dataforseo_labs/google/related_keywords/live |
| Full metrics for a known keyword list | /v3/dataforseo_labs/google/keyword_overview/live |
| Keyword difficulty for many keywords | /v3/dataforseo_labs/google/bulk_keyword_difficulty/live |
| Search intent classification | /v3/dataforseo_labs/google/search_intent/live |
| Official Google Ads volume/CPC/competition | /v3/keywords_data/google_ads/search_volume/live |
| Volume/CPC trend over time | /v3/dataforseo_labs/google/historical_keyword_data/live |
| Relative interest trend (Google Trends) | /v3/keywords_data/google_trends/explore/live |
| PPC traffic/cost forecast at a bid | /v3/keywords_data/google_ads/ad_traffic_by_keywords/live |
For search volume: DataForSEO Labs (keyword_overview) gives richer, more precise metrics; Google Ads search_volume gives the official rounded Ads numbers. Choose based on what the user needs.
Competitor & domain analysis → references/labs-domain-competitor-research.md
| The user wants… | Endpoint |
|---|
| Which keywords a domain/page ranks for (+ positions) | /v3/dataforseo_labs/google/ranked_keywords/live |
| A domain's SEO scorecard (traffic, positions) | /v3/dataforseo_labs/google/domain_rank_overview/live |
| Find a domain's organic competitors | /v3/dataforseo_labs/google/competitors_domain/live |
| Keyword gap / overlap between two domains | /v3/dataforseo_labs/google/domain_intersection/live |
| A domain's top-performing pages | /v3/dataforseo_labs/google/relevant_pages/live |
| Subdomain performance breakdown | /v3/dataforseo_labs/google/subdomains/live |
| Who dominates a set of keywords' SERPs | /v3/dataforseo_labs/google/serp_competitors/live |
| Traffic estimate for many domains/pages at once | /v3/dataforseo_labs/google/bulk_traffic_estimation/live |
| A domain's visibility trend over months | /v3/dataforseo_labs/google/historical_rank_overview/live |
| Keyword ideas relevant to a site | /v3/dataforseo_labs/google/keywords_for_site/live |
SERP & rank checking → references/serp-api.md
| The user wants… | Endpoint |
|---|
| Live Google SERP with all features | /v3/serp/google/organic/live/advanced |
| Live Google SERP, rankings only (cheaper) | /v3/serp/google/organic/live/regular |
| Check a domain's position for a keyword | live/advanced then match items[].domain → rank_group |
| Cheap, queued, bulk SERP | /v3/serp/google/organic/task_post → task_get |
For rankings across many keywords for one domain, prefer Labs ranked_keywords over per-keyword SERP calls.
Backlinks → references/backlinks.md
| The user wants… | Endpoint |
|---|
| Backlink profile summary (rank, counts, spam) | /v3/backlinks/summary/live |
| List of individual backlinks | /v3/backlinks/backlinks/live |
| Referring domains | /v3/backlinks/referring_domains/live |
| Anchor-text distribution | /v3/backlinks/anchors/live |
| Most-linked pages of a domain | /v3/backlinks/domain_pages/live |
| Backlink competitors | /v3/backlinks/competitors/live |
| Backlink rank for many targets | /v3/backlinks/bulk_ranks/live |
Also available → references/onpage-and-domain-analytics.md
| The user wants… | Endpoint |
|---|
| Technical audit of a single page (instant) | /v3/on_page/instant_pages |
| Full-site technical crawl | /v3/on_page/task_post → summary/pages |
| Lighthouse performance/SEO scores | /v3/on_page/lighthouse/task_post |
| Detect a site's tech stack | /v3/domain_analytics/technologies/domain_technologies/live |
| WHOIS + SEO metrics, filterable | /v3/domain_analytics/whois/overview/live |
If the endpoint you need isn't listed here
These references cover the most important SEO-research endpoints, not the entire API (DataForSEO also has Merchant, App Data, Business Data, Content Analysis/Generation, and more). When you need something not covered:
- Browse the full docs at https://docs.dataforseo.com/v3/ and
WebFetch the relevant endpoint page to get its exact path, parameters, and response fields.
- Then call it with the same
dfs.mjs script — --endpoint <path> + a JSON-array payload. The script is endpoint-agnostic, so nothing extra is needed.
Locations & languages
If you need valid codes, fetch the per-API list endpoint (GET), e.g. /v3/dataforseo_labs/locations_and_languages, /v3/serp/google/locations, /v3/keywords_data/google_ads/languages. Most of the time passing location_name ("United States") and language_name ("English") directly is enough.