Docs menu

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 speaksBase URL
OpenAI Chat Completionshttps://sator-api.princep.org/v1
Anthropic Messageshttps://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#

  1. Sign up with an email address — you will be asked to confirm it — then accept the Terms of Service.
  2. 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.
  3. 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#

bash
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):

json
{
  "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:

bash
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"}]
  }'
json
{
  "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#

python
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#

ts
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#

python
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#

ts
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 SatorSet up your tool has a page per tool with the exact config keys.
  • Browse modelsModels lists every id with its context window and max output.
  • Handle errorsErrors has both envelopes and every status and code.