Inference
OpenAI-compatible model inference: one fixed price per model, zero data retention, billed per token from your credits.
Inference serves large language models behind an OpenAI-compatible API. Use any OpenAI client: set the base URL and an API key, then call chat completions.
- One price per model. Every model has one fixed price per token, at all hours. The price is in
GET /v1/inference/modelsand on every response asusage.cost. - Zero data retention. Prompts and outputs are never stored or used for training.
- Tools, vision and reasoning. Function calling on every model; image input and
reasoning_effortwhere the model lists them. - Billed from your credits, per token, like every other Platinum meter.
Early access
Inference is in early access. An organization without access gets 404 or 403 inference_not_enabled. Request access and say which models and volume you need.
Base URL and authentication
| Setting | Value |
|---|---|
| Base URL | https://api.platinum.dev/v1/inference |
| Auth | Authorization: Bearer pt_live_… |
| Scope | inference (inference:read lists models and usage; inference:write sends completions) |
Create a key with the inference scope:
- Dashboard: Inference tab → Create an inference key, or API keys → new key → scope Inference.
- CLI:
pt key create --name inference --scope inference - API:
POST /v1/auth/api-keyswith{"name": "inference", "scopes": ["inference"]}(needs a*key or a dashboard session).
The key is shown once. See API keys.
Quickstart
curl https://api.platinum.dev/v1/inference/chat/completions \
-H "Authorization: Bearer $PLATINUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "What is 2 + 2? Answer with the number only."}]
}'import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.platinum.dev/v1/inference",
api_key=os.environ["PLATINUM_API_KEY"],
)
completion = client.chat.completions.create(
model="deepseek-v4.1-flash",
messages=[{"role": "user", "content": "What is 2 + 2? Answer with the number only."}],
)
print(completion.choices[0].message.content)
print(completion.usage) # includes `cost` in USDimport OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.platinum.dev/v1/inference",
apiKey: process.env.PLATINUM_API_KEY,
});
const completion = await client.chat.completions.create({
model: "deepseek-v4.1-flash",
messages: [{ role: "user", content: "What is 2 + 2? Answer with the number only." }],
});
console.log(completion.choices[0].message.content);The response is the OpenAI chat completion object. usage.cost is what the request cost, in USD.
Streaming
Set stream: true. The response is server-sent events, ending with data: [DONE]. Ask for stream_options.include_usage to get a final chunk with usage (including cost). Lines starting with : are keepalive comments; skip them, as every SSE client does.
stream = client.chat.completions.create(
model="glm-5.3-flash",
messages=[{"role": "user", "content": "Count from 1 to 5."}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
if chunk.usage:
print("\ncost:", chunk.usage.cost)const stream = await client.chat.completions.create({
model: "glm-5.3-flash",
messages: [{ role: "user", content: "Count from 1 to 5." }],
stream: true,
stream_options: { include_usage: true },
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
if (chunk.usage) console.log("\ncost:", (chunk.usage as any).cost);
}Tool calling
Pass tools as in the OpenAI API. Send the tool result back as a tool message.
import json
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Weather for a city",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
},
}]
messages = [{"role": "user", "content": "What is the weather in Paris? Use the tool."}]
first = client.chat.completions.create(model="deepseek-v4.1-flash", messages=messages, tools=tools)
call = first.choices[0].message.tool_calls[0]
print(call.function.name, json.loads(call.function.arguments))
messages += [first.choices[0].message, {"role": "tool", "tool_call_id": call.id, "content": '{"temp_c": 21, "sky": "sunny"}'}]
final = client.chat.completions.create(model="deepseek-v4.1-flash", messages=messages, tools=tools)
print(final.choices[0].message.content)Vision
On models whose modalities.input includes image, send an image_url content part: an https:// URL or a data: URL.
completion = client.chat.completions.create(
model="deepseek-v4.1-flash",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Describe this image in one sentence."},
{"type": "image_url", "image_url": {"url": "https://platinum.dev/brand/platinum-app-icon-dark-512.png"}},
],
}],
)Reasoning effort
Set reasoning_effort to one of the model's reasoning_efforts. Lower effort answers faster with fewer reasoning tokens. Reasoning tokens are output tokens and are billed as such; the reasoning text is in message.reasoning_content.
completion = client.chat.completions.create(
model="glm-5.3-flash",
reasoning_effort="low",
messages=[{"role": "user", "content": "Is 7 prime? Answer yes or no."}],
)A value the model does not list returns 400.
List models
GET /v1/inference/models returns the models your organization can call, in order of preference.
curl https://api.platinum.dev/v1/inference/models -H "Authorization: Bearer $PLATINUM_API_KEY"| Field | Meaning |
|---|---|
id | Pass as model |
name | Display name |
context_length | Maximum prompt + output tokens |
max_output_tokens | Maximum output tokens per request |
pricing | USD per 1M tokens: input, cached_input, output |
modalities.input | text, plus image on vision models |
capabilities | tools, reasoning |
reasoning_efforts | Accepted reasoning_effort values |
zdr | true: zero data retention |
data_collection | deny: never used for training |
Usage and billing
- Metered per token at the model's price: input, cached input and output. Cached input is the part of the prompt the model served from its prompt cache; it costs less. Repeat a long, stable prefix (system prompt, tool definitions, documents) at the start of your messages to benefit.
- Every response carries
usage.costin USD; streams carry it on the final usage chunk. - Charged from your credits, on the billing page with your other usage. Spend limits apply: an organization or member over its limit gets
402. - An aborted stream is billed for the tokens it produced.
GET /v1/inference/usage?hours=720 returns your organization's usage: totals, per model, and a series (hourly up to 48 h, daily beyond).
curl "https://api.platinum.dev/v1/inference/usage?hours=24" -H "Authorization: Bearer $PLATINUM_API_KEY"{
"hours": 24, "days": 1, "bucket": "hour",
"totals": { "requests": 12, "input_tokens": 4810, "cached_input_tokens": 2048, "output_tokens": 913, "cost_usd": 0.0026 },
"models": [{ "model": "deepseek-v4.1-flash", "requests": 12, "input_tokens": 4810, "cached_input_tokens": 2048, "output_tokens": 913, "cost_usd": 0.0026 }],
"series": [{ "start": "2026-10-22T00:00:00.000Z", "requests": 0, "input_tokens": 0, "cached_input_tokens": 0, "output_tokens": 0, "cost_usd": 0 }]
}The dashboard Inference tab shows the same usage for 24 h, 7 d and 30 d, the model list with prices, quickstart snippets and a playground.
Errors
Errors use the OpenAI shape: {"error": {"message", "type", "code"}}.
| Status | Meaning | What to do |
|---|---|---|
400 / 413 / 422 | The request is invalid (bad messages, context too long, unsupported reasoning_effort). The message explains why. | Fix the request. Do not retry unchanged. |
401 | Missing or invalid API key | Check the key |
403 inference_not_enabled | Your organization has no Inference access | Request access |
403 scope | The key lacks the inference scope | Use a key with inference |
404 | Inference is not available to your organization | Request access |
402 insufficient_credits | No credits left, or a spend limit is reached | Add credits or raise the limit |
429 | The model is busy | Retry with exponential backoff, starting at 1 s |
503 | The model is temporarily unavailable | Retry with backoff |
Inference retries internally before it returns 429 or 503, so a retry after a short wait usually succeeds.
Limits
- Context and output limits per model are in
GET /v1/inference/models. - Inference requests do not count against the per-organization write rate limit; your spend limits apply instead.