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-searchbun add rekall-searchpnpm add rekall-searchyarn add rekall-searchClient
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}");Search
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 stringDocuments
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 fallbackSee 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
| Method | Returns |
|---|---|
rekall.store(name) | Store (get-or-create) |
rekall.stores.create / get / list / delete | Store / 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 / delete | list / info / void |
rekall.health() | { status, version, engine } |
Errors
Fail with the fix. Every Rekall error carries a type, a human message, and the exact command or code that resolves it.
Python SDK
rekall-search — the same shape as the TypeScript SDK, Pythonic surface. Sync-first with AsyncRekall, search_many for eval loops, and LangChain / LlamaIndex adapters in-package.