Overview
The ANTARES API gives your servers access to the models ANTARES serves. It is prepaid: you add credit in the developer console and every request is paid from that balance, which is separate from any ANTARES chat subscription.
Base URL: https://emtantares.com/api/developer. Every endpoint is under /v1 and exchanges JSON; amounts are integer micro-euros (millionths of a euro) sent as strings, such as "charged_micros": "41".
Before the model runs, a request holds its maximum possible charge: its input and max_tokens at the model's price. When the model has answered, only the measured usage is charged and the rest of the hold returns to your balance at once. Each project has a spending cap and may have a monthly limit; a request that could exceed either is refused before anything runs.
| Method | Path | Key scope |
|---|---|---|
| POST | /v1/messages | messages:write |
| POST | /v1/messages/count_tokens | messages:write |
| GET | /v1/models | any |
| GET | /v1/models/{model_id} | any |
| GET | /v1/requests/{idempotency_key} | any |
| POST | /v1/chat/completions | messages:write |
| POST | /v1/responses | responses:write |
Authentication and keys
Create a project and an API key in the developer console. The secret (ant_sk_v1_…) is shown once: keep it in your server's secret store. Send it as x-api-key: <key> or as Authorization: Bearer <key>; if both are sent they must be the same key.
Keys are for servers only. A request that carries an Origin header, which is every request made from a web page, is refused with server_side_only, so there is never a reason to put a key in a browser or a mobile app.
messages:writeallows Messages, token counting and chat completions;responses:writeallows the first text endpoint;models:readonly lists models. Any active key can list models and read the outcome of its own requests.- Keys expire after the validity you choose and can be revoked at any time; archiving a project revokes all its keys for good.
- The account needs a verified e-mail address and the current terms accepted.
Send antares-version: 2026-09-24 to pin the version your code was written for. Without it the newest version answers; an unknown version is refused with unsupported_version, never guessed.
Quickstart
Take a model id from GET /v1/models and send a first message.
curl https://emtantares.com/api/developer/v1/messages \
-H "x-api-key: $ANTARES_API_KEY" \
-H "antares-version: 2026-09-24" \
-H "content-type: application/json" \
-d '{
"model": "MODEL_ID",
"max_tokens": 256,
"messages": [{ "role": "user", "content": "Hello" }]
}'import Antares from "antares-sdk";
const client = new Antares(); // reads ANTARES_API_KEY
const message = await client.messages.create({
model: "MODEL_ID",
max_tokens: 256,
messages: [{ role: "user", content: "Hello" }],
});
console.log(message.content, message.billing?.charged_micros);from antares import Antares
client = Antares() # reads ANTARES_API_KEY
message = client.messages.create(
model="MODEL_ID",
max_tokens=256,
messages=[{"role": "user", "content": "Hello"}],
)
print(message["content"], message["billing"]["charged_micros"]){
"id": "msg_…",
"type": "message",
"role": "assistant",
"model": "MODEL_ID",
"content": [{ "type": "text", "text": "Hello! How can I help?" }],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": { "input_tokens": 9, "output_tokens": 8, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0 },
"billing": { "currency": "EUR", "charged_micros": "41" }
}Messages
POST /v1/messages generates the assistant's next turn of a conversation. messages alternates user and assistant turns and starts with a user turn; each turn's content is a string or a list of blocks (text, image, tool_use, tool_result).
| Field | Meaning |
|---|---|
model | A model id from /v1/models. There is no fallback: another model is never used instead. |
max_tokens | The most output tokens the model may produce (required). The hold is computed with it. |
system | Instructions for the whole conversation: a string or a list of text blocks. |
temperature, top_p | Sampling, temperature from 0 to 1. |
stop_sequences | Up to 4 strings that end the answer. |
tools, tool_choice | Tools the model may call (see Tools). |
metadata.user_id | An opaque id of your end user, for abuse reports. Never an e-mail address or a name. |
stream | true for server-sent events (see Streaming). |
The answer is a message with content blocks, a stop_reason (end_turn, max_tokens, tool_use or content_filter; a matched stop sequence ends the message with end_turn), usage in tokens and billing.charged_micros, the exact charge. Unknown fields are refused with invalid_request, so a typo never passes silently.
Streaming
With stream: true the answer arrives as server-sent events. Text arrives in content_block_delta events; a tool call arrives whole, after the text. message_delta carries the final stop reason, usage and charge, and message_stop ends the stream. A ping event every 15 seconds keeps idle connections open.
event: message_start
data: {"type":"message_start","message":{"id":"msg_…","type":"message","role":"assistant","content":[],"stop_reason":null,…}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{…},"billing":{"currency":"EUR","charged_micros":"41"}}
event: message_stop
data: {"type":"message_stop"}The hold, the spending checks and the input review run before the stream starts, so a refusal there is an ordinary HTTP error. After the stream has started, an error arrives as one final error event with the usual error object. The output review runs when the model has finished: text already sent cannot be withdrawn, a refused output ends the stream with content_blocked, and the usage is charged.
Streaming is offered only when it is enabled for the API: the model then lists streaming in its capabilities; otherwise a streamed request is refused with streaming_unavailable.
const stream = client.messages.stream({
model: "MODEL_ID", max_tokens: 512, messages: [{ role: "user", content: "Tell me a story" }],
});
for await (const text of stream.textStream()) process.stdout.write(text);
const final = await stream.finalMessage();Tools
Describe each tool with a name, a description and an input_schema (a JSON Schema of an object, up to 64 KB; up to 64 tools). When the model decides to call one, the answer stops with tool_use and contains a tool_use block. Run the tool yourself and send its output back in a tool_result block of the next user turn, with the same id.
tool_choice is auto (the model decides), any (it must call a tool), none, or { "type": "tool", "name": … } for one specific tool. A model that does not support tools refuses the request with model_capability_unavailable.
{
"model": "MODEL_ID",
"max_tokens": 512,
"tools": [{
"name": "get_weather",
"description": "Current weather for a city.",
"input_schema": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] }
}],
"messages": [
{ "role": "user", "content": "What is the weather in Rome?" },
{ "role": "assistant", "content": [{ "type": "tool_use", "id": "call_1", "name": "get_weather", "input": { "city": "Rome" } }] },
{ "role": "user", "content": [{ "type": "tool_result", "tool_use_id": "call_1", "content": "21 °C and sunny" }] }
]
}Images
User turns can include images as base64 image blocks: JPEG, PNG, GIF (not animated) or WebP, at most 5 MB each, 20 per request and 24 MB together. The declared media_type must match the bytes. The API never downloads a URL for you. Each image is held as up to 6,000 input tokens depending on its size.
{
"role": "user",
"content": [
{ "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": "iVBORw0KGgo…" } },
{ "type": "text", "text": "What is in this picture?" }
]
}Token counting
POST /v1/messages/count_tokens takes the same prompt (model, system, messages, tools) and returns an estimate of its input tokens, plus reservation_input_tokens: the conservative bound a real call with that prompt is held and rate-limited with. It is free and does not run the model.
curl https://emtantares.com/api/developer/v1/messages/count_tokens \
-H "x-api-key: $ANTARES_API_KEY" -H "content-type: application/json" \
-d '{ "model": "MODEL_ID", "messages": [{ "role": "user", "content": "Hello" }] }'
{ "input_tokens": 5, "reservation_input_tokens": 2311 }Models and prices
GET /v1/models lists the models available now, ordered by id, with pages of up to 100 (limit, after_id, before_id). Each model has its context window, output limit, capabilities, the provider that serves it (owned_by), a notice when its origin must be disclosed, and its prices in micro-euros per million tokens, charged from your credit (VAT is paid when you buy credit, as shown at checkout). Prices carry a revision and an expiry date; the price that applies is the one in force when the request is made.
No model is available through the API yet.
Errors
Every error has the same body. error.type is the broad class and follows the HTTP status; error.code is the precise, stable reason to branch on; error.message is a fixed English sentence that never repeats your input. Quote request_id (also in the request-id header) to support.
{
"type": "error",
"error": {
"type": "billing_error",
"code": "API_INSUFFICIENT_FUNDS",
"message": "The prepaid balance does not cover this request's maximum charge. Add funds in the console.",
"request_id": "5f0c2c1e-…"
},
"request_id": "5f0c2c1e-…"
}| Status | Type | Examples of code |
|---|---|---|
| 400 | invalid_request_error | invalid_request, unsupported_version, context_limit_exceeded, model_capability_unavailable, streaming_unavailable |
| 401 | authentication_error | invalid_api_key |
| 402 | billing_error | API_INSUFFICIENT_FUNDS, API_PROJECT_CAP_REACHED, API_PROJECT_MONTHLY_CAP_REACHED |
| 403 | permission_error | insufficient_scope, server_side_only, API_WALLET_FROZEN |
| 404 | not_found_error | model_unavailable, API_REQUEST_NOT_FOUND |
| 409 | invalid_request_error | request_already_recorded, API_IDEMPOTENCY_CONFLICT |
| 413 | request_too_large | request_too_large |
| 422 | invalid_request_error | content_blocked |
| 429 | rate_limit_error | rate_limit_exceeded (with retry-after) |
| 502, 503 | api_error | response_not_deliverable, usage_not_verified, safety_unavailable, service_unavailable |
Retry 429 after retry-after, and 5xx with a backoff, always with the same Idempotency-Key. Do not retry other 4xx errors unchanged.
Rate limits and tiers
Limits apply to each API key per minute: requests, input tokens (counted with the request's conservative bound when it is admitted) and output tokens (counted as they are produced). Every answer carries antares-ratelimit-tier and, for each bucket, antares-ratelimit-<bucket>-limit, -remaining and -reset. A request over a limit receives HTTP 429 with retry-after.
The tier follows the credit you have bought, net of refunds. Test credit always uses the tier 1 limits.
| Tier | From credit bought | Requests per minute | Input tokens per minute | Output tokens per minute |
|---|---|---|---|---|
0 tier_0 | 0,00 € | 10 | 20 000 | 4000 |
1 tier_1 | 10,00 € | 60 | 100 000 | 20 000 |
2 tier_2 | 50,00 € | 300 | 400 000 | 80 000 |
3 tier_3 | 250,00 € | 1000 | 1 500 000 | 300 000 |
4 tier_4 | 1000,00 € | 3000 | 4 000 000 | 800 000 |
Idempotency and request status
Send an Idempotency-Key (8 to 128 of A-Z a-z 0-9 . _ : -) with every paid request. A retry with the same key and the same body is never run or charged twice: it receives 409 request_already_recorded. The same key with a different body receives 409 API_IDEMPOTENCY_CONFLICT. Without a key the server issues one and returns it in the idempotency-key response header.
After a timeout or a lost connection, read what happened with GET /v1/requests/{idempotency_key} instead of sending the request again: settled (charged), released (nothing charged), reserved (still running) or uncertain (the provider's usage could not be confirmed; the hold is reviewed and released within 24 hours unless usage is confirmed). The status never includes the content.
curl https://emtantares.com/api/developer/v1/messages -H "Idempotency-Key: order-1234" …
# later, after a timeout:
curl https://emtantares.com/api/developer/v1/requests/order-1234 -H "x-api-key: $ANTARES_API_KEY"
{ "id": "…", "status": "settled", "fundingMode": "live", "reservedMicros": "1200", "chargedMicros": "41", … }Test credit
Accounts that ANTARES allowlists for testing can buy test credit with Stripe test cards and spend it by sending antares-funding: test. Test credit never mixes with paid credit, uses the tier 1 limits and a monthly budget set by ANTARES, and each response says "funding": "test". For every other account the header is refused.
Data handling
- The API does not store prompts, images or answers. For each paid request it records the key and project, the model, the token counts and the provider's request id, the amounts held and charged, the
Idempotency-Keyand a SHA-256 fingerprint of the request body (to recognise retries). - To answer, the prompt is sent to the provider that serves the model (
owned_by), under that provider's terms for API traffic. - Prompts and answers pass an automated safety review before and after the model runs, which may use a third-party moderation service. When content is refused, only a hash of it, the categories and its length are recorded.
storeis not supported: on the compatibility endpoints onlyfalseis accepted.- With the developer platform, what you choose to store is kept: uploaded files until you delete them, and a batch's requests until each has run, its results 29 days after it ends. Files, batches and admin keys are listed in the account's data export and erased with the account.
Developer platform
The sections below describe the developer platform: files, documents with citations, structured outputs, prompt caching, server tools and MCP servers, message batches and the Admin API. They are available where ANTARES has enabled them; elsewhere their routes answer 404 NOT_ENABLED and the fields are refused as unknown. They change nothing about prices: every model call is charged exactly as a direct call to /v1/messages.
| Method | Path | Key |
|---|---|---|
| POST, GET | /v1/files | messages:write (POST), any |
| GET, DELETE | /v1/files/{file_id} | any, messages:write (DELETE) |
| GET | /v1/files/{file_id}/content | any |
| POST, GET | /v1/messages/batches | messages:write (POST), any |
| GET, DELETE | /v1/messages/batches/{batch_id} | any, messages:write (DELETE) |
| POST | /v1/messages/batches/{batch_id}/cancel | messages:write |
| GET | /v1/messages/batches/{batch_id}/results | any |
| GET | /v1/organizations/me | admin |
| GET, POST | /v1/organizations/workspaces | admin |
| GET, POST | /v1/organizations/workspaces/{workspace_id} | admin |
| POST | /v1/organizations/workspaces/{workspace_id}/archive | admin |
| GET | /v1/organizations/api_keys | admin |
| GET, POST | /v1/organizations/api_keys/{api_key_id} | admin |
| GET | /v1/organizations/usage_report/messages | admin |
| GET | /v1/organizations/cost_report | admin |
Files
POST /v1/files stores one file (multipart, field file, at most 32 MiB) for the key's project, so later requests can name it instead of sending it again. The type is read from the bytes: PDF, plain text, Markdown, CSV, JSON, DOCX, XLSX, PPTX, JPEG, PNG, GIF or WebP. A project holds at most 500 files and 512 MiB. GET /v1/files lists them, GET /v1/files/{file_id} describes one, GET /v1/files/{file_id}/content downloads it and DELETE removes it at once. Files are free.
curl https://emtantares.com/api/developer/v1/files \
-H "x-api-key: $ANTARES_API_KEY" \
-H "antares-version: 2026-09-24" \
-F "file=@wing-loads.pdf"Documents and citations
A user turn can include document blocks: a base64 PDF, plain text, a list of text blocks, or a stored file ({ "type": "file", "file_id": … }; a PDF or Office file is read up to 8 MiB). An image block can also name a stored image by file_id. Up to 20 documents a request.
With citations.enabled, the document is given to the model in numbered passages and the answer's text blocks carry citations: the exact cited_text with char_location offsets into the document, or content_block_location for a document given as blocks. Text that cites nothing stays a plain block. title names the document; context is shown to the model but never cited.
{
"model": "MODEL_ID",
"max_tokens": 512,
"messages": [{ "role": "user", "content": [
{ "type": "document", "source": { "type": "file", "file_id": "file_..." },
"title": "Wing loads", "citations": { "enabled": true },
"cache_control": { "type": "ephemeral" } },
{ "type": "text", "text": "Which load case sizes the spar?" }
] }]
}Structured outputs
output_format: { "type": "json_schema", "schema": … } asks for one JSON object that matches the schema (an object at the top, no $ref, at most 64 KB). The serving provider is asked for constrained output, so the model must list structured-output in its capabilities. The message then carries parsed_output: the object, checked again against the schema, or null when the answer did not match.
{
"model": "MODEL_ID",
"max_tokens": 256,
"output_format": { "type": "json_schema", "schema": {
"type": "object",
"properties": { "part": { "type": "string" }, "mass_kg": { "type": "number" } },
"required": ["part", "mass_kg"]
} },
"messages": [{ "role": "user", "content": "The bracket weighs 120 g." }]
}Prompt caching
Put cache_control: { "type": "ephemeral" } on a system block, a text block, a document or a tool to mark where a reusable prefix ends (at most 4 marks a request). Where the serving provider takes explicit cache marks, the prefix is cached for about five minutes; elsewhere caching is automatic or absent. Only the five-minute lifetime is available: ttl other than 5m is refused with cache_ttl_unavailable. usage reports cache_read_input_tokens and cache_creation_input_tokens, charged at the model's listed cache prices.
Server tools and MCP servers
Server tools run on ANTARES between two model calls of one request. web_search searches through ANTARES's own search service and is offered only while that service costs nothing per query; otherwise it is refused with server_tool_unavailable. web_fetch reads one public https page (never a private, local or metadata address). Both take max_uses and allowed_domains or blocked_domains. Code execution and the other server toolsets are not available on this API and are refused with server_tool_unavailable.
mcp_servers lists up to 5 remote MCP servers (public https). ANTARES lists their tools, offers them to the model and calls them when it asks; the authorization_token is used for that request only and never stored. The answer shows what ran as server_tool_use with web_search_tool_result or web_fetch_tool_result, and mcp_tool_use with mcp_tool_result.
Such a request may take several model calls, each charged like a direct call under its own idempotency key (the first is yours). billing.charged_micros is their sum and billing.model_calls their number; usage.server_tool_use counts searches and page reads. After 8 calls the message stops with pause_turn: send it back to continue. These requests cannot stream.
{
"model": "MODEL_ID",
"max_tokens": 1024,
"tools": [
{ "type": "web_search_20250305", "name": "web_search", "max_uses": 3 },
{ "type": "web_fetch_20250910", "name": "web_fetch", "allowed_domains": ["example.com"] }
],
"mcp_servers": [{ "type": "url", "url": "https://mcp.example.com/mcp", "name": "inventory",
"authorization_token": "YOUR_TOKEN" }],
"messages": [{ "role": "user", "content": "Check the stock of part 42." }]
}Message batches
POST /v1/messages/batches takes up to 1,000 requests, each with a custom_id and the params of a /v1/messages call (no streaming). They are checked at once and run in the background within 24 hours. Each is an ordinary paid call recorded under the idempotency key batch_<batch>_<position>, so its outcome can also be read at /v1/requests/{idempotency_key}; there is no separate batch price. Revoking the key that created a batch stops its remaining requests.
Read the status with GET /v1/messages/batches/{batch_id} (in_progress, canceling, ended, with counts per outcome), cancel with POST …/cancel, and once it has ended read GET …/results: one JSON line per request, in order, succeeded with the message or errored, canceled or expired. Results are kept 29 days, or until you delete the batch.
curl https://emtantares.com/api/developer/v1/messages/batches \
-H "x-api-key: $ANTARES_API_KEY" \
-H "antares-version: 2026-09-24" \
-H "content-type: application/json" \
-d '{ "requests": [
{ "custom_id": "a", "params": { "model": "MODEL_ID", "max_tokens": 128,
"messages": [{ "role": "user", "content": "Summarise report A." }] } }
] }'Admin API
An admin key (ant_admin_v1_…, created in the console under Admin keys and shown once) works only on /v1/organizations/…: it lists, creates, renames and archives workspaces (the console's projects), lists API keys and renames or deactivates them, and reads reports. It can never call a model, and an API key is refused there with admin_key_required.
GET /v1/organizations/usage_report/messages returns requests and tokens per UTC day (bucket_width=1d, up to 31 days) or hour (1h, up to 2 days), grouped by model, workspace_id or api_key_id. GET /v1/organizations/cost_report returns the charges already recorded for settled requests, and what is still held, in EUR micros. Neither computes a price: they add up what each request was charged.
curl "https://emtantares.com/api/developer/v1/organizations/usage_report/messages?starting_at=2026-10-01T00:00:00Z&group_by[]=model" \
-H "x-api-key: $ANTARES_ADMIN_KEY" \
-H "antares-version: 2026-09-24"Computer and browser toolsets
Add { "type": "computer_v1", "name": "computer", "display_width_px": 1280, "display_height_px": 800 } or { "type": "browser_v1", "name": "browser" } to tools. They are client tools: the model asks for one action at a time (screenshot, click, type, key, scroll… or, for the browser, navigate, read_page, find, click, type…) and your own driver performs it on a screen or browser you control. Answer with a tool_result; a screenshot goes in it as an image block (base64 or a file id). ANTARES never touches your machine.
Safety: run the driver in a separate virtual machine or browser profile without your own accounts, show the person what is about to happen, ask before anything irreversible (sending, buying, deleting), and treat page text as data: the model is told the same.
Advisor tool
{ "type": "advisor_v1", "name": "advisor", "model": "…", "max_uses": 2 } lets the model consult a second engine on a hard step. ANTARES sends that engine the conversation so far and the question, and returns its advice as an advisor_tool_result. Each consultation is an ordinary paid call of the advisor's engine, counted in billing and in usage.server_tool_use.advisor_requests.
Skills
POST /v1/skills stores a skill from its SKILL.md (frontmatter with name and description, then the instructions) and up to 20 text files; POST /v1/skills/{skill_id}/versions adds an immutable version. Name skills in a request with skills: [{ "skill_id": "skill_…", "version": 2 }]: the model sees each skill's name and description and reads its instructions or files with the read_skill tool. Storing and reading skills is free.
Compaction and system messages
compaction: { "now": true } (or "trigger_input_tokens": 150000) summarises the turns before the latest keep_recent_messages (6 by default) with one paid call of the request's model, and answers from the summary. The response starts with a compaction block: send it back as a user block in place of those turns. A { "role": "system" } message may appear anywhere in messages: it applies from that point on. Tools may change between turns.
Hosted agents
POST /v1/agents saves an agent (model, system prompt, tools, skills, max_tokens). POST /v1/sessions starts a conversation with it, optionally with a memory store (POST /v1/memory_stores). POST /v1/sessions/{session_id}/events with { "content": … } runs one turn: it is one /v1/messages request built from the agent and the session, charged exactly as such under your Idempotency-Key, and a retry with the same key returns the recorded turn. With a memory store the agent gets the memory tool (view, write, delete), and its entries are kept for later sessions.
Teams: give an agent subagents (other agents of the same project, with a description). Its turns then offer the delegate server tool (one task to one sub-agent) and run_workflow (a plan of up to 8 tasks, with depends_on, run in parallel where possible); each sub-agent answer is one ordinary paid call of that agent's model. POST /v1/sessions/{session_id}/grades with { "model": …, "rubric": [{ "criterion": …, "weight": 1 }] } scores the transcript (0 to 10 per criterion, weighted overall) in one paid call. POST /v1/agents/{agent_id}/deployments with a daily or weekly schedule (time and time zone) and content opens a new session at each time and runs that first turn as your key.
OpenTelemetry export
With an admin key, PUT /v1/organizations/workspaces/{workspace_id}/otel_export with { "endpoint": "https://…/v1/traces", "headers": { … } } sends every finished request of the workspace to your OpenTelemetry collector as an OTLP/HTTP JSON span: model, tokens, status and the charge already recorded, never prompt or answer text. Headers are kept sealed and never returned. Spans leave within minutes of the request.
Organisation webhooks
With an admin key, POST /v1/organizations/webhooks with { "url": "https://…", "events": ["message_batch.ended"] } subscribes an https address to the organisation's events: message_batch.ended, workspace.created, workspace.archived, api_key.deactivated, session.turn_completed, skill.version_created. The signing secret is returned once. Each delivery is a JSON event (id, type, created_at, organization_id, data) signed in antares-webhook-signature: t=<seconds>,v1=<hex HMAC-SHA256 of "<t>.<body>">; check it and reject anything older than five minutes. Answer 2xx within 10 seconds; otherwise ANTARES retries after 1, 5 and 30 minutes, then 2, 6, 12 and 24 hours. POST …/test sends a webhook.test event at once and GET …/deliveries shows the latest attempts.
Tool runner in the SDKs
The SDKs run the tool loop for you: give each tool a run function, and client.beta.messages.toolRunner(...) (Python: client.beta.messages.tool_runner(...)) sends the request, runs the tools the model asks for, sends their results back and stops when the model answers without a tool call (at most 10 turns by default). Each turn is a paid call with its own idempotency key; an exception in a tool is sent back as an error result.
const { message } = await client.beta.messages.toolRunner({
model: "MODEL_ID",
max_tokens: 512,
messages: [{ role: "user", content: "What is 12 x 7?" }],
tools: [{
name: "multiply",
input_schema: { type: "object", properties: { a: { type: "number" }, b: { type: "number" } } },
run: ({ a, b }) => String(Number(a) * Number(b)),
}],
});Compatibility endpoints
POST /v1/chat/completions accepts the Chat Completions request shape that many existing SDKs and tools send, on the same prepaid path: set the client's base URL to the one below and the API key as its key. Only what ANTARES can honour is accepted: one choice, inline data: images, temperature up to 1. Without max_tokens the hold uses up to 4,096 output tokens. Errors on this route use the { "error": { "message", "type", "param", "code" } } body.
import os
from openai import OpenAI # or any client that lets you set the base URL
client = OpenAI(base_url="https://emtantares.com/api/developer/v1", api_key=os.environ["ANTARES_API_KEY"])
reply = client.chat.completions.create(model="MODEL_ID", max_tokens=256,
messages=[{"role": "user", "content": "Hello"}])POST /v1/responses is the first, text-only endpoint of this API, kept for existing callers. New integrations use /v1/messages.
Versions and changes
The current version is 2026-09-24. Additions that cannot break a correct client (a new endpoint, a new optional field, a new error code or event) can arrive within a version; anything else gets a new version, announced in advance, and the older one keeps answering to clients that pin it.
2026-09-24: Messages with tools, images and streaming; token counting; model details and pages; tiers with rate-limit headers; typed errors;x-api-key; optional Idempotency-Key and request status; Chat Completions compatibility.