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 useStatusAction
searchDirect replacementRename package; run tests
searchNewsDirect replacementRename package; run tests
SafeSearchType, SearchTimeTypeDirect replacementKeep enum values
ESM/CJS/namespace/default/dynamicSupportedKeep style; test tarball
Arbitrary Needle optionsReview requiredMap to supported options
Message or empty-result handlingReview requiredHandle DdgError.code
Images or videosKeep duck-duck-scrapeMigrate Web/News only
Autocomplete/Spice/public VQDKeep duck-duck-scrapeOutside P0
Aggregation/ranking/routingNot applicableHigher-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 needddg-kit optionAction
Cancel a requestsignalPass the caller's AbortSignal
Set a total query deadlinetimeoutMsSet the deadline in milliseconds
Use a proxyproxyPass 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 OFF for 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.