OpenAI compatibility
Exactly which endpoints exist, which fields are validated and forwarded, and what is refused.
Atlas is OpenAI-compatible at the wire level. The design rule behind it is worth knowing, because it explains the edges:
Permissive on what is not modelled, strict on what is. A field Atlas does not know about is accepted and dropped, never rejected. A field Atlas does model is validated, and a failure names the offending field.
Real SDKs send fields defensively and add new ones between releases. Rejecting an unrecognised key is exactly the "works against OpenAI, fails against you" complaint that compatibility exists to prevent — so unknown top-level fields are silently discarded rather than refused, and never reach the backend.
Endpoints
| Endpoint | |
|---|---|
POST /v1/chat/completions | Yes |
GET /v1/models | Yes |
GET /v1/models/{model} | Yes |
That is the whole inference surface. There is no /v1/completions (the legacy
text endpoint), no /v1/embeddings, no /v1/responses, no /v1/assistants,
no /v1/files, no /v1/batches, no audio or image endpoints, and no
fine-tuning.
Authentication is Authorization: Bearer <key> only. There is no query-string
key, no api-key header, and no organization header — your organization is
determined by the key.
Request fields
These are modelled, range-checked, and passed to the serving backend:
| Field | Constraint |
|---|---|
model | Required, non-empty. Must resolve in the catalog. |
messages | Required, at least one. |
temperature | 0 – 2 |
top_p | 0 – 1 |
frequency_penalty | −2 – 2 |
presence_penalty | −2 – 2 |
max_tokens | Positive integer |
max_completion_tokens | Positive integer |
stop | A string, or up to 4 strings |
seed | Integer |
logit_bias | Object of string → number |
logprobs | Boolean |
top_logprobs | 0 – 20 |
stream | Boolean |
stream_options | See the note below |
tools, tool_choice, parallel_tool_calls | See Tool calling |
response_format | See Structured outputs |
user, safety_identifier, prompt_cache_key, service_tier, store, metadata | Carried |
Validated and forwarded is not the same as honored. Atlas passes these
through to the serving backend; whether a given sampling parameter takes
effect is the backend's behavior, not an Atlas guarantee. temperature,
top_p, stop, and the penalties are the ones you can rely on in
practice.
stream_options
Atlas always sends stream_options: {"include_usage": true} to the backend on a
streamed request, whatever you set — metering cannot depend on a caller's flag —
and the resulting usage frame is forwarded to you. Your streaming code must
tolerate a final chunk whose choices is [] even if you never asked for
usage. See Streaming.
On a non-streaming request, stream_options is dropped.
Messages
Accepted roles: system, developer, user, assistant, tool.
Content may be a string or an array of content parts. Text parts are the supported form. A content part of a type Atlas does not model is carried rather than refused — but the catalog is text-only, so no model serves image content. Do not build on image parts.
An assistant message may carry tool_calls and refusal; a tool message
requires tool_call_id. This is the OpenAI shape unchanged, so replaying a
conversation works as-is.
Responses
chat.completion and chat.completion.chunk in the OpenAI shape, with one
additive field: request_id, also returned as the x-request-id header.
SDKs ignore the body field; the header is what OpenAI clients surface on
exceptions.
finish_reason is one of stop, length, tool_calls, content_filter,
function_call.
Errors
The status codes and error.type values are OpenAI's, so existing retry and
backoff logic works unmodified. param and code are always present —
explicitly null when unknown — because that is what OpenAI sends and some
clients read the keys unconditionally.
One Atlas-specific distinction is worth wiring in: 429 rate_limit_exceeded
is your limit; 503 server_overloaded is Atlas capacity. The overload carries
no quota headers at all. See Error codes.
Known differences
OpenAI returns x-ratelimit-* on successful responses too. Atlas emits them
on the rate-limit rejection itself. Track headroom through
usage rather than by scraping headers off every 200.
See stream_options above.
See the Refused tab.
Atlas never changes what an identifier serves. A weight change is published under a new identifier, and the old one keeps serving for a published minimum window. See Model lifecycle.
Use the OpenAI Python or Node client. A separate Atlas client library would be a second integration surface to keep in step with the first, which is the opposite of the point.