Check where a site ranks for a query
Find where a domain or URL ranks in DuckDuckGo’s organic results for a query — built for SEO rank tracking. Send a query and a target and get back whether it was found, its best (lowest) position, and every match with its position, URL, title and result page, scanning up to depth positions (1–200, default 100). A domain target (example.com, or https://www.example.com/) matches that site and, by default, its subdomains; matching is on whole hostname labels, so notexample.com and example.com.evil.net never match, and www. is ignored on both sides. A URL with a path (example.com/blog) matches pages on that host under that path: /blog, /blog/ and /blog/post, but not /blogging. Positions are counted exactly as search counts them — organic results only, ads never take a position, a repeated URL once. The scan stops after the first result page containing a match, so several matches on that page are all reported; set allMatches to scan the full depth. found: false always means the whole depth was examined, or every result DuckDuckGo lists when that is fewer (a note says so); if DuckDuckGo cannot be read part-way through, the response is a retryable 503, never a "not found". Rankings differ by the searcher’s country: results reflect a US searcher (us-en) unless you set region, and positions shift over time. DuckDuckGo lists at most about 190–200 results for a query, so a deeper check ends with endOfResults true. A deep check can take 30 seconds or more. Billing: A rank check costs 1 credit regardless of depth: scanning 200 positions across 10 or more result pages is billed as one request. serpPagesFetched reports how many result pages were read and is never billed.
/rank-check Web search https://api.fetchlayer.dev/duckduckgo/rank-check Headers
Authorization string required Bearer token — your FetchLayer API key.
Parameters
query string required The search query, as a searcher would type it (1–500 characters).
target string required What to look for: a domain such as example.com (matches the site and its subdomains), or a URL with a path such as example.com/blog (matches pages under that path).
depth number optional Organic positions to scan, e.g. 10, 20, 50 or 100 (1–200), or -1 for the maximum. One request at any depth. (default: 100)
allMatches boolean optional Scan the full depth and report every matching position, instead of stopping after the first result page with a match. (default: false)
includeSubdomains boolean optional For a domain target, whether subdomains such as blog.example.com count as the site. (default: true)
region string optional Region code: us-en, uk-en, de-de, fr-fr, es-es, it-it, jp-jp, in-en, au-en, ca-en, br-pt and 52 more (64 codes, DuckDuckGo’s own list), or wt-wt for no region. Results and rankings differ by region. (default: us-en)
safeSearch string optional DuckDuckGo’s own filter level: strict, moderate or off. DuckDuckGo still returns a site for a query that names it outright. (default: moderate)
timeRange string optional any, day, week, month or year: only results published within it. (default: any)
format string optional json or markdown. (default: json)
timeoutMs number optional Time limit for each page read, in milliseconds (1000–120000).
Response Fields
rank-check
Check where a site ranks for a query
resultType string search, rank-check, instant-answer, suggestions, news, videos or images.
requestedUrl string The DuckDuckGo page this result corresponds to.
notes string[] Caveats about this particular result — a list that may be incomplete, a depth DuckDuckGo could not reach, a later page that could not be read. Empty when there are none. Read it on every call.
transport string The source this data came from. Always "duckduckgo".
scrapedAt string ISO 8601 timestamp of when the data was collected.
query string The query that was run.
region string The region the results are for.
safeSearch string The safe search level applied.
timeRange string The time range applied.
target object The target as understood: input, type (domain or url_prefix), value (the hostname, or host and path, that was matched) and includeSubdomains.
found boolean Whether the target appeared within the scanned depth.
position number Its best (lowest) organic position, or null.
matches object[] Every match found: position, url, title and page.
depth number The depth you asked for.
depthScanned number Organic positions actually examined.
serpPagesFetched number Result pages read. Informational only — never billed.
endOfResults boolean DuckDuckGo ran out of results before the requested depth.
pagesScraped number Always 1: a rank check counts as one request against your plan, however deep it scans.
Request
curl -X POST "https://api.fetchlayer.dev/duckduckgo/rank-check" \
-H "Authorization: Bearer ss-your-key" \
-H "Content-Type: application/json" \
-d '{"query":"reddit api","target":"fetchlayer.dev","depth":200}'Response
{
"resultType": "rank-check",
"requestedUrl": "https://duckduckgo.com/?q=reddit+api&kl=us-en&kp=-1",
"scrapedAt": "2026-09-15T19:53:58.766Z",
"transport": "duckduckgo",
"notes": [
"Stopped after the first page containing the target; set allMatches to scan the full depth."
],
"query": "reddit api",
"region": "us-en",
"safeSearch": "moderate",
"timeRange": "any",
"target": {
"input": "fetchlayer.dev",
"type": "domain",
"value": "fetchlayer.dev",
"includeSubdomains": true
},
"found": true,
"position": 129,
"matches": [
{
"position": 129,
"url": "https://fetchlayer.dev/blog/reddit-api-closed-2026",
"title": "Reddit API Shut Down in 2026: What Still Works | FetchLayer",
"page": 10
},
{
"position": 131,
"url": "https://fetchlayer.dev/reddit-scraper",
"title": "Reddit Scraper API & MCP for AI Agents | FetchLayer",
"page": 10
}
],
"depth": 200,
"depthScanned": 139,
"serpPagesFetched": 10,
"endOfResults": false,
"pagesScraped": 1
}