Start
Quickstart
Working examples in the ways people actually call this API. Every snippet targets the credit surface, so swap the base URL if you hold a token key.
Setup (permalink)
You need a key. Put it in your environment rather than in source, and every example below picks it up without further editing.
export KORLOGIC_API_KEY="kl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Install a client if you want one. curl and fetch need nothing.
pip install openai # OpenAI-compatible clients
pip install anthropic # Anthropic Messages clientsnpm install openai
npm install @anthropic-ai/sdkYour first call (permalink)
A single chat completion against claude-sonnet-5. Any id from the model catalogue works the same way.
curl https://api.korlogic.com/v1/chat/completions \
-H "Authorization: Bearer $KORLOGIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"messages": [{ "role": "user", "content": "Say hello in five words." }]
}'import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["KORLOGIC_API_KEY"],
base_url="https://api.korlogic.com/v1",
)
response = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(response.choices[0].message.content)
print(response.usage) # the tokens your key was billed forimport OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.KORLOGIC_API_KEY,
baseURL: "https://api.korlogic.com/v1",
});
const response = await client.chat.completions.create({
model: "claude-sonnet-5",
messages: [{ role: "user", content: "Say hello in five words." }],
});
console.log(response.choices[0].message.content);
console.log(response.usage);const response = await fetch("https://api.korlogic.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KORLOGIC_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-5",
messages: [{ role: "user", content: "Say hello in five words." }],
}),
});
if (!response.ok) {
const body = await response.json().catch(() => null);
const error = body?.error;
throw new Error(`${response.status} ${error?.code}: ${error?.message}`);
}
const data = await response.json();
console.log(data.choices[0].message.content);The response is the standard OpenAI chat completion object, including the usage block. That block is what your key is billed against, and what shows up in the usage checker.
Streaming (permalink)
Set stream: true and read server-sent events. The gateway forwards the stream byte for byte, so any SDK’s streaming helper works unchanged.
curl -N https://api.korlogic.com/v1/chat/completions \
-H "Authorization: Bearer $KORLOGIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"stream": true,
"messages": [{ "role": "user", "content": "Count to five." }]
}'stream = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Count to five."}],
stream=True,
)
for chunk in stream:
if not chunk.choices:
continue # the final usage frame has no choices
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)const stream = await client.chat.completions.create({
model: "claude-sonnet-5",
messages: [{ role: "user", content: "Count to five." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}const response = await fetch("https://api.korlogic.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KORLOGIC_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-5",
stream: true,
messages: [{ role: "user", content: "Count to five." }],
}),
});
if (!response.ok || !response.body) {
throw new Error(`Stream failed with ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() ?? ""; // keep the partial line for the next read
for (const line of lines) {
if (!line.startsWith("data:")) continue;
const payload = line.slice(5).trim();
if (!payload || payload === "[DONE]") continue;
const event = JSON.parse(payload);
process.stdout.write(event.choices?.[0]?.delta?.content ?? "");
}
}- You do not need to set
stream_options. On the OpenAI-style paths (/chat/completions,/responses,/completions) usage reporting is requested on your behalf, so a streamed request is metered as accurately as a buffered one. - Billing settles when the stream finishes, so a streamed request appears in the usage checker a moment after its last token.
- Use
curl -Nwhen testing by hand. Without it curl buffers the whole response and streaming looks like it did nothing.
Anthropic Messages (permalink)
The Anthropic format is served on the same key at /messages. Two things it needs that Chat Completions does not: max_tokens in the body, which is required, and the anthropic-version header. The SDKs send the version header for you, so only raw HTTP callers have to add it.
curl https://api.korlogic.com/v1/messages \
-H "x-api-key: $KORLOGIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 256,
"messages": [{ "role": "user", "content": "Say hello in five words." }]
}'import os
from anthropic import Anthropic
# No /v1 here: the Anthropic SDK appends /v1/messages itself.
client = Anthropic(
api_key=os.environ["KORLOGIC_API_KEY"],
base_url="https://api.korlogic.com",
)
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=256, # required by the Messages API
messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(message.content[0].text)
print(message.usage)import Anthropic from "@anthropic-ai/sdk";
// No /v1 here: the Anthropic SDK appends /v1/messages itself.
const client = new Anthropic({
apiKey: process.env.KORLOGIC_API_KEY,
baseURL: "https://api.korlogic.com",
});
const message = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 256, // required by the Messages API
messages: [{ role: "user", content: "Say hello in five words." }],
});
console.log(message.content[0].text);Streaming works the same way, with the SDK’s own helper.
with client.messages.stream(
model="claude-sonnet-5",
max_tokens=256,
messages=[{"role": "user", "content": "Count to five."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)const stream = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 256,
stream: true,
messages: [{ role: "user", content: "Count to five." }],
});
for await (const event of stream) {
if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
process.stdout.write(event.delta.text);
}
}Listing models (permalink)
The catalogue endpoint returns the upstream model list in OpenAI list format. It is the quickest way to confirm that a key and a base URL agree, because it needs no request body.
curl https://api.korlogic.com/v1/models \
-H "Authorization: Bearer $KORLOGIC_API_KEY"It reports the catalogue, not your permissions. If your key has a model allowlist, this response is still the full list. See Models.
If it fails (permalink)
| You see | Usually means |
|---|---|
| 401 | A mistyped or truncated key, or the right key on the wrong base URL. Check which surface your key belongs to. |
| 402 | Out of credit, or out of token budget. Confirm in the usage checker. |
| 403 | The key is suspended, expired, or not scoped to the model you asked for. |
| 404 | The path was built wrong, usually a doubled or a missing /v1. |
| 400 on Messages | max_tokens is missing. The Anthropic Messages API requires it on every request. |
Every status and code, with what to do about each, is on Errors.