From OpenAI
Keep the SDK and the code; change the base URL, the key and the model id.
The three changes
- Base URL
https://api.openai.com/v1becomeshttps://api.ai.ml/v1. - Key
sk-…becomesaiml-live-…(create one in the console,inferencescope). - Model
gpt-5-minibecomesopenai/gpt-5-mini. Every model isvendor/model; a bare OpenAI model name is also accepted and resolves toopenai/<name>.
const client = new OpenAI({
baseURL: (process.env.AIML_BASE_URL ?? "https://api.ai.ml") + "/v1",
apiKey: process.env.AIML_API_KEY,
});What stays the same
- Chat Completions request and response bodies, including
tools,tool_choice,response_format(json_objectandjson_schema),stream,stream_options,n,seed,logprobs,stop, penalties anduser. - Streaming frames (
chat.completion.chunk,data: [DONE]). - The error envelope
{"error":{"message","type","param","code"}}.codecarries the aiml code, which is more specific than OpenAI's; see Errors.
What is different
| Topic | OpenAI | aiml |
|---|---|---|
| Request id | x-request-id header | x-aiml-request-id header and the response id |
| Cost | not returned | aiml.cost.total_micro_usd in the body, x-aiml-cost-micro header |
| Usage on streams | stream_options.include_usage | same; the last chunk also carries aiml |
| Rate limits | per organisation | per key (rpm, tpm, concurrency), see Rate limits |
| Billing | postpaid | prepaid credits; 402 insufficient_credits when the balance cannot cover the reserve |
| Routing | one provider | several endpoints per model; aiml.route steers it |
Steering the route
The optional aiml object on the request carries what the OpenAI format cannot express:
{
"model": "openai/gpt-5-mini",
"messages": [{ "role": "user", "content": "hi" }],
"aiml": {
"route": {
"providers": { "order": ["openai", "azure"] },
"require": { "regions": ["eu"], "zdr": true, "max_price": { "output": 2.5 } },
"sort": "latency",
"fallbacks": true,
"ttft_budget_ms": 1500
},
"metadata": { "app": "support-bot", "tags": { "tier": "free" } }
}
}route.providers (only, ignore, order) and route.require (regions, zdr, data_policy, max_price in USD per million tokens, reasoning) narrow the candidate endpoints; sort, fallbacks, affinity, stream_retry, schema_retry, ttft_budget_ms and max_duration_ms shape how they are tried. metadata (app, user_id, tags) lands in analytics and on the generation record.
The Responses API
/v1/responses takes the same body the OpenAI SDK sends (client.responses.create): input as a string or items, instructions, tools (function and hosted: web_search_preview, file_search, code_interpreter, image_generation), tool_choice, text.format, reasoning, include, max_output_tokens, temperature, top_p, stream, user, metadata. Streaming returns the named events (response.created … response.completed) with sequence_number; the final response.completed object carries usage and aiml.
curl -sS "$BASE/v1/responses" \
-H "Authorization: Bearer $AIML_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"model\":\"${AIML_MODEL:-openai/gpt-5}\",\"input\":\"Say hello in one short sentence.\",\"max_output_tokens\":64,\"store\":false}" \
| python3 -c 'The gateway is stateless, which changes three things:
| Topic | OpenAI | aiml |
|---|---|---|
store | responses are stored for 30 days | never stored; store: true is ignored with a param_dropped warning and store: false is what reaches the provider |
previous_response_id, conversation, item_reference | continue a stored response | 422 unsupported_feature.previous_response_id (or .conversation, .item_reference); send the full history as input items |
| Reasoning across turns | the stored response carries it | ask for include: ["reasoning.encrypted_content"] (the gateway adds it whenever reasoning is set) and replay the returned reasoning item as is; its encrypted_content is what lets the model continue |
Every model behind the gateway is reachable through /v1/responses, including providers that speak another wire format: items translate to the IR and back, so input_image, input_file, function_call / function_call_output and text.format: json_schema work against Anthropic and Gemini endpoints too, with aiml.warnings listing anything the endpoint could not honour.