Rekalldocs
SDKs

TypeScript SDK

rekall-search — a thin, typed, zero-dependency client. Pure fetch; works in Node 18+, Bun, Deno, and edge runtimes.

rekall-search is a thin, typed, zero-dependency client over the REST API. It's pure fetch, so it runs in Node 18+, Bun, Deno, and edge runtimes (Vercel Edge, Cloudflare Workers). Latency and incremental updates are the ergonomic priorities.

Install

npm i rekall-search
bun add rekall-search
pnpm add rekall-search
yarn add rekall-search

Client

import { Rekall } from "rekall-search";

// Reads REKALL_API_KEY and REKALL_BASE_URL from the environment.
const rekall = new Rekall();

// Or configure explicitly (e.g. pointing at a self-hosted server):
const rekall2 = new Rekall({
  apiKey: process.env.REKALL_API_KEY,
  baseUrl: "http://localhost:9009/v1",
});

Prop

Type

Stores

rekall.store(name) is get-or-create — no "create vs get" ceremony. The explicit rekall.stores.* methods exist for full control.

const store = await rekall.store("contracts");        // get-or-create by name

// Explicit control:
const s  = await rekall.stores.create("contracts");
const g  = await rekall.stores.get("contracts");      // by name or id
const ls = await rekall.stores.list({ limit: 20 });   // { stores, nextCursor }
await rekall.stores.delete("contracts");

Ingest

Pass whole documents — no chunking. They're queryable when the promise resolves.

const { ingested, docIds } = await store.ingest([
  { id: "supplier_a", text: supplierText, metadata: { team: "procurement" } },
  { id: "policy_2026", text: policyText },
]);

Streaming ingest (big corpora)

store.ingestStream batches and backpressures automatically — O(N), memory-flat.

async function* documents() {
  for await (const file of walk("./corpus")) {
    yield { id: file.path, text: await file.read(), metadata: { path: file.path } };
  }
}

await store.ingestStream(documents(), {
  concurrency: 4,
  onProgress: ({ ingested, total }) => {
    process.stdout.write(`\ringested ${ingested}${total ? `/${total}` : ""}`);
  },
});

Files helper (Node)

Extraction parity with the CLI — walks globs, extracts PDF/MD/HTML/DOCX/TXT:

await store.ingestFiles("./docs/**/*.{pdf,md,txt}");
const result = await store.search(
  "which supplier contract conflicts with the new policy?",
  { maxHops: 4, maxResults: 5, trace: true, filter: { team: "procurement" } },
);

result.passages.forEach((p) => console.log(p.docId, p.score, p.hop));
console.log(result.confidence, result.hopsUsed, result.latencyMs);
console.log(result.trace); // [{ hop, docId, why }]

Prop

Type

Typed results & metadata generics

SearchResult, Passage, TraceStep, and Store are exported types. search is generic over your metadata shape:

import type { SearchResult, Passage, TraceStep } from "rekall-search";

const r = await store.search<{ team: string }>("…");
r.passages[0].metadata.team; // typed as string

Documents

const { documents, nextCursor } = await store.documents.list({ limit: 100 });
const doc = await store.documents.get("supplier_a", { includeText: true });
await store.documents.delete("supplier_a");

Health

const health = await rekall.health(); // { status, version, engine }

Confidence-gated fallback

The idiomatic composability pattern — take the 4 ms path most of the time, escalate only when the sufficiency head is unsure:

const r = await store.search(q);
const context = r.confidence >= 0.6
  ? r.passages                       // 4 ms path — most queries end here
  : await expensiveAgenticSearch(q); // rare fallback

See the full LLM fallback recipe.

Error classes

Every error is a typed subclass of RekallError, carrying the server's type and the prescriptive fix string.

Prop

Type

import { RekallError, StoreNotFoundError } from "rekall-search";

try {
  await rekall.stores.get("contracts");
} catch (err) {
  if (err instanceof StoreNotFoundError) {
    console.error(err.fix); // "Create it: … rekall stores create contracts"
  } else if (err instanceof RekallError) {
    console.error(err.type, err.fix);
  }
}

Retries built in

Idempotent operations (and 429s) retry automatically with jittered backoff, up to maxRetries. Non-idempotent calls are never silently retried.

Edge runtimes

Because the client is pure fetch with zero dependencies, it runs unchanged on Vercel Edge Functions and Cloudflare Workers — set REKALL_API_KEY as an environment secret and new Rekall() just works.

Method summary

MethodReturns
rekall.store(name)Store (get-or-create)
rekall.stores.create / get / list / deleteStore / list / void
store.ingest(docs){ ingested, docIds }
store.ingestStream(iter, opts)void (streamed)
store.ingestFiles(glob){ ingested, docIds }
store.search(query, opts)SearchResult
store.documents.list / get / deletelist / info / void
rekall.health(){ status, version, engine }

On this page