DuckDuckGo Search API — Getting Started — FetchLayer Docs

Getting Started

Search DuckDuckGo, check where your site ranks for the queries you care about, then widen the research with suggestions and news.

1. Get your API key

Create an account and generate a FetchLayer API key from your dashboard. There is no DuckDuckGo account or search API agreement involved.

Create free account →

2. Search

Every organic result comes back with its position, title, URL, snippet and date. Ads come back separately and never take a position.

bash
curl -X POST https://api.fetchlayer.dev/duckduckgo/search \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"query":"open source crm","limit":10}'

Going deeper

A limit above 10 reads more result pages — up to 200 results — and each page returned counts as one request (pagesScraped). To continue later, send nextCursor back as cursor with the same query and filters. DuckDuckGo lists at most about 190–200 results for a query; endOfResults says when it has stopped.

3. Check where your site ranks

For SEO rank tracking, don’t page through search yourself. /rank-check scans up to 200 positions for a domain or a URL prefix and returns whether it was found, its best position, and every match with the page it was on.

bash
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}'

One credit at any depth

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 checked and is never billed. A 200-position check typically takes around 30 seconds.

Rankings depend on the searcher

Results reflect a US searcher (region: "us-en") unless you set a region, and the same query ranks differently in uk-en or de-de. Positions also move over time — github.com for “open source crm” has shown up on the first page and on the third within days. Track a query in the region your audience searches from.

By default the scan stops after the first result page that contains a match. Set allMatches to scan the full depth and see every page of yours that ranks. found: false always means the whole depth was examined.

4. Expand the research

/suggestions returns what DuckDuckGo’s search box offers for a partial query — the queries worth tracking next. /news and /videos search those verticals, and /instant-answer returns the topic summary, facts and related topics DuckDuckGo shows.

bash
curl -X POST https://api.fetchlayer.dev/duckduckgo/suggestions \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"query":"fetchla"}'

Tip

Already have a DuckDuckGo link? Send it to /resolve-url — it returns the query, region, safe search and time range the URL carries, or where a result link leads, without contacting DuckDuckGo, and it does not consume a credit.

Errors and retries

A 400 names the field to fix. Unrecognised fields are rejected rather than ignored, so a typo such as regoin fails loudly instead of quietly returning US results — and /suggestions takes a region but no safe search or time range. A 503 means DuckDuckGo turned the request away repeatedly within the time allowed; it is always worth retrying, and a rank check never reports “not found” in its place. A search that matches nothing is a 200 with an empty list. Read notes on every successful response too.

Error responses

Every error, on every route, is JSON in one shape: error (what went wrong, in words), status, code, retryable and requestId, which also comes as the X-Request-Id header. Branch on code and retryable rather than on the text; on a 5xx the text is generic. Quote requestId when reporting a problem.

json
{
  "error": "Missing required parameter: url",
  "status": 400,
  "missing": ["url"],
  "code": "invalid_input",
  "retryable": false,
  "requestId": "922874e3-c0d0-40d8-8866-d198af132420"
}
codeStatusRetryableMeaning
invalid_input400noThe request needs fixing; error names the field. A missing required parameter also lists it in missing.
unauthorized401noThe API key is missing or not valid.
insufficient_credits402noNothing left to spend: no credits, a used-up free trial, or a monthly allowance and credit balance both spent. error says which. Waiting does not help; topping up does.
forbidden403noThis key or account is not allowed this request.
not_found404noWhat the request names does not exist or is not public.
expired410noWhat the request refers to is no longer available; ask for it afresh.
quota_exceeded429noA request-rate limit was reached; error says which. Slow down before sending more.
cancelled499noThe caller closed or cancelled the request before it finished. The work was stopped and nothing was charged.
time_limit503yesYour timeoutMs ran out before there was anything to return; ask again, or allow more time.
temporarily_unavailable503yesNot available right now; ask again shortly.
posts_unavailable403noTikTok user videos: TikTok is not showing this account’s posts publicly right now. Asking again at once returns the same answer; nothing is charged.
store_unreachable503yesStore routes (Shopify, WooCommerce): the store itself can’t be reached right now — it may be offline. Ask again later. Where a store route doesn’t say so yet, an offline store is temporarily_unavailable.
upstream_error502yesThe source answered in a way that could not be used; ask again later.
internal_error500yesSomething failed on our side; ask again later, and quote requestId if it persists.
tool_error—noMCP only: a tool call failed in a way no other code describes; error says what.

More codes may be added; treat one you do not recognise by its retryable flag. Over MCP, a tool error carries the same fields in structuredContent, and as a JSON text block beside the message.

Next steps

Product overviewDuckDuckGo Search API: what it returns, pricing and use cases