ddg-kit

August 2, 2026 ยท View on GitHub

npm version CI license

A focused, community-maintained compatibility client and maintenance path for the Web and News APIs used by duck-duck-scrape.

ddg-kit provides a maintained migration path for applications that depend on the observed duck-duck-scrape Web and News API. It preserves familiar function names, options, enums, and result shapes while adding typed provider failures, cancellation, one query deadline, and explicit proxy control.

The code is a greenfield rewrite. ddg-kit is not an official duck-duck-scrape release and is not affiliated with DuckDuckGo.

Current release: 0.1.0 supports Web and News only. Images, video, autocomplete, public VQD/bootstrap helpers, and spice endpoints are outside the current compatibility claim.

Relationship to duck-duck-scrape

ddg-kit occupies the community-maintained compatibility role for downstream applications that use the Web and News surface of duck-duck-scrape. We maintain the greenfield client, compatibility fixtures, release artifacts, failure semantics, and migration guidance for that surface.

We do not maintain the duck-duck-scrape repository, own its npm package, or represent its maintainers. ddg-kit is not an official fork, package transfer, or DuckDuckGo integration. Its compatibility target is the observed downstream API, not an upstream promise.

Applications can migrate Web and News to ddg-kit while keeping duck-duck-scrape for endpoints that are not yet supported here. This staged path is intentional: it gives downstream maintainers a maintained compatibility option without requiring an all-at-once dependency replacement.

Installation

npm install ddg-kit

Requirements:

  • Node.js 18.17 or newer
  • ESM or CommonJS

No API key is required. DuckDuckGo does not publish a supported contract for the endpoints used by this package. Provider changes can therefore cause typed failures until the package adapts.

Quick start

import { SafeSearchType, search } from "ddg-kit";

const response = await search("Node.js AbortSignal", {
  maxResults: 5,
  safeSearch: SafeSearchType.MODERATE,
});

for (const result of response.results) {
  console.log(result.title, result.url);
}
import { SearchTimeType, searchNews } from "ddg-kit";

const response = await searchNews("semiconductor earnings", {
  maxResults: 5,
  time: SearchTimeType.WEEK,
});

for (const result of response.results) {
  console.log(result.title, result.url);
}

CommonJS consumers can use the same named API:

const { search, searchNews } = require("ddg-kit");

Why this package exists

Many existing duck-duck-scrape consumers need a maintained migration path. That path must not turn uncertain provider responses into successful empty results. DuckDuckGo can return a challenge page, an HTTP error, or a changed payload. ddg-kit rejects those responses with DdgError.

The package keeps the useful part of the prior Web/News contract:

  • search, searchNews, SafeSearchType, and SearchTimeType
  • familiar Web and News result fields
  • named, namespace, default, dynamic, ESM, and CommonJS imports

This release also adds:

  • structured errors for challenges, limits, timeouts, HTTP failures, response limits, and parse changes
  • one total deadline across fallback attempts
  • AbortSignal cancellation
  • explicit proxy selection and credential-safe failures
  • tests against the packed npm artifact

Public API

ExportPurpose
search(query, options?, request?)Run a Web search
searchNews(query, options?, request?)Run a News search
createDdgClient(options?)Create a configured client
DdgErrorInspect a typed provider or transport failure
SafeSearchTypeSelect strict, moderate, or disabled safe search
SearchTimeTypeSelect day, week, month, year, or all time

Common search options:

OptionWebNewsPurpose
safeSearchYesYesSelect content filtering
timeYesYesRestrict results by age
localeYesYesSelect the request locale
region, marketRegionYesNoSelect Web result regions
offsetYesYesRequest a result offset
vqdYesYesSupply an existing DuckDuckGo query token
maxResultsYesYesBound the returned result count

The third argument accepts signal, timeoutMs, and proxy. It does not accept arbitrary Needle options.

Handle failures by code

import { DdgError, search } from "ddg-kit";

try {
  const response = await search("example query");
  console.log(response.results);
} catch (error) {
  if (!(error instanceof DdgError)) {
    throw error;
  }

  if (error.code === "BOT_CHALLENGE") {
    console.error("DuckDuckGo returned a challenge", error.cooldownMs);
  } else if (error.retryable) {
    console.error("Search can be retried later", error.code);
  } else {
    console.error("Search failed", error.code);
  }
}
CodeMeaning
INVALID_INPUTThe query or option value is invalid.
UNSUPPORTED_OPTIONThe package cannot preserve the requested semantics.
TIMEOUTThe total query deadline expired.
RATE_LIMITEDDuckDuckGo returned a rate limit.
BOT_CHALLENGEDuckDuckGo returned a bot or anomaly challenge.
UPSTREAM_4XXDuckDuckGo returned a client HTTP error.
UPSTREAM_5XXDuckDuckGo returned a server HTTP error.
PARSE_ERRORA provider response no longer matches the parser contract.
RESPONSE_TOO_LARGEA response crossed the configured safety limit.
UNKNOWNThe transport failed without a more specific classification.

