Migrating from duck-duck-scrape
August 2, 2026 ยท View on GitHub
ddg-kit@0.1.1 is an unofficial greenfield compatibility replacement for the
Web and News surface of duck-duck-scrape. It is not a continuation of the
original repository and it is not a complete replacement for every legacy
endpoint.
Use this guide to decide whether to migrate. Read the public compatibility matrix for the detailed contract and verification evidence.
Can I migrate?
| Your current use | Status | Action |
|---|---|---|
search | Direct replacement | Rename package; run tests |
searchNews | Direct replacement | Rename package; run tests |
SafeSearchType, SearchTimeType | Direct replacement | Keep enum values |
| ESM/CJS/namespace/default/dynamic | Supported | Keep style; test tarball |
| Arbitrary Needle options | Review required | Map to supported options |
| Message or empty-result handling | Review required | Handle DdgError.code |
| Images or videos | Keep duck-duck-scrape | Migrate Web/News only |
| Autocomplete/Spice/public VQD | Keep duck-duck-scrape | Outside P0 |
| Aggregation/ranking/routing | Not applicable | Higher-level layer |
The P0 interface is frozen. It accepts maintenance fixes and compatibility evidence, but it does not add new public search surfaces.
Package and imports
For a Web or News consumer, the smallest migration is a package-source replacement:
- import { search, SafeSearchType } from "duck-duck-scrape";
+ import { search, SafeSearchType } from "ddg-kit";
Named, namespace, default, dynamic ESM, and CommonJS consumption are covered by packed-tarball fixtures.
If the consumer uses more than Web and News, keep duck-duck-scrape beside
ddg-kit until every required surface has an accepted compatibility design.
No-source migration with an npm alias
If the consumer uses only the P0 Web/News exports, it can keep its existing
duck-duck-scrape import paths and alias the dependency to ddg-kit:
npm install duck-duck-scrape@npm:ddg-kit@0.1.1
This changes the dependency and lockfile. It does not change source imports. It still requires the consumer's existing tests to pass. The alias does not restore Images, Videos, autocomplete, public VQD helpers, or arbitrary Needle request options. Keep the legacy package for those surfaces.
Runtime requirement
ddg-kit requires Node.js 18.17 or newer. Verify the consumer on its supported
Node.js versions before removing the old dependency.
Request options
The third argument is deliberately smaller than Needle's request options. Map only the options that the new interface supports:
| Consumer need | ddg-kit option | Action |
|---|---|---|
| Cancel a request | signal | Pass the caller's AbortSignal |
| Set a total query deadline | timeoutMs | Set the deadline in milliseconds |
| Use a proxy | proxy | Pass an explicit HTTP or HTTPS proxy, or false |
await search("query", searchOptions, {
signal,
timeoutMs: 10_000,
proxy: false,
});
Arbitrary Needle options are not accepted. Do not pass an old request object and expect unsupported fields to be ignored.
Error handling
Do not parse error-message text. Use DdgError.code:
try {
await search("query");
} catch (error) {
if (error instanceof DdgError && error.code === "BOT_CHALLENGE") {
// Respect error.cooldownMs and do not retry immediately.
}
}
Provider failures no longer masquerade as { noResults: true, results: [] }.
A successful empty result is returned only after a representation parsed
successfully and contained no results.
This is a behavior change. Update callers that previously treated every resolved response as a successful search.
Deliberate differences
- The default safe-search value remains
OFFfor compatibility; applications should pass their preferred level explicitly. - Proxy use is explicit and credentials are redacted from public failures.
- One logical query has one total deadline across all representation attempts.
- Bot challenges enter bounded, client-local cooldown and are not retried.
- Web fallback is Web preload -> HTML -> Lite. Lite is attempted only after HTML status 202.
- News failures never fall back to ordinary Web search.
- Image, video, autocomplete, public VQD helpers, and spice endpoints are not included in P0.
Behavior changes that require review
Provider failures are explicit
ddg-kit returns a successful empty result only when the provider response
parsed successfully and contained no results. It throws DdgError for a
challenge, rate limit, timeout, HTTP error, response limit, or parse change.
Use error.code, error.retryable, and error.cooldownMs when present. Do not
parse error-message text and do not retry a bot challenge immediately.
Web fallback is internal
The client owns the Web preload, HTML, and Lite representation policy. Callers cannot select a representation. A caller-provided Web VQD must match the provider-signed preload because HTML and Lite fallback cannot preserve it.
News has its own failure path
News failures do not fall back to ordinary Web search. Keep Web and News error handling separate in the consumer.
Web VQD behavior
A caller-provided Web VQD is a semantic constraint. It must match the token in the provider-signed preload. HTML and Lite fallback cannot preserve that token, so the client rejects those fallback paths. News can use a provided VQD directly.
Rollback
For a local canary, remove ddg-kit and restore the previous dependency. In a
downstream migration PR, one revert must restore the previous dependency,
import source, and lockfile entry.
The migration is complete only when the consumer can build and run its existing Web/News tests with the published package or the reviewed tarball. A local canary is evidence for that consumer, not proof of broad provider stability.