DuckDuckGo Engines
DuckDuckGo WEB
DDG’s WEB search:
- DuckDuckGo WEB :
https://links.duckduckgo.com/d.js?q=..(HTTP GET) - DuckDuckGo WEB no-AI:
https://noai.duckduckgo.com/(HTTP GET) - DuckDuckGo WEB html :
https://html.duckduckgo.com/html(HTTP POST no-JS / form data) - DuckDuckGo WEB lite :
https://lite.duckduckgo.com/lite(HTTP POST no-JS / form data)
DDG’s content search / see engine duckduckgo_extra.py
- DuckDuckGo Images :
https://duckduckgo.com/i.js??q=...&vqd=... - DuckDuckGo Videos :
https://duckduckgo.com/v.js??q=...&vqd=... - DuckDuckGo News :
https://duckduckgo.com/news.js??q=...&vqd=...
Hint
For WEB searches and to determine the vqd value, DDG-html (no-JS) is
used.
Special features of the no-JS services (DDG-lite & DDG-html):
- The no-JS clients receive a form that contains all the controlling parameters.
- When the form data is submitted, a real WEB browser sets the HTTP Sec-Fetch headers.
HTML <form>, HTTP-Headers & DDG’s bot Blocker:
The HTTP User-Agent (see below) is generated by the WEB-client and are checked by DDG’s bot blocker.
To simulate the behavior of a real browser session, it might be necessary to
evaluate additional headers. For example, in the response from DDG, the
Referrer-Policy is always set to origin. A real browser would then include
the following header in the next request:
Referer: https://html.duckduckgo.com/
The fields of the html-form are reverse-engineered from DDG-html and may be subject to additional bot detection mechanisms and breaking changes in the future.
Query field:
Intro page: <https://html.duckduckgo.com/html/>
q(str): Search query stringb(str): Beginning parameter - empty string for first page requests. If a second page is requested, this field is not set!
Search options:
kl(str): Keyboard language/region code (e.g. ‘en-us’ default: ‘wt-wt’)df(str): Time filter, maps to values like ‘d’ (day), ‘w’ (week), ‘m’ (month), ‘y’ (year)
The key/value pairs df and kl are additional saved in the cookies,
example:
Cookie: kl=en-us; df=m
next page form fields:
nextParams(str): Continuation parameters from previous page response, typically empty string. Opposite ofb; this field is not set when requesting the first result page.api(str): API endpoint identifier, typically ‘d.js’o(str): Output format, typicallyjsonv(str): Typicallylfor subsequent pagesdc(int): Display count - value equal to offset (s) + 1s(int): Search offset for paginationvqd(str): Validation query digest
General assumptions regarding DDG’s bot blocker:
- Except
Cookie: kl=..; df=..DDG does not use cookies in any of its services. - DDG does not accept queries with more than 499 chars
- The
vqdvalue (“Validation query digest”) is needed to pass DDG’s bot protection and is used by all request to DDG. - The
vqdvalue is generally not needed for the first query (intro); it is only required when additional pages are accessed (or when new content needs to be loaded for the query while scrolling). - The second page (additional content) for a query cannot be requested without
vqd, as this would lead to an immediate blocking, since such a use-case does not exist in the process flows provided by DDG (and is a clear indication of a bot).
The following HTTP headers are being evaluated (and may possibly be responsible for issues):
User-Agent: : The HTTP User-Agent is also involved in the formation of the vqd value, read . However, it is not checked whether the UA is a known header. However, it is possible that certain UA headers (such as curl) are filtered.
Sec-Fetch-Mode: : In the past, Sec-Fetch-Mode had to be set to ‘navigate’, otherwise there were problems with the bot blocker.. I don’t know if DDG still evaluates this header today
Accept-Language:
: DDG-Lite and DDG-HTML TRY to guess user’s preferred language from the HTTP
Accept-Language. Optional the user can select a region filter (but not a
language).
In DDG’s bot blocker, the IP will be blocked (DDG does not have a client session!)
- As far as is known, it is possible to remove a un-blocked an IP by executing a DDG query in a real web browser over the blocked IP (at least that’s my assumption).
How exactly the blocking mechanism currently works is not fully known, and there were also changes to the bot blocker in the period of Q3/Q4 2025: in the past, the IP blocking was implemented as a ‘sliding window’ (unblock after about 1 hour without requests from this IP)
Terms / phrases that you keep coming across:
d.js,i.js,v.js,news.jsare the endpoints of the DDG’s web API through which additional content for a query can be requested (vqd required)
The *.js endpoints return a JSON response and can therefore only be
executed on a JS-capable client.
The service at <https://lite.duckduckgo.com/lite> offers general WEB searches (no news, videos etc). DDG-lite and DDG-html can be used by clients that do not support JS, aka no-JS.
DDG-lite works a bit differently: here, d.js is not an endpoint but a
field (api=d.js) in a form that is sent to DDG-lite.
- The request argument origin=funnel_home_website is often seen in the DDG
services when the category is changed (e.g., from web search to news, images,
or to the video category)
searx.engines.duckduckgo.safesearch: bool = True : DDG-lite: user can’t select but the results are filtered.
searx.engines.duckduckgo.ddg_url: str = 'https://html.duckduckgo.com/html/'
: The process flow for determining the vqd values was implemented for the
no-JS variant (DDG-html)
searx.engines.duckduckgo.get_vqd(query: str, params: OnlineParams) → str[source]
: Returns the vqd value that fits to the query (and HTTP User-Agent
header).
Parameters:
: - **query** – the query term
- **params** – request parameters
searx.engines.duckduckgo.get_ddg_lang(eng_traits: EngineTraits, sxng_locale: str, default: str = 'en_US') → str | None[source] : Get DuckDuckGo’s language identifier from SearXNG’s locale.
#### Hint
[DDG-lite](https://lite.duckduckgo.com/lite) and the *no Javascript*
page <https://html.duckduckgo.com/html> do not offer a language selection
to the user.
DDG defines its languages by a region code ([`fetch_traits`](#searx.engines.duckduckgo.fetch_traits)). To
get region and language of a DDG service use:
It might confuse, but the `l` value of the cookie is what SearXNG calls
the *region*:
```
# !ddi paris :es-AR --> {'ad': 'es_AR', 'ah': 'ar-es', 'l': 'ar-es'}
params['cookies']['ad'] = eng_lang
params['cookies']['ah'] = eng_region
params['cookies']['l'] = eng_region
```
searx.engines.duckduckgo.quote_ddg_bangs(query: str) → str[source] : To avoid a redirect, the !bang directives in the query string are quoted.
searx.engines.duckduckgo.is_ddg_captcha(dom: ElementBase | _Element)[source] : In case of CAPTCHA ddg response its own not a Robot dialog and is not redirected to a CAPTCHA page.
searx.engines.duckduckgo.fetch_traits(engine_traits: EngineTraits)[source] : Fetch languages & regions from DuckDuckGo.
SearXNG’s `all` locale maps DuckDuckGo’s “All regions” (`wt-wt`).
DuckDuckGo’s language “Browsers preferred language” (`wt_WT`) makes no
sense in a SearXNG request since SearXNG’s `all` will not add a
`Accept-Language` HTTP header. The value in `engine_traits.all_locale`
is `wt-wt` (the region).
Beside regions DuckDuckGo also defines its languages by region codes. By
example these are the english languages in DuckDuckGo:
- en\_US
- en\_AU
- en\_CA
- en\_GB
The function [`get_ddg_lang`](#searx.engines.duckduckgo.get_ddg_lang) evaluates DuckDuckGo’s language from
SearXNG’s locale.
DuckDuckGo Extra (images, videos, news)
searx.engines.duckduckgo_extra.ddg_category = ''
: The category must be any of images, videos and news
DuckDuckGo Instant Answer API
The DDG-API is no longer documented but from reverse engineering we can see that some services (e.g. instant answers) still in use from the DDG search engine.
As far we can say the instant answers API does not support languages, or at least we could not find out how language support should work. It seems that most of the features are based on English terms.
searx.engines.duckduckgo_definitions.is_broken_text(text: str) → bool[source]
: duckduckgo may return something like <a href=“xxxx”>http://somewhere Related website<a/>
The href URL is broken, the “Related website” may contains some HTML.
The best solution seems to ignore these results.
searx.engines.duckduckgo_definitions.area_to_str(area: dict[str, str]) → str[source]
: parse {“unit”: “https://www.wikidata.org/entity/Q712226”, “amount”: “+20.99”}
DuckDuckGo Weather
Copyright © SearXNG team
Made with Furo
