Getting started
Quickstart
The Bineric API is OpenAI-compatible. Point an OpenAI SDK at the Bineric base URL, use a Bineric API key, and call any model in the list below.
Base URL
Chat completions and the model list live under /v1, where OpenAI SDKs expect them. Account endpoints (file conversion, usage and balance) live under /api/v1. The older /api/v1/ai/chat/completions still works for chat completions.
https://api.bineric.com/v1
First request
To switch models, change model to any id from Models. Everything else stays the same.
from openai import OpenAIclient = OpenAI(base_url="https://api.bineric.com/v1",api_key="YOUR_API_KEY",)response = client.chat.completions.create(model="gpt-4o",messages=[{"role": "user", "content": "Hello!"}],)print(response.choices[0].message.content)
import OpenAI from "openai";const client = new OpenAI({baseURL: "https://api.bineric.com/v1",apiKey: "YOUR_API_KEY",});const response = await client.chat.completions.create({model: "gpt-4o",messages: [{ role: "user", content: "Hello!" }],});console.log(response.choices[0].message.content);
curl https://api.bineric.com/v1/chat/completions \-H "Content-Type: application/json" \-H "Authorization: Bearer YOUR_API_KEY" \-d '{"model": "gpt-4o","messages": [{"role": "user", "content": "Hello!"}]}'
Getting started
Authentication
Every request except the model list (GET /v1/models and GET /v1/models/{model}) needs an API key.
Send the key as a Bearer token in the Authorization header (what the OpenAI SDKs do), or in an api-key header. If both are sent, the Bearer token is used.
-H "Authorization: Bearer YOUR_API_KEY"# or-H "api-key: YOUR_API_KEY"
Get a key
- Create an account at platform.bineric.com.
- Create an API key in the dashboard and copy it.
- Buy credits under Billing. A call needs a completed purchase and a balance of at least $0.10; a saved card on its own is not enough.
Getting started
Models
Pass the id in the first column as model. Prices are in USD, at the provider's list price.
Language models · 35
| model | Provider | Context | In / out per 1M | Supports |
|---|---|---|---|---|
lynx-30bLynx · hosted in EU | Bineric AI | 262K | $3 / $15 | |
gpt-4oGPT-4o · hosted in EU | OpenAI | 128K | $2.50 / $10 | ToolsVision |
gpt-5GPT-5-Reasoning · hosted in EU | OpenAI | 128K | $1.38 / $11 | ToolsVision |
gpt-5-miniGPT-5-Mini · hosted in EU, US | OpenAI | 128K | $0.25 / $2 | ToolsVision |
gpt-5.1GPT 5.1 · hosted in EU, US | OpenAI | 400K | $1.25 / $10 | ToolsVision |
gpt-5.2GPT 5.2 · hosted in US | OpenAI | 400K | $1.75 / $14 | ToolsVision |
gpt-5.3GPT 5.3 · hosted in US | OpenAI | 128K | $1.75 / $14 | ToolsVision |
gpt-5.4GPT 5.4 · hosted in EU | OpenAI | 1M | $2.75 / $16.50 | ToolsVision |
gpt-5.5GPT-5.5 · hosted in EU | OpenAI | 1M | $5.50 / $33 | ToolsVision |
gpt-5.6-lunaGPT-5.6-Luna · hosted in EU | OpenAI | 1M | $0.22 / $1.32 | Vision |
gpt-5.6-solGPT 5.6 Sol · hosted in EU | OpenAI | 1M | $4.40 / $22 | Vision |
gpt-5.6-terraGPT 5.6 Terra · hosted in EU | OpenAI | 1M | $2.20 / $13.20 | Vision |
gpt-oss-20bgpt-oss-20b · hosted in US | OpenAI | 128K | $0.07 / $0.30 | |
gpt-oss-120bgpt-oss-120b · hosted in US | OpenAI | 128K | $0.15 / $0.60 | |
o1O1 · hosted in EU | OpenAI | 128K | $15 / $60 | ToolsVision |
o3-miniO3-Mini · hosted in EU | OpenAI | 128K | $1.10 / $4.40 | ToolsVision |
claude-haiku-4-5Claude Haiku 4.5 · hosted in EU | Anthropic | 200K | $1.10 / $5.50 | ToolsVision |
claude-haiku-5.5Claude Haiku 5.5 · hosted in EU | Anthropic | 80K | $0.11 / $0.55 | ToolsVision |
claude-opus-4-5Claude Opus 4.5 · hosted in EU | Anthropic | 200K | $5.50 / $27.50 | ToolsVision |
claude-opus-4-6Claude Opus 4.6 · hosted in EU | Anthropic | 1M | $5.50 / $27.50 | ToolsVision |
claude-opus-4-7Claude Opus 4.7 · hosted in EU | Anthropic | 1M | $5.50 / $27.50 | ToolsVision |
claude-opus-4.8Claude Opus 4.8 · hosted in EU | Anthropic | 1M | $5.50 / $27.50 | ToolsVision |
claude-opus-5Claude Opus 5 · hosted in EU | Anthropic | 1M | $5.50 / $27.50 | Vision |
claude-opus-5.5Claude Opus 5.5 · hosted in EU | Anthropic | 1M | $4.40 / $22 | Vision |
claude-sonnet-4-6Claude Sonnet 4.6 · hosted in EU | Anthropic | 1M | $3.30 / $16.50 | ToolsVision |
claude-sonnet-5Claude Sonnet 5 · hosted in EU | Anthropic | 1M | $2.20 / $11 | Vision |
claude-sonnet-5.5Claude Sonnet 5.5 · hosted in EU | Anthropic | 1M | $2.20 / $11 | Vision |
gemini-2.5-flashGemini-2.5-Flash · hosted in US | 1M | $0.30 / $2.50 | ToolsVision | |
gemini-2.5-proGemini-2.5-Pro · hosted in US | 1M | $1.25 / $10 | ToolsVision | |
gemini-3-pro-previewGemini-3-Pro · hosted in US | 1M | $1.25 / $10 | ToolsVision | |
borealis-2Borealis 2 · hosted in EU | Nasjonalbiblioteket | 16K | $2 / $10 | |
meta-llama-3.3-70bLlama 3.3-70B · hosted in US | Meta | 128K | $0.72 / $0.72 | |
mistral_large_24_02Mistral Large 24.02 · hosted in EU | Mistral AI | 32K | $4.30 / $13 | |
qwen-3-235bQwen 3 235B · hosted in EU | Qwen | 128K | $0.22 / $0.88 | |
qwen-3-coder-480bQwen 3 Coder 480B · hosted in EU | Qwen | 128K | $0.45 / $1.80 |
Image models · 3
| model | Provider | Context | Price | Supports |
|---|---|---|---|---|
gpt-image-1gpt-image-1 · hosted in EU | OpenAI | 128K | $10 / $40 | Vision |
nano-banana-2Nano Banana 2 · hosted in US | 128K | $0.50 / $61 | ||
stability-aiStable Diffusion 3.5 Large · hosted in US | Stability AI | – | $0.08 per image |
Speech, search and research models · 3
| model | Provider | Context | Price | Supports |
|---|---|---|---|---|
deep-researchDeep Research · hosted in US | Bineric AI | 128K | $0.25 per call | |
speech-to-textSpeech to Text · hosted in EU | Microsoft | 4K | $0.18 per hour | |
sonar-web-searchsonar-web-search · hosted in US | Perplexity | 128K | $1 / $1 |
Retired ids still work and are answered by their replacement: claude-sonnet-4 → claude-sonnet-4-6.
Decision models (Zuse) answer fixed-choice questions through their own endpoint; see the Zuse API. The model library compares every model side by side.
GET /v1/models
/v1/modelsEvery model the API serves, chat and decision models together, with live prices. No key is needed, and client.models.list() works. Per-token prices are per million tokens; per_request is a fixed price per call (0 when there is none), and decision models show per_decision instead. For speech-to-text, per_request is the price per hour of speech. The text-to-speech model works but is not in this list. models repeats the decision models with their name and description.
owned_by is the provider the model runs on. hosting is EU, US or Both. context_length is the model's context window in tokens.
curl https://api.bineric.com/v1/models
{"object": "list","data": [{"id": "claude-sonnet-4-6","object": "model","created": 1735689600,"owned_by": "bedrock-anthropic","name": "Claude Sonnet 4.6","description": "Claude Sonnet 4.6 delivers frontier intelligence at scale, built for coding, agents, and enterprise workflows.","category": "General","hosting": "EU","tier": "premium","context_length": 1000000,"pricing": {"input_per_million_tokens": 3.3,"output_per_million_tokens": 16.5,"per_request": 0,"currency": "USD"}}],"models": []}
GET /v1/models/{model}
/v1/models/{model}One model by id, with the same fields as its entry in the list. No key is needed, and client.models.retrieve("zuse-27b") works. An unknown id returns 404 model_not_found.
curl https://api.bineric.com/v1/models/zuse-27b
{"id": "zuse-27b","object": "model","created": 1793025018,"owned_by": "bineric","name": "Zuse 27B","description": "Typed decisions with calibrated probabilities in 115 languages, hosted in the EU.","category": "Decision","hosting": "EU","tier": "premium","context_length": 65536,"pricing": {"input_per_million_tokens": 0.75,"output_per_million_tokens": 0,"per_decision": 0,"currency": "USD"}}
Zuse API
How Zuse differs
Zuse uses the same API key, base URL and credit as the rest of the API, but not the OpenAI format. It has its own endpoint, request and answer: Jev's format, not chat.
| Chat, images, speech, research | Zuse | |
|---|---|---|
| Endpoint | POST /v1/chat/completions | POST /v1/zuse |
| You send | messages | A document (state) and your questions |
| You get back | Generated text | A choice, a yes or no, or a score, with how sure it is |
| Client | The OpenAI SDK, pointed at our base URL | Any HTTP client, or TypeSafe's Jev SDKs |
| Models | The model list | zuse-27b (Decision models) |
| List models | GET /v1/models | The same GET /v1/models: one list for both, where Zuse models have category: "Decision" |
| You pay for | Input and output tokens | Input tokens only |
| Rate limit | 500 requests a minute per IP address | 1,200 requests a minute per key |
- Endpoint
- Chat
POST /v1/chat/completionsZusePOST /v1/zuse - You send
- Chat
messagesZuseA document (state) and yourquestions - You get back
- ChatGenerated textZuseA choice, a yes or no, or a score, with how sure it is
- Client
- ChatThe OpenAI SDK, pointed at our base URLZuseAny HTTP client, or TypeSafe's Jev SDKs
- Models
- ChatThe model listZuse
zuse-27b(Decision models) - List models
- Chat
GET /v1/modelsZuseThe sameGET /v1/models: one list for both, where Zuse models havecategory: "Decision" - You pay for
- ChatInput and output tokensZuseInput tokens only
- Rate limit
- Chat500 requests a minute per IP addressZuse1,200 requests a minute per key
Sending a decision model to chat completions, or a chat model to /v1/zuse, returns 400 model_not_supported. New to Zuse? Start with the Zuse quickstart, or try it without a key in the playground.
Zuse API
Zuse quickstart
Zuse reads a document and answers your questions with a choice, a yes or no, or a score, and says how sure it is. Hosted in the EU.
/v1/zuseThe examples read your key from an environment variable, so set it first: export BINERIC_API_KEY=your_key. The Python example needs pip install requests. Save the JavaScript example as a .mjs file, because it uses top-level await.
Send the document as state and your questions as questions, keyed by ids you choose. Every question about one document is answered in a single pass, and the answers come back under the same ids.
curl https://api.bineric.com/v1/zuse \-H "Authorization: Bearer $BINERIC_API_KEY" \-H "Content-Type: application/json" \-d '{"model": "zuse-27b","state": "Hi, I was charged twice for my monthly pass last week. I want my money back as soon as possible, this is the second time it has happened!","questions": {"team": {"type": "choice","instructions": "Which team should handle it?","criteria": {"billing": "Payments and refunds","technical": "The app or website is not working","travel": "Questions about routes and departures","other": "Other"}},"refund": {"type": "noul","instructions": "Is the customer asking for a refund?"},"urgency": {"type": "score","instructions": "How urgent is it?","criteria": ["Not urgent","Normal","Urgent","Critical"]}}}'
import osimport requestsresponse = requests.post("https://api.bineric.com/v1/zuse",headers={"Authorization": f"Bearer {os.environ['BINERIC_API_KEY']}"},json={"model": "zuse-27b","state": "Hi, I was charged twice for my monthly pass ...","questions": {"team": {"type": "choice","instructions": "Which team should handle it?","criteria": {"billing": "Payments and refunds","technical": "The app or website is not working","other": "Other",},},"refund": {"type": "noul", "instructions": "Is the customer asking for a refund?"},},},timeout=30,)response.raise_for_status()answers = response.json()["answers"]if answers["team"]["confidence"] >= 0.9:print("Route to", answers["team"]["choice"]) # sure enough: act on itelse:print("Send to a person") # not sure enough
const response = await fetch("https://api.bineric.com/v1/zuse", {method: "POST",headers: {Authorization: `Bearer ${process.env.BINERIC_API_KEY}`,"Content-Type": "application/json",},body: JSON.stringify({model: "zuse-27b",state: "Hi, I was charged twice for my monthly pass ...",questions: {team: {type: "choice",instructions: "Which team should handle it?",criteria: { billing: "Payments and refunds", technical: "The app is not working", other: "Other" },},refund: { type: "noul", instructions: "Is the customer asking for a refund?" },},}),});const { answers } = await response.json();console.log(answers.team.choice, answers.team.confidence, answers.refund.noul);
Request body
| Field | Type | Description |
|---|---|---|
model | string | Optional. A decision model id; see Decision models. |
state | string, object or array | The document or record to decide about. JSON is passed as is; you do not need to flatten it. |
questions | object | At least one question, keyed by an id you choose. See Question types. |
{"model": "zuse-27b","state": "Hi, I was charged twice for my monthly pass last week. I want my money back as soon as possible, this is the second time it has happened!","questions": {"team": {"type": "choice","instructions": "Which team should handle it?","criteria": {"billing": "Payments and refunds","technical": "The app or website is not working","travel": "Questions about routes and departures","other": "Other"}},"refund": {"type": "noul","instructions": "Is the customer asking for a refund?"},"urgency": {"type": "score","instructions": "How urgent is it?","criteria": ["Not urgent","Normal","Urgent","Critical"]}}}
Zuse API
Decision models
Pass the id as model, or leave it out and the request goes to zuse-27b.
| model | Use it for | Context | Price |
|---|---|---|---|
zuse-27bZuse 27B · hosted in EU | Highest accuracy, for the decisions you automate. | 66K | $0.75 per 1M input tokens |
Zuse 9B is not on the Bineric API, so zuse-9b returns 404 here. It runs in your own cloud account: get it from AWS Marketplace. Azure is coming soon.
You pay per input token. Zuse writes no text, so there are no output tokens to pay for. Decision models are also listed by GET /v1/models, with category set to Decision. Chat models sent to /v1/zuse return 400 model_not_supported.
Zuse API
Question types
Every question takes type and instructions: the question itself, in any language.
| type | criteria | What you get |
|---|---|---|
choice | Object of option id to description, 1 to 255 options. A description can be null. | choice (the most likely option id), confidence, and probabilities for every option. |
noul | None. State the yes/no question in instructions. | noul: the probability that the answer is yes. |
score | Array of levels in order, lowest first. Use at least 2: a single level is accepted and billed, but tells you nothing. | score (the expected level, counted from 0), confidence, legend and probabilities per level. |
What confidence means
confidence is not the top probability. It is rescaled so that 0 means the model cannot tell the options apart and 1 means it is certain, however many options you give it. With two options and a top probability of 0.95, confidence is 0.90.
choice:(p_max - 1/K) / (1 - 1/K), wherep_maxis the top probability andKthe number of options. With one option it is 1.score:max(0, 1 - E|level - mode| / D), wheremodeis the most likely level andDis the average distance from the middle of the scale. It is 1 when all the probability is on one level and 0 when it is spread evenly.noulhas noconfidencefield: it is the probability of yes, so a low value is a confident no. On the same scale asconfidence, use|2 * noul - 1|.
Zuse API
Response
The answers, keyed by your question ids, with the tokens you are billed for.
The values here are an example. usage.input_tokens is what you are billed for. usage.output_tokens is reported for information: Zuse models are priced on input tokens. latency_ms is model time, without the network.
{"model": "zuse-27b","answers": {"team": {"type": "choice","choice": "billing","confidence": 0.9883,"probabilities": {"billing": 0.9912,"technical": 0.0031,"travel": 0.0009,"other": 0.0048}},"refund": {"type": "noul","noul": 0.9874},"urgency": {"type": "score","score": 2.3001,"legend": {"0": "Not urgent","1": "Normal","2": "Urgent","3": "Critical"},"probabilities": {"0": 0.0102,"1": 0.0915,"2": 0.4863,"3": 0.412},"confidence": 0.4761}},"usage": {"input_tokens": 214,"output_tokens": 209},"latency_ms": 18.6}
Response headers
| Header | Meaning |
|---|---|
x-bineric-model | The model that answered. |
x-bineric-hosting | Where it ran, for example EU. |
Zuse API
Errors and limits
Errors use the same OpenAI shape as the rest of the API, plus detail, where clients written for Jev look for the message.
{"error": {"message": "`questions` must contain at least one question.","type": "invalid_request_error","code": "invalid_request"},"detail": "`questions` must contain at least one question."}
| Status | code | Meaning |
|---|---|---|
| 400 | invalid_request | The body is a JSON array, or questions is missing, empty or not an object. |
| 400 | – | The body is neither a JSON object nor an array, for example invalid JSON or a bare string. The parser refuses it before the API key is checked, in its own shape: {"success":false,"error":"...","message":"...","requestId":"..."}, without code or detail. A zstd-compressed body gets invalid_request in these cases instead. |
| 400 | model_not_supported | A chat model was sent. Use a decision model. |
| 401 | missing_api_key, invalid_api_key | No key, or the key is wrong or disabled. |
| 402 | insufficient_quota | Not enough credit. Buy credits. |
| 403 | payment_required | This model needs credit on the account. Buy credits. |
| 404 | model_not_found | Unknown model id. The message lists the decision models. |
| 400, 413, 422 | – | The decision server rejected the request, for example a malformed question or a document that is too long. Its body comes back as it was sent. |
| 429 | rate_limit_exceeded | Too many requests. Slow down and retry. |
| 502, 503, 504 | upstream_error, upstream_unavailable, server_busy, model_not_available, upstream_timeout | Retry with backoff. Failed requests are not billed. |
Limits
- 1,200 requests a minute per API key, and 2,400 a minute per IP address. These are separate from the chat limits.
- The document and questions must fit the model's context length,
context_lengthinGET /v1/models. - Up to 255 options per choice question.
Zuse API
Using the probabilities
Act on the sure answers and send the rest to a person.
- Set a threshold per question and send anything below it to a person. On our European suite, 77.2% of the 27B model's answers clear the threshold that keeps errors under 5%.
- Add a "none of these" option to choice questions whose answer may not be in the list. The model is trained to pick it when nothing fits.
- Routing, reading comprehension and entailment automate well. Keep legal interpretation, grammar judgements and questions that need world knowledge review-first.
Zuse API
Moving from Jev
Code written for Jev, including TypeSafe's Python and JavaScript SDKs, runs on Zuse with a new base URL and key.
- Install TypeSafe's Python SDK with
pip install typesafe-sdk. For the JavaScript SDK, see TypeSafe's own documentation. POST /v1/systemone, Jev's path, works the same asPOST /v1/zuse.- Jev model names, such as
jev-latest, are answered by Zuse 27B. - Bodies compressed with zstd (
Content-Encoding: zstd), as the SDKs send them, are accepted. - Choice, yes/no and score questions are supported. Image (box) questions are not.
How the two compare: Zuse, the European Jev alternative.
- TYPESAFE_BASE_URL=https://api.typesafe.ai+ TYPESAFE_BASE_URL=https://api.bineric.com- TYPESAFE_API_KEY=your-typesafe-key+ TYPESAFE_API_KEY=your-bineric-keyTYPESAFE_DEFAULT_MODEL=jev-latest
Chat
Chat completions
Generate a response from a list of messages, in the OpenAI format.
/v1/chat/completionsRequest body
| Field | Type | Description |
|---|---|---|
model | string | Required. A model id from Models. |
messages | array | Required. The conversation so far; see Messages below. |
stream | boolean | Stream the response as server-sent events. Default false. See Streaming. |
stream_options | object | {"include_usage": true} adds a final chunk with token usage. |
max_completion_tokens | integer | Maximum tokens in the response, never above the model's own output limit. max_tokens also works. |
temperature | number | 0 to 2. Out of range returns 400. |
top_p | number | 0 to 1. Out of range returns 400. |
reasoning_effort | string | low, medium or high; minimal, none, xhigh and max are also accepted. Used by models that reason; other values are ignored. |
tools | array | Functions the model may call. See Tool calling. |
tool_choice | string or object | auto, none, required, or {"type":"function","function":{"name":"..."}}. |
parallel_tool_calls | boolean | Sent to OpenAI and Qwen models; others ignore it. |
GPT-5 and the o-series ignore temperature and top_p, and so do Claude Opus 4.7 and later, Claude Sonnet 5, and any Claude request with reasoning on. Other Claude models use one of the two: temperature if both are sent. reasoning_effort: "none" picks the lowest level the model has, which on older Claude models and Gemini 2.5 Flash turns reasoning off.
Older integrations can still send stream, temperature, top_p and reasoning_effort inside an options object, and max_response_length at the top level. Top-level fields win when both are sent.
Messages
| Field | Type | Description |
|---|---|---|
role | string | system, user, assistant or tool. |
content | string or array | The text, or an array of content parts (below). Can be null on an assistant message that only has tool_calls. |
tool_calls | array | Assistant messages only: the tool calls the model made, sent back unchanged. |
tool_call_id | string | Tool messages only: the id of the call this result answers. |
attachments | array | Bineric's own format: base64 data URLs. Content parts do the same thing. |
Content parts
| Type | Description |
|---|---|
text | {"type": "text", "text": "..."} |
image_url | image_url.url is a base64 data URL or an http(s) URL. Images fetched from a URL can be up to 20 MB. |
file | file.file_data is a base64 data URL; file.filename is optional. file_id is not supported. |
input_audio | input_audio.data (base64) and input_audio.format (wav or mp3). |
Images, files and audio are read from the last user message only; attach them to the message you are asking about. A malformed part returns 400 with param pointing at it, for example messages[0].content[1].
{"model": "gpt-4o","messages": [{"role": "user","content": [{ "type": "text", "text": "What is in this image, and what does the report say about it?" },{ "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } },{ "type": "file", "file": { "filename": "report.pdf", "file_data": "data:application/pdf;base64,JVBERi0..." } }]}]}
Files
| Kind | Formats | Notes |
|---|---|---|
| Images | JPEG, PNG, WebP, GIF | Models that support vision. Each model has its own size limit (Claude: 3.75 MB); a larger image returns 400. Images are scaled to fit 1024×1024. |
| Documents | PDF, DOCX, DOC, XLSX, XLS, TXT, CSV, MD, JSON, HTML | The text is extracted and sent to the model. |
| Audio | WAV, MP3 | Gemini models and speech-to-text only. Other models return 400. |
Response
model in the response is the model's display name, such as GPT-4o, not the id you sent. finish_reason is stop, length (cut off by max_completion_tokens or the model's limit) or tool_calls; image models can also return content_filter.
When a model is retired, requests for its id are answered by its replacement, and model shows the replacement's name.
{"id": "chatcmpl-1768817914002-yq8xw50tv2g","object": "chat.completion","created": 1768817914,"model": "GPT-4o","choices": [{"index": 0,"message": { "role": "assistant", "content": "Hello! How can I help?" },"finish_reason": "stop"}],"usage": { "prompt_tokens": 9, "completion_tokens": 7, "total_tokens": 16 }}
Chat
Streaming
Set stream: true to receive the response as server-sent events while it is written.
stream = client.chat.completions.create(model="gpt-4o",messages=[{"role": "user", "content": "Write a haiku about the sea."}],stream=True,)for chunk in stream:if chunk.choices and chunk.choices[0].delta.content:print(chunk.choices[0].delta.content, end="")
const stream = await client.chat.completions.create({model: "gpt-4o",messages: [{ role: "user", content: "Write a haiku about the sea." }],stream: true,});for await (const chunk of stream) {process.stdout.write(chunk.choices[0]?.delta?.content ?? "");}
curl -N https://api.bineric.com/v1/chat/completions \-H "Content-Type: application/json" \-H "Authorization: Bearer YOUR_API_KEY" \-d '{"model": "gpt-4o","messages": [{"role": "user", "content": "Write a haiku about the sea."}],"stream": true}'
Events
- The first chunk carries the role, then come content chunks, then an empty
deltawith thefinish_reason. - The usage chunk (empty
choices) is only sent when the request sets"stream_options": {"include_usage": true}. - Reasoning models also send
delta.reasoning_contentchunks with their reasoning. - Search models add
_bineric.citationsto the last chunk. - If an error happens after the stream has started, it arrives as
data: {"error": {...}}and the stream closes without[DONE].
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1768818339,"model":"GPT-4o","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1768818339,"model":"GPT-4o","choices":[{"index":0,"delta":{"content":"Waves"},"finish_reason":null}]}data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1768818339,"model":"GPT-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1768818339,"model":"GPT-4o","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":17,"total_tokens":31}}data: [DONE]
Tool calls
Tool calls stream as delta.tool_calls. The first piece of each call carries its id and function name; join the arguments strings by index.
Image, speech and deep research models do not send OpenAI chunks; see their sections.
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1768818339,"model":"GPT-4o","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_abc123","type":"function","function":{"name":"get_weather","arguments":""}}]},"finish_reason":null}]}data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1768818339,"model":"GPT-4o","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":\"Oslo\"}"}}]},"finish_reason":null}]}data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1768818339,"model":"GPT-4o","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}
Chat
Tool calling
Give the model functions it can call. The model returns the call; your code runs it and sends the result back.
The Supports column in Models marks the models that support tool calling (Tools). Image, audio, search and research models return 400 with code: "tools_not_supported" and param: "tools", both when tools is sent and when the messages carry earlier tool calls or results.
1. Send tools
Describe each function with a name, a description and a JSON Schema for its parameters. tool_choice: "auto" lets the model decide; "required" makes it call one.
{"model": "gpt-4o","messages": [{ "role": "user", "content": "What's the weather in Oslo?" }],"tools": [{"type": "function","function": {"name": "get_weather","description": "Get the current weather for a city","parameters": {"type": "object","properties": { "city": { "type": "string" } },"required": ["city"]}}}],"tool_choice": "auto"}
2. The model calls a tool
finish_reason is tool_calls and message.tool_calls holds the calls. arguments is a JSON string: parse it, then run your function.
{"id": "chatcmpl-1768817914002-yq8xw50tv2g","object": "chat.completion","created": 1768817914,"model": "GPT-4o","choices": [{"index": 0,"message": {"role": "assistant","content": null,"tool_calls": [{"id": "call_abc123","type": "function","function": { "name": "get_weather", "arguments": "{\"city\":\"Oslo\"}" }}]},"finish_reason": "tool_calls"}],"usage": { "prompt_tokens": 62, "completion_tokens": 18, "total_tokens": 80 }}
3. Send the result back
Add the assistant message unchanged, then a tool message with the matching tool_call_id. Keep sending tools. The model then answers in text with finish_reason: "stop".
{"model": "gpt-4o","messages": [{ "role": "user", "content": "What's the weather in Oslo?" },{"role": "assistant","content": null,"tool_calls": [{"id": "call_abc123","type": "function","function": { "name": "get_weather", "arguments": "{\"city\":\"Oslo\"}" }}]},{ "role": "tool", "tool_call_id": "call_abc123", "content": "{\"temp_c\": 12, \"sky\": \"cloudy\"}" }],"tools": [ ... ]}
Chat
Errors
Errors use the OpenAI shape, so SDKs raise their usual exceptions.
{"error": {"message": "temperature must be between 0 and 2","type": "invalid_request_error","param": "temperature","code": "invalid_request"}}
| Status | code | Meaning |
|---|---|---|
| 400 | invalid_request | A field is wrong; param names it (for example temperature, tools[0].function.name or messages[0].content[1]). |
| 400 | model_not_found | Unknown model id in a request. Use an id from GET /v1/models. |
| 400 | model_not_supported | A decision model was sent to chat completions. Use POST /v1/zuse. |
| 400 | tools_not_supported | The model does not support tools. |
| 400 | null | An attachment is too large, of an unsupported type, or could not be processed. |
| 401 | missing_api_key, invalid_api_key | No key, or the key is wrong or disabled. |
| 402 | insufficient_quota | Balance below $0.10. Buy credits. |
| 403 | payment_required | No completed purchase on the account yet. Buy credits. |
| 404 | model_not_found | GET /v1/models/{model} with an id that is not in the list. |
| 404 | unknown_endpoint | No such endpoint. |
| 429 | rate_limit_exceeded | Too many requests from your IP address. Wait for Retry-After seconds. |
| 429 | concurrent_request_limit | Too many requests running at once on your account. |
| 500 | internal_error | Something failed on our side. Retry. |
| 502 | image_generation_failed | The image model failed. |
| 503 | model_not_configured | The model is listed but not available right now. Try another model. |
Errors from the model provider, such as its own rate limits or content policy, are passed through with the provider's status and code. Retry 429 and 5xx responses with exponential backoff.
Chat
Rate limits
- 500 requests a minute per IP address across
/v1and/api/v1. Over the limit you get 429 with aRetry-Afterheader and the standardRateLimit-*headers. - Up to 50 requests running at once per account; more return 429
concurrent_request_limit. - Each model's context window and output limit apply; see Models.
Images, speech and research
Image generation
Use an image model with chat completions. The last user message is the prompt.
/v1/chat/completionsgpt-image-1also readssize,quality,output_formatandnat the top level of the body.- Attach images to the last user message to edit them instead;
_bineric.operationis thenediting. - Image models do not stream. With
stream: trueyou still get one JSON response, not events, so call them without streaming.
{"model": "gpt-image-1","messages": [{ "role": "user", "content": "A lighthouse on a rocky coast at dawn, watercolour" }],"size": "1024x1024","quality": "high"}
Response
message.content holds each image as a Markdown image with a data URL. The raw data URLs are also in _bineric.images.
When Stable Diffusion or a Gemini image model blocks a prompt, you get 200 with a short text reply, finish_reason: "content_filter" and an empty _bineric.images. gpt-image-1 returns the provider's content policy error instead.
{"id": "chatcmpl-1768817914002-yq8xw50tv2g","object": "chat.completion","created": 1768817914,"model": "gpt-image-1","choices": [{"index": 0,"message": { "role": "assistant", "content": "" },"finish_reason": "stop"}],"usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 },"_bineric": {"images": ["data:image/png;base64,iVBORw0KGgo..."],"image_count": 1,"operation": "generation","total_cost": 0.04}}
Images, speech and research
Text to speech
Turn text into speech with the text-to-speech model. The last user message is the text.
/v1/chat/completions| Field | Default | Description |
|---|---|---|
voice | nb-NO-FinnNeural | An Azure neural voice name, such as nb-NO-PernilleNeural, en-US-JennyNeural or sv-SE-SofieNeural. The voice decides the language you hear. |
language | nb-NO | The locale of the text, such as en-US. Also accepted as locale. It does not choose a voice, so set voice too. |
rate | medium | Speaking rate, as SSML prosody: slow, medium, fast or a percentage like +10%. |
pitch | medium | Pitch, such as low, high or +5%. |
volume | loud | Volume, such as soft, medium or +20%. |
Send these at the top level of the body. Norwegian voices: Finn (male), Iselin and Pernille (female). Voices for English, Swedish, Danish and other languages follow the same pattern, <locale>-<Name>Neural.
{"model": "text-to-speech","messages": [{ "role": "user", "content": "Hei, velkommen til Bineric!" }],"voice": "nb-NO-PernilleNeural","language": "nb-NO","rate": "medium"}
Response
The speech is MP3 (16 kHz, 32 kbps, mono), base64-encoded in message.audio.data. It is priced at $16 per million characters.
Long text is split into chunks of up to 500 characters at sentence ends. If some chunks fail, you still get 200 with the audio that worked; compare processedChunks with totalChunks.
{"id": "chatcmpl-1768817914002-yq8xw50tv2g","object": "chat.completion","created": 1768817914,"model": "Text to Speech","choices": [{"index": 0,"message": {"role": "assistant","content": null,"audio": { "data": "<base64 mp3>", "format": "mp3" }},"finish_reason": "stop"}],"usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 },"_bineric": {"metadata": {"language": "nb-NO","voice": "nb-NO-PernilleNeural","textLength": 27,"format": "mp3","quality": "16khz/32kbps","processedChunks": 1,"totalChunks": 1,"settings": { "rate": "medium", "pitch": "medium", "volume": "loud" }},"characters": 27,"total_cost": 0.00043}}
import base64data = response.json()audio = base64.b64decode(data["choices"][0]["message"]["audio"]["data"])with open("speech.mp3", "wb") as f:f.write(audio)
Images, speech and research
Speech to text
Transcribe audio with speaker labels using the speech-to-text model.
/v1/chat/completionsSend one audio file in the last user message, as an input_audio content part (WAV or MP3), a file part, or a data URL in attachments. Only the first file is transcribed.
language (or locale, which wins if both are sent) is the main locale, default en-US. The transcriber also detects en-US, es-ES, de-DE, fr-FR, it-IT, pt-BR, nl-NL, nb-NO, fi-FI and sv-SE.
{"model": "speech-to-text","messages": [{"role": "user","content": [{ "type": "text", "text": "Transcribe this" },{ "type": "input_audio", "input_audio": { "data": "<base64 audio>", "format": "mp3" } }]}],"language": "nb-NO"}
Response
_bineric.phraseshas one entry per phrase, with ISO 8601startTimeandduration.- Priced at $0.18 per hour of recognised speech.
- Transcription runs as a batch job and does not stream. After 5 minutes it stops with 500
Transcription timed out after 5 minutes; split long recordings.
{"id": "chatcmpl-1768817914002-yq8xw50tv2g","object": "chat.completion","created": 1768817914,"model": "Speech to Text","choices": [{"index": 0,"message": {"role": "assistant","content": "Speaker 1: Hello and welcome.\nSpeaker 2: Thanks for having me."},"finish_reason": "stop"}],"usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 },"_bineric": {"phrases": [{ "speaker": 1, "text": "Hello and welcome.", "startTime": "PT0.5S", "duration": "PT1.8S" },{ "speaker": 2, "text": "Thanks for having me.", "startTime": "PT2.4S", "duration": "PT1.6S" }],"total_cost": 0.0002}}
Images, speech and research
Deep research
Research a question on the web and get an answer with sources, using the deep-research model. $0.25 per call.
/v1/chat/completions{"model": "deep-research","messages": [{ "role": "user", "content": "What changed in EU AI regulation this year?" }],"options": { "maxDepth": 5, "timeLimit": 180, "maxUrls": 5 }}
| Option | Default | Description |
|---|---|---|
options.maxDepth | 5 | How many rounds of searching and reading. |
options.timeLimit | 180 | Time limit in seconds. |
options.maxUrls | 5 | Maximum number of pages to read. |
stream | false | Send progress events while it works (top level or in options). |
The research options are only read inside options. Each account can make 50 deep research calls a calendar month; unused calls do not carry over.
Response
Token counts in usage are estimates; you pay the fixed price per call.
{"id": "chatcmpl-1768817914002-yq8xw50tv2g","object": "chat.completion","created": 1768817914,"model": "Deep Research","choices": [{"index": 0,"message": { "role": "assistant", "content": "Based on my research..." },"finish_reason": "stop"}],"usage": { "prompt_tokens": 320, "completion_tokens": 540, "total_tokens": 860 },"_bineric": {"request_id": "req_abc123","citations": [{ "url": "https://example.com/article", "title": "Article title", "description": "Relevant excerpt..." }],"activities": [{ "type": "search", "status": "complete", "message": "Searching...", "timestamp": "2026-10-26T10:00:00Z", "depth": 1 }],"deep_research_info": { "usage_count": 5, "usage_limit": 50, "usage_left": 45 },"total_cost": 0.25,"response_time_ms": 12000}}
Streaming
With stream: true you get named events, not OpenAI chunks: info when research starts, activity for each step, and complete with the answer in response. There is no [DONE].
event: infodata: {"request_id":"req_abc123","model":"Deep Research","message":"Deep research started"}event: activitydata: {"type":"search","message":"Searching...","request_id":"req_abc123"}event: completedata: {"success":true,"request_id":"req_abc123","model":"Deep Research","response":"Based on my research...","citations":[...],"activities":[...],"deep_research_info":{"usage_count":5,"usage_limit":50,"usage_left":45},"usage":{"prompt_tokens":320,"completion_tokens":540,"total_tokens":860,"total_cost":0.25,"response_time_ms":12000}}event: errordata: {"success":false,"request_id":"req_abc123","error":{"type":"ProcessingError","message":"...","code":500}}
Errors
| Status | Meaning |
|---|---|
403 not_eligible | This month's 50 calls are used up, or the balance is too low. |
| 429 | Too many requests at once, or the research service's own limit. |
| 500, 502 | The research failed. |
| 503 | Deep research is temporarily unavailable. |
When streaming, everything except the 403 arrives as an error event.
Files
Convert a file to base64
Upload a file and get it back as a data URL you can attach to a chat message.
/api/v1/media/base64Multipart form data with one file in a field named file, up to 200 MB. Free; no credit is used.
curl -X POST https://api.bineric.com/api/v1/media/base64 \-H "Authorization: Bearer YOUR_API_KEY" \-F "file=@report.pdf"
import requestswith open("report.pdf", "rb") as f:response = requests.post("https://api.bineric.com/api/v1/media/base64",headers={"Authorization": "Bearer YOUR_API_KEY"},files={"file": f},)data_url = response.json()["data"]["base64String"]
const form = new FormData();form.append("file", fileInput.files[0]);const response = await fetch("https://api.bineric.com/api/v1/media/base64", {method: "POST",headers: { Authorization: "Bearer YOUR_API_KEY" },body: form,});const { data } = await response.json();console.log(data.base64String);
Response
{"success": true,"request_id": "req_abc123","data": {"base64String": "data:application/pdf;base64,JVBERi0xLjcK...","originalName": "report.pdf","fileInfo": { "mimeType": "application/pdf" },"size": { "original": 102400, "base64": 136563 }}}
The MIME type is read from the file's bytes, so binary formats (images, audio, PDF, Office files) are recognised. Plain-text formats such as TXT, CSV, MD, JSON and HTML come back as application/octet-stream.
| Status | code | Meaning |
|---|---|---|
| 400 | invalid_request | No file, or larger than 200 MB. |
| 401 | missing_api_key, invalid_api_key | No key, or the key is wrong. |
| 500 | internal_error | The file could not be converted. A form field not named file also ends here. |
Files
Decode base64
Send a base64 data URL and get the original file back.
/api/v1/decode-base64The response is the file itself. Content-Type is detected from the decoded bytes, and Content-Disposition is inline; filename="file.<ext>". Errors: 400 invalid_request with param: "data" when data is missing, not a string or not valid base64, and 500 internal_error when decoding fails.
curl -X POST https://api.bineric.com/api/v1/decode-base64 \-H "Authorization: Bearer YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="}' \--output pixel.png
import requestsresponse = requests.post("https://api.bineric.com/api/v1/decode-base64",headers={"Authorization": "Bearer YOUR_API_KEY"},json={"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="},)with open("pixel.png", "wb") as f:f.write(response.content)
Account
Usage
Your API usage and cost, per model.
/api/v1/monitoring/usageFilter with startDate and endDate (YYYY-MM-DD). Send both: with only one, the result covers all time. Without dates, period is {"start": "all time", "end": "current"}.
model is the model's display name. recent_requests holds the last 5 paid requests. All numbers are JSON numbers.
curl "https://api.bineric.com/api/v1/monitoring/usage?startDate=2026-10-01&endDate=2026-10-31" \-H "Authorization: Bearer YOUR_API_KEY"
{"success": true,"data": {"by_model": [{"model": "GPT-4o","usage": { "input_tokens": 50000, "output_tokens": 75000, "total_cost": 0.88, "request_count": 150 }}],"total_usage": { "total_cost": 10.25, "total_tokens": 500000, "total_requests": 500 },"recent_requests": [{ "model": "GPT-4o", "cost": 0.0213, "tokens": 1840, "timestamp": "2026-10-31T14:30:00.000Z" }],"period": { "start": "2026-10-01", "end": "2026-10-31" }}}
Account
Daily usage
Today's usage and whether your credit is active.
/api/v1/monitoring/daily-usage- "Today" is the server's calendar day.
requestscounts paid requests; the token and cost totals include every API request today.last_requestisnullwhen there are no paid requests yet.expires_atis only present whileactiveis true.
curl https://api.bineric.com/api/v1/monitoring/daily-usage \-H "Authorization: Bearer YOUR_API_KEY"
{"success": true,"data": {"payment_status": { "active": true, "expires_at": "2026-11-20T09:12:00.000Z" },"today_usage": { "requests": 42, "input_tokens": 15000, "output_tokens": 22000, "total_cost": 0.85 },"last_request": { "time": "2026-10-26T14:30:00.000Z", "model": "Claude Sonnet 4.6", "cost": 0.015 }}}
Account
Balance
Your credit balance and lifetime token usage.
/api/v1/monitoring/balancebalance is in USD. payment_status.active means a completed purchase in the last 30 days, and expires (present only while active) is 30 days after it. A new account has balance: 0 and lastUpdated: null.
curl https://api.bineric.com/api/v1/monitoring/balance \-H "Authorization: Bearer YOUR_API_KEY"
{"success": true,"data": {"balance": 25.5,"inputTokens": 500000,"outputTokens": 750000,"lastUpdated": "2026-10-26T14:30:00.000Z","payment_status": { "active": true, "expires": "2026-11-20T09:12:00.000Z" },"usage": {"daily": { "requests": 42, "tokens": { "input": 15000, "output": 22000 }, "cost": 0.85 },"total": { "input_tokens": 500000, "output_tokens": 750000 }}}}