Quickstart
Four steps: pick the base URL for your tool, get a key, add credit, make a call.
1. The two base URLs#
The one thing everyone gets wrong first. Sator has two endpoints, and the base URL differs by a /v1:
| If your tool speaks | Base URL |
|---|---|
| OpenAI Chat Completions | https://sator-api.princep.org/v1 |
| Anthropic Messages | https://sator-api.princep.org (no /v1 — the SDK appends it) |
Every model is available on both. If your tool has an "OpenAI base URL" field, use the first; if it has an "Anthropic base URL" field, use the second.
2. Get a key#
- Sign up with an email address — you will be asked to confirm it — then accept the Terms of Service.
- In the dashboard, create an API key. Give it a name — one per tool or machine is a good habit, because usage is attributed per key.
- Copy it now. The key is shown in full exactly once; after that only its last four characters are visible. A lost key is revoked and replaced, never recovered.
You can create keys at a $0 balance. Only calls need credit, so you can wire up your tools before you pay.
A key looks like sk-sator-v1- followed by 24 letters and digits. See Authentication for the headers it goes in.
3. Add credit#
Credits are prepaid, in US dollars, and never expire. The minimum top-up is $10. Every request debits your balance at the per-token rates on the price page, and nothing recurs. Details in Billing.
4. Make a call#
Every sample uses deepseek-v4-flash; any id from Models works in its place.
curl — OpenAI wire#
curl https://sator-api.princep.org/v1/chat/completions \
-H "Authorization: Bearer $SATOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "Say hi"}],
"max_tokens": 64
}'The response is a standard chat completion (a live one, trimmed of null fields):
{
"id": "router-841ccc4fe2eb6c57ce12fa44e2709764",
"object": "chat.completion",
"created": 1787596599,
"model": "deepseek-v4-flash",
"choices": [{
"index": 0,
"finish_reason": "stop",
"message": {"role": "assistant", "content": "Hi!", "reasoning_content": "The user says hi, so greet them."}
}],
"usage": {"prompt_tokens": 85, "completion_tokens": 26, "total_tokens": 111}
}Three things to expect: the id prefix varies by model (router-…, chatcmpl-…), so do not parse it; prompt_tokens includes the model's chat-template overhead, which is why six words cost 85; and reasoning models return their reasoning as reasoning_content beside content, and bill it as output.
curl — Anthropic wire#
Note the bare host, the required max_tokens, and the anthropic-version header:
curl https://sator-api.princep.org/v1/messages \
-H "x-api-key: $SATOR_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"max_tokens": 64,
"messages": [{"role": "user", "content": "Say hi"}]
}'{
"id": "msg_ca0525a31544409394c9c37b48200d05",
"type": "message",
"role": "assistant",
"model": "deepseek-v4-flash",
"content": [
{"type": "text", "text": "Hi!"}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {"input_tokens": 85, "output_tokens": 22, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0}
}A reasoning model still reasons — output_tokens counts it — and returns that reasoning as a thinking block ahead of the text block only when the request sets "thinking": {"type": "enabled", "budget_tokens": 1024}. Read the reply by block type, not by index — the samples below do.
Python — openai#
from openai import OpenAI
client = OpenAI(base_url="https://sator-api.princep.org/v1", api_key="sk-sator-v1-...")
completion = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Say hi"}],
)
print(completion.choices[0].message.content)TypeScript — openai#
import OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'https://sator-api.princep.org/v1', apiKey: process.env.SATOR_API_KEY });
const completion = await client.chat.completions.create({
model: 'deepseek-v4-flash',
messages: [{ role: 'user', content: 'Say hi' }],
});
console.log(completion.choices[0].message.content);Python — anthropic#
from anthropic import Anthropic
client = Anthropic(base_url="https://sator-api.princep.org", auth_token="sk-sator-v1-...")
message = client.messages.create(
model="deepseek-v4-flash",
max_tokens=64,
messages=[{"role": "user", "content": "Say hi"}],
)
print(next(block.text for block in message.content if block.type == "text"))TypeScript — @anthropic-ai/sdk#
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({ baseURL: 'https://sator-api.princep.org', authToken: process.env.SATOR_API_KEY });
const message = await client.messages.create({
model: 'deepseek-v4-flash',
max_tokens: 64,
messages: [{ role: 'user', content: 'Say hi' }],
});
const reply = message.content.find((block) => block.type === 'text');
console.log(reply?.type === 'text' ? reply.text : '');Then#
- Point your editor at Sator — Set up your tool has a page per tool with the exact config keys.
- Browse models — Models lists every id with its context window and max output.
- Handle errors — Errors has both envelopes and every status and code.