@anyr/sdk
v0.1.3
Published
Official TypeScript SDK for the AnyRouter LLM gateway API — chat, embeddings, images, models, credits, keys, presets, responses, and the Anthropic Messages API.
Downloads
2,247
Maintainers
Readme
@anyr/sdk
Official TypeScript SDK for the AnyRouter LLM gateway API.
One key, one base URL, every model — chat, embeddings, images, models, credits, key management, presets, the Responses API, and the Anthropic Messages API, with typed requests, typed responses, and a typed error envelope.
Install
npm install @anyr/sdkQuickstart
import { AnyRouter } from "@anyr/sdk"
// Reads the key from ANYROUTER_API_KEY when omitted.
const client = new AnyRouter()
const completion = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
messages: [{ role: "user", content: "Hello!" }],
})
console.log(completion.choices[0].message.content)Authentication
| Option | Env fallback | Used for |
| --- | --- | --- |
| apiKey (sk-ar-v1-…) | ANYROUTER_API_KEY | Inference (chat, embeddings, images, responses, messages) |
| managementKey (ak_…) | ANYROUTER_MANAGEMENT_KEY | Management plane (keys, presets) — falls back to apiKey |
const client = new AnyRouter({
apiKey: "<your-anyrouter-key>",
managementKey: "ak_...", // optional; falls back to apiKey
baseURL: "https://anyrouter.dev/api/v1", // default
headers: { "X-My-Header": "value" }, // merged into every request
maxRetries: 2, // default; retries transient failures (see below)
timeout: 600_000, // default 10 min; bounds time-to-first-byte
})Retries, timeouts & cancellation
Every request automatically retries transient failures — connection errors,
timeouts, and 408 / 409 / 429 / 5xx responses — with exponential
backoff and jitter, honoring the server's Retry-After / Retry-After-Ms
headers. The default budget is 2 retries; set maxRetries: 0 to disable.
The default per-request timeout is 10 minutes, applied to the time until
response headers arrive — so it never aborts an in-progress stream.
Both are overridable per request, alongside an AbortSignal for cancellation
and one-off headers:
const controller = new AbortController()
const stream = await client.chat.completions.create(
{ model: "openai/gpt-4o-mini", messages, stream: true },
{ signal: controller.signal, timeout: 30_000, maxRetries: 0 },
)
// later — stop generating
controller.abort()A caller-triggered abort throws APIUserAbortError and is never retried.
A timeout throws APITimeoutError.
Resources
Chat completions
// Non-streaming
const completion = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
messages: [{ role: "user", content: "Explain quantum entanglement simply." }],
temperature: 0.7,
max_tokens: 512,
})
console.log(completion.choices[0].message.content)
// Streaming
const stream = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
messages: [{ role: "user", content: "Write a haiku about TypeScript." }],
stream: true,
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "")
}
// With AnyRouter provider routing
const completion = await client.chat.completions.create({
model: "openai/gpt-4o",
messages: [{ role: "user", content: "Hello" }],
provider: {
order: ["openai", "deepinfra"],
allow_fallbacks: true,
},
session_id: "my-session-123",
})Embeddings
const result = await client.embeddings.create({
model: "openai/text-embedding-3-small",
input: ["Hello world", "AnyRouter rocks"],
encoding_format: "float",
})
console.log(result.data[0].embedding) // number[]Image generation
const result = await client.images.generate({
model: "dall-e-3",
prompt: "A futuristic city at sunset, digital art",
n: 1,
size: "1024x1024",
})
console.log(result.data[0].url)Models
// List all available models
const { data: models } = await client.models.list()
console.log(models.map((m) => m.id))
// Retrieve a single model
const model = await client.models.retrieve("openai/gpt-4o-mini")
console.log(model.context_length, model.pricing)Credits
const credits = await client.credits.retrieve()
console.log(`Balance: $${credits.balance.toFixed(4)} USD`)API Key management (management key required)
// Create a new key
const { key, data } = await client.keys.create({
name: "My production key",
limit: 10.0, // USD spend limit
limit_reset: "monthly",
})
console.log("Save this key — shown only once:", key)
// List all keys
const { data: keys } = await client.keys.list()
// Update a key
await client.keys.update(data.hash, { disabled: false, name: "Renamed key" })
// Delete a key
await client.keys.del(data.hash)Presets (management key required)
Presets are saved model configurations routed via @preset/<slug>.
// Create a preset
const preset = await client.presets.create({
slug: "my-assistant",
name: "My Assistant",
config: {
model: "openai/gpt-4o-mini",
system: "You are a helpful assistant.",
temperature: 0.5,
max_tokens: 1024,
},
})
// List presets
const { data: presets } = await client.presets.list()
// Use the preset in a chat completion
const completion = await client.chat.completions.create({
model: "@preset/my-assistant",
messages: [{ role: "user", content: "Help me." }],
})
// Update and delete
await client.presets.update("my-assistant", { config: { temperature: 0.8 } })
await client.presets.del("my-assistant")Responses API (OpenAI-compatible)
// Non-streaming
const response = await client.responses.create({
model: "openai/gpt-4o-mini",
input: "What is 2 + 2?",
})
console.log(response.output_text)
// Streaming
const stream = await client.responses.create({
model: "openai/gpt-4o-mini",
input: "Count to 5.",
stream: true,
})
for await (const event of stream) {
// handle server-sent events
}
// Retrieve / delete a stored response
const stored = await client.responses.retrieve(response.id)
await client.responses.del(response.id)Messages API (Anthropic-compatible)
// Non-streaming
const message = await client.messages.create({
model: "anthropic/claude-3-5-haiku-20241022",
max_tokens: 256,
system: "You are a concise assistant.",
messages: [{ role: "user", content: "Hello!" }],
})
console.log(message.content[0]) // { type: "text", text: "…" }
// Streaming
const stream = await client.messages.create({
model: "anthropic/claude-3-5-haiku-20241022",
max_tokens: 256,
messages: [{ role: "user", content: "Tell me a short joke." }],
stream: true,
})
for await (const event of stream) {
// handle Anthropic SSE events
}Error handling
All API errors throw typed subclasses of AnyRouterError:
import {
AnyRouterError,
AuthenticationError,
InsufficientCreditsError,
RateLimitError,
NotFoundError,
} from "@anyr/sdk"
try {
await client.chat.completions.create({ model: "...", messages: [] })
} catch (err) {
if (err instanceof AuthenticationError) {
console.error("Bad key:", err.message, "requestId:", err.requestId)
} else if (err instanceof InsufficientCreditsError) {
console.error("Top up your credits at https://anyrouter.dev/credits")
} else if (err instanceof RateLimitError) {
console.error("Rate limited — back off and retry")
} else if (err instanceof AnyRouterError) {
console.error(`HTTP ${err.status} ${err.code}: ${err.message}`)
}
}| Error class | HTTP status |
| --- | --- |
| AuthenticationError | 401 |
| InsufficientCreditsError | 402 |
| PermissionDeniedError | 403 |
| NotFoundError | 404 |
| ConflictError | 409 |
| RateLimitError | 429 |
| InternalServerError | 5xx |
| UpstreamError | 502, 503 |
| APIConnectionError | network failure |
| APITimeoutError | request timeout |
| APIUserAbortError | request aborted via AbortSignal |
Each error exposes .status, .code, .message, .requestId, and .metadata.
Vercel AI SDK
Using the Vercel AI SDK? See @anyr/ai-sdk-provider,
which wraps this SDK as a fully-typed Vercel AI provider.
License
MIT © Duyet Le
