SDK · TypeScript
anoman-ai · TypeScript
Zero-dependency client for browser, Node, Bun, Deno, and Edge runtimes. Async iterators for streaming, AbortController-aware cancellation, batch poll helper, typed _anoman metadata.
Install
Any package manager
npm install anoman-aiZero runtime dependencies. ESM + CJS dual export. npm ↗
Client init
Same client, every runtime
import { AnomanClient } from "anoman-ai";
const client = new AnomanClient({
apiKey: process.env.ANOMAN_API_KEY!,
// baseURL defaults to https://api.anoman.io
timeout: 120_000, // ms
maxRetries: 3, // retried only on 429 / 503 / 504
});For Cloudflare Workers / Vercel Edge / Deno, use the anoman-ai/edge entrypoint — no Node APIs.
Chat completions
Same shape as OpenAI + typed _anoman
const response = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What's the capital of Indonesia?" },
],
temperature: 0.7,
max_tokens: 200,
});
// Standard fields — identical to OpenAI SDK
console.log(response.choices[0].message.content);
console.log(response.usage.total_tokens);
// Anoman extension — fully typed
console.log(response.anoman.cost_usd); // "0.000041"
console.log(response.anoman.guardrails.injection); // { status: "pass", score: 0.02 }
console.log(response.anoman.cache.hit); // false
console.log(response.anoman.routing.region); // "id"Full endpoint reference at /docs/endpoints/chat-completions.
Streaming
Native async iteration
const stream = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "Tell me a short story" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
// The final _anoman frame is exposed as a promise on the stream
const meta = await stream.anomanMeta;
console.log(`Cost: ${meta.cost_usd}`);Batch
Enqueue + poll in one call
const job = await client.chat.completions.create({
model: "deepseek-v3",
messages: [{ role: "user", content: "Summarize this 50-page doc..." }],
preferBatch: true, // x-anoman-prefer-batch
});
console.log(`Queued ${job.id}, SLA ${job.sla_minutes}m`);
// Poll to completion — SDK handles 202/200 + Retry-After
const result = await client.pollBatch(job.id, {
deadlineMinutes: 30,
onProgress: (r) => console.log(` remaining ${r.sla_remaining_minutes}m`),
});
console.log(result.choices[0].message.content);
console.log(`Saved: $${result.anoman.savings_usd}`);Full batch lifecycle at /docs/endpoints/batch.
Errors
Typed exception hierarchy
All errors extend AnomanError. Use instanceof to branch.
import {
AnomanError,
AuthError, // 401, 403 auth_*
BudgetExceededError, // 402
GuardrailError, // 403 guardrail_*
RateLimitError, // 429
ProviderError, // 503, 504
} from "anoman-ai";
try {
const response = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Hi" }],
});
} catch (err) {
if (err instanceof GuardrailError) {
console.log(`Blocked: ${err.code} — ${err.message}`);
} else if (err instanceof RateLimitError) {
console.log(`Wait ${err.retryAfter}s`);
} else if (err instanceof ProviderError) {
console.log(`Upstream ${err.statusCode}: ${err.message}`);
} else if (err instanceof BudgetExceededError) {
console.log("Out of budget");
} else if (err instanceof AuthError) {
console.log("Auth failed");
} else if (err instanceof AnomanError) {
console.log(`Unexpected: ${err}`);
} else {
throw err; // not from Anoman
}
}Anoman-specific options
camelCase kwargs for headers
Common x-anoman-* headers exposed as typed kwargs.
const response = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [/* ... */],
sessionId: "conv-7k4mP", // x-anoman-session-id
agentId: "support-bot-v3", // x-anoman-agent-id
metadata: { customer_tier: "enterprise" }, // surfaces in traces
});