TypeScript SDK
typesearch-js — the official client for TypeScript and JavaScript.
npm install typesearch-jsWorks in Node 18+, Bun, Deno, Cloudflare Workers and other edge runtimes. No dependencies. ESM and CommonJS, fully typed.
Create a client
import Typesearch from 'typesearch-js';
const ts = new Typesearch(); // reads TYPESEARCH_API_KEY| Option | Default | |
|---|---|---|
apiKey | TYPESEARCH_API_KEY | Your key. new Typesearch('ts_live_…') works too. |
baseURL | https://api.typesearch.ai | Or TYPESEARCH_BASE_URL. |
timeout | 70000 | Milliseconds before a request is aborted. A deep search can take about a minute. |
maxRetries | 2 | Retries on connection errors, 429 rate_limited and 5xx. |
defaultHeaders | — | Headers sent with every request. |
fetch | global fetch | A custom implementation, for proxies or tests. |
Search
const res = await ts.search('el dólar', {
mode: 'normal',
max_results: 10,
include_domains: ['infobae.com', 'lanacion.com.ar'],
published_after: '2026-09-20',
highlights: true,
});
for (const r of res.results) {
console.log(r.score.toFixed(2), r.title, r.highlights[0]);
}Options and response fields have the same names as the HTTP API and are
typed: SearchOptions, SearchResponse, Result and the rest are exported.
Several queries at once:
const res = await ts.search(['el dólar', 'el FMI'], { mode: 'fast' });
res.groups?.forEach((g) => console.log(g.query, g.total));Stream
for await (const event of ts.searchStream('el dólar', { mode: 'deep' })) {
switch (event.type) {
case 'step':
console.log('·', event.step.text);
break;
case 'partial':
render(event.response.results);
break;
case 'result':
render(event.response.results);
}
}
// Or only the final result
const final = await ts.searchStream('el dólar').finalResponse();An error event is thrown as an APIError from the loop.
Similar and contents
const similar = await ts.similar('https://www.lanacion.com.ar/economia/…', { exclude_domains: ['lanacion.com.ar'] });
const pages = await ts.contents(['https://www.infobae.com/economia/…'], { query: 'el dólar' });Live site search
// Create the job and wait for it
const res = await ts.siteSearchAndWait('lanacion.com.ar', 'el dólar', { mode: 'normal' });
// Or handle the job yourself
const job = await ts.siteSearch('lanacion.com.ar', 'el dólar');
const done = await ts.jobs.wait(job.id, { pollInterval: 2000, waitTimeout: 120_000 });
// Or stream it
for await (const event of ts.siteSearchStream('lanacion.com.ar', 'el dólar')) {
// …
}jobs.wait() throws JobFailedError if the job fails.
Sources and usage
const { sources } = await ts.sources();
const usage = await ts.usage();Errors
Every error extends TypesearchError. API errors are APIError subclasses with status, code,
requestId and, for invalid requests, errors per field.
| Class | When |
|---|---|
BadRequestError | 400 |
AuthenticationError | 401 |
BudgetError | 402 — max_tokens too small |
PermissionDeniedError | 403 |
NotFoundError | 404 |
RateLimitError | 429 — with retryAfter |
InternalServerError | 5xx |
APIConnectionError · APITimeoutError | No response, or too slow |
JobFailedError | A live site search job failed |
import { BadRequestError, RateLimitError, APIError } from 'typesearch-js';
try {
await ts.search('x');
} catch (e) {
if (e instanceof BadRequestError) console.log(e.code, e.errors);
else if (e instanceof RateLimitError) console.log(e.code, e.retryAfter);
else if (e instanceof APIError) console.log(e.status, e.code, e.requestId);
else throw e;
}Retries, timeouts and cancelling
Connection errors, 429 rate_limited and 5xx are retried with exponential backoff and jitter,
honouring Retry-After; quota_exceeded never is. Every method takes a last argument to override the
client’s settings per request:
const controller = new AbortController();
const res = await ts.search('el dólar', { mode: 'deep' }, {
timeout: 90_000,
maxRetries: 0,
signal: controller.signal,
headers: { 'X-Trace-Id': 'abc' },
});Aborting the signal also stops reading a stream.