Official JavaScript and TypeScript SDK for the Desearch public API.
This repo publishes the npm package desearch-js. In the current source tree, the package version is 1.5.1.
Related docs:
- docs/features.md
- docs/architecture.md
- docs/known-issues.md
- docs/decisions/0001-thin-node-sdk-transport.md
desearch.js is a thin, Node-oriented SDK for the Desearch API. It wraps the public read-side endpoints implemented in src/index.ts behind one Desearch client class and ships a typed surface from src/types.ts.
What it covers today:
- AI multi-source search
- AI web-links search
- AI X-links search
- X search and lookup endpoints
- web search
- page extraction
What it does not try to be:
- a browser-first SDK
- a configurable transport layer
- a write or mutation client
- a framework with retries, pagination helpers, or runtime validation
npm install desearch-jsRequires Node.js ≥ 20.18.1
Desearch is both the default export and a named export.
import Desearch from 'desearch-js';
// or: import { Desearch } from 'desearch-js';
const client = new Desearch(process.env.DESEARCH_API_KEY!);
const result = await client.aiSearch({
prompt: 'latest Bittensor developments',
tools: ['web'],
start_date: '2025-05-01T00:00:00Z',
end_date: '2025-05-08T00:00:00Z',
include_domains: ['bbc.com', 'reuters.com'],
result_type: 'LINKS_WITH_FINAL_SUMMARY',
count: 10,
});
console.log(result);const { Desearch } = require('desearch-js');
// or: const Desearch = require('desearch-js').default;
const client = new Desearch(process.env.DESEARCH_API_KEY);The constructor accepts a single argument, your API key:
const client = new Desearch('your-api-key');The SDK sends that value as the Authorization header on every request.
aiSearch(payload)→POST /desearch/ai/searchaiWebLinksSearch(payload)→POST /desearch/ai/search/links/webaiXLinksSearch(payload)→POST /desearch/ai/search/links/twitter
xSearch(params)→GET /twitterxPostsByUrls(params)→GET /twitter/urlsxPostById(params)→GET /twitter/postxPostsByUser(params)→GET /twitter/post/userxPostRetweeters(params)→GET /twitter/post/retweetersxUserPosts(params)→GET /twitter/user/postsxUserReplies(params)→GET /twitter/repliesxPostReplies(params)→GET /twitter/replies/postxTrends(params)→GET /twitter/trends
webSearch(params)→GET /webextract(params)→GET /web/extractwebCrawl(params)→GET /web/crawl(deprecated compatibility method)
const result = await client.aiSearch({
prompt: 'recent AI chip announcements',
tools: ['web'],
exclude_domains: ['pinterest.com'],
result_type: 'LINKS_WITH_FINAL_SUMMARY',
count: 20,
});Notes:
toolsaccepts the source names defined insrc/types.ts:web,hackernews,reddit,wikipedia,youtube,twitter,arxivaiSearchalways sendsstreaming: false, even if a caller tries to add astreamingfield manually
const links = await client.aiWebLinksSearch({
prompt: 'open source browser automation tools',
tools: ['web', 'hackernews', 'reddit', 'youtube'],
count: 20,
});const xLinks = await client.aiXLinksSearch({
prompt: 'Bittensor subnet updates',
count: 20,
});const tweets = await client.xSearch({
query: 'bittensor',
sort: 'Top',
lang: 'en',
min_likes: 50,
count: 20,
});const tweets = await client.xPostsByUrls({
urls: ['https://x.com/DesearchAI/status/1234567890123456789'],
});const results = await client.webSearch({
query: 'site:desearch.ai sdk docs',
start: 0,
});const page = await client.extract({
url: 'https://desearch.ai',
format: 'text',
});Existing webCrawl() integrations continue to use the deprecated GET /web/crawl compatibility route. Use extract() for new integrations.
SDK methods return the same raw payload shape by default:
const results = await client.webSearch({ query: 'desearch sdk' });
console.log(results.data);If you want per-request cost visibility, pass { includeMetadata: true } as the second argument. The SDK reads metadata from the same API response headers; it does not make an extra billing or pricing request.
const response = await client.webSearch(
{ query: 'desearch sdk' },
{ includeMetadata: true },
);
console.log(response.data);
console.log(response.metadata.costUsd);
console.log(response.metadata.usageCount);
console.log(response.metadata.service);
console.log(response.metadata.currency);The metadata wrapper works for JSON object, JSON array, and text endpoints such as extract() and webCrawl(). Missing or malformed metadata headers are ignored, so successful API calls still resolve normally.
- TypeScript
^5.9.3 undici^6.28.1 || ^7.29.1for HTTP transportdotenv^17.3.1as a runtime dependency inpackage.jsontsup^8.5.1for bundlingvitest^4.0.18for teststypedoc^0.28.17typedoc-plugin-markdown^4.10.0- output targets: CommonJS, ESM, and bundled declaration files
| Command | Purpose |
|---|---|
npm run build |
Build dist/ with tsup using the config in tsup.config.ts |
npm run build-fast |
Build src/index.ts directly into CJS and ESM without the full default tsup command |
npm test |
Run the Vitest test suite |
npm run generate-docs |
Generate markdown API docs from src/index.ts using TypeDoc |
npm run build:beta |
Build for beta publishing |
npm run version:beta |
Bump a beta prerelease version |
npm run version:stable |
Bump a stable patch version |
npm run publish:beta |
Version, build, and publish with the beta npm tag |
npm run publish:stable |
Version, build, and publish as the default release |
npm run prepublishOnly |
Automatic npm hook that runs npm run build before publish |
Source layout:
src/index.ts, theDesearchclient plus the sharedhandleRequest<T>()transport helpersrc/types.ts, request/response contracts and X entity modelstsup.config.ts, build output configurationpackage.json, scripts, exports, dependency versions, and package metadata
Key design decisions in the current source:
- one shared request helper powers every public method
- GET payloads are serialized through
URLSearchParams - POST payloads are serialized as JSON
- response parsing switches between JSON and text based on
content-type - default calls return the parsed payload directly, preserving the existing SDK response shape
- callers can opt into
{ data, metadata }wrappers with{ includeMetadata: true } extract()and the legacywebCrawl()method return text while most other methods return JSON-shaped data- the base URL is fixed to
https://api.desearch.ai aiSearch()forcibly disables streaming
See docs/architecture.md for the detailed flow and ADR 0001 for the reasoning behind the current transport design.
HTTP failures are thrown as plain Error instances in this format:
HTTP <status>: <response body>Non-HTTP failures are wrapped as:
Unexpected Error: <message>Example:
try {
await client.webSearch({ query: 'desearch' });
} catch (error) {
console.error(error);
}package.json exports:
./dist/index.jsfor CommonJS./dist/index.mjsfor ESM./dist/index.d.tsfor TypeScript declarations
Current limitations include:
- no configurable base URL
- no timeout, retry, or abort controls in the public API
aiSearchalways disables streaming- runtime failures surface as plain
Errorstrings rather than typed SDK errors
Full details: docs/known-issues.md