# Apify Duckduckgo MCP Serp Research

> Check where a site ranks on DuckDuckGo and research a live SERP from an AI agent, using the Apify DuckDuckGo Scraper Actor (johnvc/DuckDuckGo-Scraper-for-serp-rankings) as a hosted duckduckgo mcp tool. Run a query, read the ranked organic_results rows (position, title, link, snippet, favicon), find a domain's position, list the competitors above it, and repeat the query across regions or date windows to see how a privacy-first engine ranks differently from Google. Use when the user asks about duckduckgo mcp, wants to check a DuckDuckGo ranking, see where a site sits on DuckDuckGo, compare competitor visibility on DuckDuckGo, watch a position over time, or ground an agent's answer in live DuckDuckGo results instead of training data. Pay-per-page billing, MCP-ready for Claude and other AI agents.

- Skill: `johnisanerd/apify-duckduckgo-mcp-serp-research` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add johnisanerd/apify-duckduckgo-mcp-serp-research`
- Raw SKILL.md: https://api.skillmd.com/api/skills/johnisanerd/apify-duckduckgo-mcp-serp-research/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: MIT
- Author: johnisanerd (https://skillmd.com/u/johnisanerd)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/johnisanerd/apify-duckduckgo-mcp-serp-research

---


# DuckDuckGo MCP: Rank Checking and SERP Research

Point an agent at a live DuckDuckGo SERP and answer the questions people actually ask about it. Where does my domain rank for this term? Who sits above me? Does that change in the UK, or in Germany, or if I only count results from the past month?

DuckDuckGo pulls from a different index than Google, so a site that ranks well on one often does not on the other. This skill turns that gap into something you can measure.

## When to use this skill

- The user asks about "DuckDuckGo MCP" or wants a DuckDuckGo tool their agent can call.
- They want to check where a domain ranks on DuckDuckGo for a keyword.
- They want to see which competitors outrank them on DuckDuckGo, and with what page titles.
- They want to compare the same query across regions, or before and after a content change.
- They want an agent to ground an answer in live search results rather than in what it remembers.

Not for: bulk SERP exports and pipeline work (use the companion DuckDuckGo scraper skill), or Google, Yandex, and Baidu rankings (see `references/actor-index.md`).

## Prerequisites

- Apify account (sign up at https://apify.com?fpr=9n7kx3&fp_sid=skillrepo).
- Authentication via `apify login`, or an `APIFY_TOKEN` environment variable (Apify Console, Settings, Integrations).

## The Actor

- Store page: https://apify.com/johnvc/DuckDuckGo-Scraper-for-serp-rankings?fpr=9n7kx3&fp_sid=skillrepo
- Actor ID: `johnvc/DuckDuckGo-Scraper-for-serp-rankings`
- Pricing: a tiny per-run setup fee plus a per-page fee (see `references/gotchas.md`).

## Connect it as an MCP tool

The Actor is MCP-ready. Add the hosted server URL to Claude, Cursor, or any MCP client:

`https://mcp.apify.com/?tools=actors,docs,johnvc/DuckDuckGo-Scraper-for-serp-rankings`

MCP setup docs: https://docs.apify.com/platform/integrations/mcp

Once connected, prompts like these work directly:

- "Where does example.com rank on DuckDuckGo for 'privacy focused search engine'?"
- "Show me the top ten DuckDuckGo results for 'open source CRM' in the UK and flag anyone I compete with."
- "Run that same query restricted to the past month and tell me what changed."

## Workflow

1. Fix the query and the market. One keyword per run. Set `localization` to the region and language you care about, `us-en` by default, `uk-en`, `de-de`, `fr-fr` and 38 more available.
2. Decide the depth. `max_pages` 1 is enough for a top-of-page check. Use 2 for a top-50-ish read. Deeper only if the domain is genuinely buried.
3. Run the Actor and take the single page item back.
4. Find the position. Walk `organic_results` and match the target domain against each row's `link`. The row's `position` is the rank on that page. If you paginated, keep `page_number` so you can report the page as well as the position.
5. Read the neighbourhood. The rows above the target, with their `title` and `snippet`, are the pages you are actually losing to. Their `title` text is usually the fastest signal for what the engine thinks the query means.
6. Repeat to compare. Same query, different `localization`, gives a regional read. Same query with `date_filter` set to `m` or an explicit range gives a freshness read. Same query re-run later gives movement.