Check error.retryable before retrying. Respect error.cooldownMs after a bot challenge.

Request control

Create a client when several calls share the same policy:

import { createDdgClient } from "ddg-kit";

const client = createDdgClient({
  timeoutMs: 10_000,
  proxy: "http://127.0.0.1:8080",
  challengeCooldownMs: 60_000,
});

const controller = new AbortController();

const response = await client.search(
  "example query",
  { maxResults: 10 },
  {
    signal: controller.signal,
    timeoutMs: 5_000,
  },
);

Pass proxy, set DUCKDUCKGO_PROXY_URL, or opt in with USE_PROXY=true and PROXY_URL. The package ignores ambient HTTP_PROXY and HTTPS_PROXY settings.

Compatibility and scope

Many Web and News consumers can migrate by changing the package name:

- import { search, SafeSearchType } from "duck-duck-scrape";
+ import { search, SafeSearchType } from "ddg-kit";

Review the third request argument during migration:

await search("query", searchOptions, {
  signal,
  timeoutMs: 10_000,
  proxy: false,
});
Supported in 0.1.0Planned or outside the current scope
Web and News searchImages, video, and autocomplete
search, searchNews, createDdgClientPublic VQD or bootstrap helpers
Safe search, time, locale, region, offset, max resultsSpice endpoints
ESM and CommonJSMCP server behavior
Timeout, cancellation, cooldown, explicit proxyAggregation and ranking

Web representation fallback is internal. Callers cannot select Web preload, HTML, or Lite parsing paths. Read MIGRATION.md for deliberate differences, VQD behavior, and rollback guidance.

The public compatibility matrix is the source for the current Web/News P0 contract. It records supported exports, deliberate option mappings, structured failure behavior, and legacy surfaces that remain outside the release line.

ddg-kit does not bypass rate limits, bot challenges, CAPTCHAs, or access controls. Applications that need a supported service-level agreement should use an official search API.

Planned compatibility (P1)

Images and video search are planned for P1. They are not part of the 0.1.0 public compatibility claim. These endpoints have different request filters and result schemas from Web and News, so P1 will add separate typed APIs and parsers. It will not alias image or video results to Web results.

P1 must add frozen fixtures for successful, empty, challenged, malformed, timed-out, and paginated responses. It must pass the packed ESM/CommonJS matrix and a downstream canary before consumers remove their remaining duck-duck-scrape dependency. Until then, applications that need Images or video should keep that dependency for those operations. They may migrate Web and News to ddg-kit independently.

Verification

Version 0.1.0 promotes the runtime code accepted in v0.1.0-rc.2. Changes after RC2 cover documentation and release metadata, not runtime behavior.

The project records each type of evidence separately:

  • Package checks: 27 tests, type checking, build, and packed-consumer tests
  • Runtime matrix: packed ESM and CommonJS consumers on Node 18.17, 20, and 22
  • Failure behavior: synthetic fixtures for challenges, limits, timeouts, HTTP errors, parse changes, and response limits
  • Independent review: RC1 was rejected after a clean-checkout failure; RC2 was accepted after the fix
  • Migration evidence: downstream canaries and maintainer-reviewed merged pull requests record focused Web/News adoption and unresolved limits

Read the RC1 rejection report, the RC2 acceptance report, and the compatibility status.

Local canaries do not prove production adoption, downstream maintainer approval, Linux or macOS runtime behavior, or long-running provider stability. The canary index records the current evidence. The adoption index separates local canaries from downstream maintainer acceptance.

Recent downstream evidence includes OpenCandle PR #145 and intercept-mcp PR #6. Both merged focused Web/News migrations. These merges show maintainer review and acceptance of a narrow compatibility change; they do not provide a production SLA or prove broad provider stability.

Maintenance and contributions

You can open a focused pull request for:

  • reproducible Web or News fixes with synthetic fixtures
  • error, timeout, cancellation, proxy, redaction, or package compatibility fixes
  • documentation corrections and focused tests

Open a proposal before changing the public API, result contract, retry policy, runtime dependencies, supported Node versions, or Web/News product boundary.

The project does not accept provider-control bypasses, live response bodies, credentials, private queries, silent semantic changes, or unrelated search aggregation work. Read CONTRIBUTING.md and SECURITY.md before submitting a change. Use the support and compatibility guide to report provider breakage or request a new compatibility surface. GOVERNANCE.md describes the maintenance and adoption signals behind trust claims.

Development

npm ci
npm run check

Run the packed Node matrix when a change affects the public API, runtime compatibility, build, or package contents:

npm run test:matrix

Default tests stay offline. The opt-in live E2E runner uses one Web request and one News request with automatic retries disabled. A provider challenge is recorded as blocked evidence, not as a successful empty result.

Release integrity

Each release binds one source commit to one annotated Git tag, GitHub Release, npm package, and recorded tarball hash. The publish workflow requests npm provenance from GitHub Actions.

See CHANGELOG.md, the release checklist, and the code provenance record.

License

Apache-2.0. See docs/PROVENANCE.md for the greenfield code origin and dependency license review.