## Rank check with the Apify CLI

Top page of results for a keyword, US English:

```bash
apify actors call "johnvc/DuckDuckGo-Scraper-for-serp-rankings" -i '{"query":"privacy focused search engine","max_pages":1}' \
  --json \
  --user-agent apify-awesome-skills/apify-duckduckgo-mcp-serp-research \
  2>/dev/null
```

Same keyword in the UK, two pages deep, for a wider rank check:

```bash
apify actors call "johnvc/DuckDuckGo-Scraper-for-serp-rankings" -i '{"query":"privacy focused search engine","localization":"uk-en","max_pages":2}' \
  --json \
  --user-agent apify-awesome-skills/apify-duckduckgo-mcp-serp-research \
  2>/dev/null
```

Fresh results only, past month, to see what recently published pages are winning:

```bash
apify actors call "johnvc/DuckDuckGo-Scraper-for-serp-rankings" -i '{"query":"best password manager","date_filter":"m","max_pages":1}' \
  --json \
  --user-agent apify-awesome-skills/apify-duckduckgo-mcp-serp-research \
  2>/dev/null
```

Every call carries the three flags this repo expects: `--json`, `--user-agent apify-awesome-skills/apify-duckduckgo-mcp-serp-research`, and `2>/dev/null`.

## What comes back (one item per page)

Confirmed on a live run. Each dataset item is a whole page, and the ranked rows live in a nested array on it.

- `organic_results` array. Every row has `position`, `title`, `link`, `snippet`, `favicon`. Rows carry `date` and `date_raw` when DuckDuckGo shows a published date (9 of 11 rows on the test query).
- Run echo: `query`, `localization`, `safe_search`, `max_pages`, `search_timestamp`.
- Counters: `total_results_found`, `pages_processed`, `page_number`.
- `search_metadata`: `localization`, `localization_name`, `safe_search`, `safe_search_description`, `max_pages`, `pagination_limit_reached`.
- `pagination_info`: `total_pages`, `max_pages_set`, `pagination_stopped_by_limit`, and `results_per_page` with `first_page` and `subsequent_pages`.
- `ads`, `knowledge_graph`, `news_results`, `inline_images`, `inline_videos`, `related_searches` come back as arrays on every item and fill in only when DuckDuckGo renders those blocks. All six were empty for the informational test query.

`search_timestamp` is what makes a series comparable. Store it with every reading.

## Reading a rank honestly

- A position is a snapshot for one query, one region, one moment. Two readings do not make a trend.
- Page size is not uniform. The live run reported 11 results on the first page and 50 on subsequent pages, so never compute a global rank by multiplying `page_number` by a fixed page size. Use `results_per_page` or just concatenate the arrays in order.
- If the domain does not appear at all, say "not found in the first N results", not "not ranking". Absence within `max_pages` is not absence from the index.
- DuckDuckGo rankings are not Google rankings. Do not present one as a proxy for the other.
- A missing `date` on a row means DuckDuckGo did not show one, not that the page is old.

## Cost

Pay per event: a fractions-of-a-cent setup fee per run plus a per-page fee. A one-page rank check costs about a penny, so daily tracking of a small keyword set is cheap. The arithmetic and confirmation thresholds are in `references/gotchas.md`.

## Troubleshooting

- Domain not in `organic_results`: raise `max_pages` to 2, then stop. Deeper pages rarely change a decision.
- Positions shifted with no content change: normal SERP movement. Re-read before reporting a drop.
- Wrong-country results: `localization` is a combined region and language code, so setting `de-de` also switches the language to German.
- Rejected `date_filter`: use `d`, `w`, `m`, `y`, or exactly `YYYY-MM-DD..YYYY-MM-DD`.

See `references/gotchas.md` for cost guardrails and error recovery, and `references/actor-index.md` for the Actor routing table.

## Related search Actors

- DuckDuckGo scraper (bulk SERP export, the companion skill for this Actor)
- Yandex Search Scraper: https://apify.com/johnvc/Scrape-Yandex?fpr=9n7kx3&fp_sid=skillrepo
- Baidu Search Scraper: https://apify.com/johnvc/Baidu-Search-Scraper?fpr=9n7kx3&fp_sid=skillrepo
- Naver Search API (Korea): https://apify.com/johnvc/naver-search-api?fpr=9n7kx3&fp_sid=skillrepo

