API docs
An OpenAI-compatible chat completions API for open-weight models. If your code already talks to the OpenAI API, change the base URL, the key and the model id.
Quickstart
curl https://inference.negbeta.com/v1/chat/completions \
-H "Authorization: Bearer $NEGBETA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen/qwen3.8-27b",
"messages": [{"role": "user", "content": "Write a haiku about tokens."}]
}'
With the OpenAI Python SDK:
from openai import OpenAI
client = OpenAI(base_url="https://inference.negbeta.com/v1", api_key=NEGBETA_API_KEY)
resp = client.chat.completions.create(
model="qwen/qwen3.8-27b",
messages=[{"role": "user", "content": "Write a haiku about tokens."}],
)
print(resp.choices[0].message.content)
Authentication
Send your API key as a bearer token on every request: Authorization: Bearer sk-negbeta-.... Each platform or customer gets its own key, which we can revoke or rotate on request. To get a key, email info@negbeta.com. A missing or wrong key gets 401 with code invalid_api_key.
Keep keys server-side. Never put them in browser code or a public repository.
Models
| Model id | Details |
|---|---|
qwen/qwen3.8-27b |
Qwen3.8 27B (Qwen/Qwen3.8-27B, Apache-2.0), NVFP4 weights. Text and image input, text output. Context 262,144 tokens, output up to 65,536 tokens. |
deepseek/deepseek-v4.1-flash-1017 |
DeepSeek V4.1 Flash 1017 (deepseek-ai/DeepSeek-V4.1-Flash-1017), FP8. Text and image input, text output. Context 1,048,576 tokens, output up to 393,216 tokens. |
Model ids are exact and case-sensitive. If your platform needs its own name for a model, we can add it for your key on request. The response's model field repeats the id you sent. A missing model gets 400 (missing_required_parameter); any other id gets 404 (model_not_found). More models are coming; we give at least 14 days' notice before a model is retired, with its replacement.
Chat completions
POST /v1/chat/completions, with OpenAI's request and response shapes. This is the only inference endpoint: there is no Anthropic Messages API, no Responses API and no embeddings endpoint.
Messages
- Roles:
system,user,assistantandtool. - System messages only at the start. A system message after a user, assistant or tool message gets
400with the messageSystem message must be at the beginning.Put all system instructions first. - Images:
image_urlcontent parts, as an https URL or a base64 data URI (PNG, JPEG or WebP, at most 20 MB each), up to 16 images per request. No audio, video or files. - Request bodies up to 64 MB.
Parameters
| Parameter | Values |
|---|---|
max_tokens, max_completion_tokens | 1 to 65,536. Prompt plus output must fit in 262,144 tokens. |
temperature | 0 to 2 |
top_p | 0 to 1 |
top_k, min_p | Accepted (top_k -1 means off) |
presence_penalty, frequency_penalty | -2 to 2 |
repetition_penalty | 0 to 2 |
stop | String or array of strings |
logprobs, top_logprobs | Boolean; 0 to 20 |
n | 1 only |
seed | Accepted, but output is not guaranteed to be reproducible |
tools, tool_choice, parallel_tool_calls, response_format | See Tools and structured output |
reasoning, reasoning_effort | See Reasoning |
Out-of-range values get 400 with param naming the field. Responses include usage with prompt_tokens, completion_tokens, total_tokens and prompt_tokens_details.cached_tokens.
Long requests: if a non-streamed request is still running after 15 seconds, the 200 headers are sent early and a newline follows every 15 seconds until the JSON body (leading whitespace is valid JSON, and OpenAI SDKs read it normally). If such a request then fails, the error object comes in the body with status 200. Use streaming for long generations. If the client disconnects, generation stops.
Streaming
Set "stream": true to receive server-sent events (text/event-stream) in OpenAI's chunk format. Every stream ends with a usage chunk (empty choices, full usage), whether or not you set stream_options.include_usage; a stream that completes then sends data: [DONE].
data: {"id":"...","object":"chat.completion.chunk","model":"qwen/qwen3.8-27b","choices":[{"index":0,"delta":{"content":"OK."},"finish_reason":null}]}
data: {"id":"...","object":"chat.completion.chunk","model":"qwen/qwen3.8-27b","choices":[],"usage":{"prompt_tokens":15,"completion_tokens":3,"total_tokens":18,"prompt_tokens_details":{"cached_tokens":0}}}
data: [DONE]
- When nothing has been sent for 15 seconds (for example while a long prompt is processed), the stream carries an SSE comment line
: keep-alive. SSE clients ignore it. - An error before the first token comes back as a normal HTTP error status. An error after the stream has started arrives as one event
data: {"error": {...}}(same shape as below), preceded by a usage chunk for the tokens already generated; then the stream ends.
Tools and structured output
- Tools:
tools(function tools),tool_choice(auto,none,requiredor a named function) andparallel_tool_calls. Tool calls come back inmessage.tool_calls(streamed as indexeddelta.tool_calls) withfinish_reason: "tool_calls". Send results back asrole: "tool"messages with the matchingtool_call_id. Tool-call arguments are generated under a grammar from your JSON schema, so they parse as valid JSON. - Structured output:
response_format{"type": "json_object"}or{"type": "json_schema", "json_schema": {"name": ..., "schema": {...}}}. Output is constrained to the schema. A schema that cannot be compiled gets400.
Reasoning
Both models think before they answer, by default (DeepSeek V4.1 Flash 1017 at effort high). The reasoning text is returned separately from the answer, in message.reasoning_content (streamed as delta.reasoning_content); message.content holds only the answer. usage.reasoning_tokens counts the reasoning tokens, which are included in completion_tokens.
reasoning_effort:none(no thinking),minimal,low,medium,high,xhighormax.- Or OpenRouter's form:
"reasoning": {"effort": "low"}, or"reasoning": {"enabled": false}to turn thinking off.
Reasoning tokens count against max_tokens. A small max_tokens with thinking on can end with finish_reason: "length" before any answer; turn thinking off for short, simple replies.
Prompt caching
Automatic for every request: nothing to opt into and no cache-write charge. When a request starts with the same tokens as a recent one (for example the same system prompt, tools and conversation so far), that prefix is not processed again. Cached tokens are reported in usage.prompt_tokens_details.cached_tokens and billed at the cached-input price. There is no fixed lifetime: the least recently used prefixes are evicted first. Keep the stable parts of your prompt at the start and unchanged between requests to get the most hits.
The cache holds the model's internal state for recent prefixes, in memory on the serving machine only. It is not a store of prompts, is never written to logs, and is cleared on restart.
Errors
Errors use OpenAI's shape, and every response, successful or not, carries an X-Request-Id header. Quote it when you contact us.
{"error": {"message": "The model `gpt-4o` does not exist or you do not have access to it.",
"type": "invalid_request_error", "param": null, "code": "model_not_found"}}
| Status | type / code | Meaning and what to do |
|---|---|---|
| 400 | invalid_request_error; context_length_exceeded, missing_required_parameter or null | The request is invalid: a parameter out of range, a system message after the start, a malformed image, prompt plus max_tokens over the model's context. Fix the request; do not retry it unchanged. |
| 401 | authentication_error / invalid_api_key | Missing or wrong API key. |
| 404 | invalid_request_error / model_not_found; not_found_error | Unknown model id, or an unknown URL. |
| 405, 413 | invalid_request_error | Wrong HTTP method; body over 64 MB. |
| 429, or 503 for platforms that ask for it | rate_limit_exceeded, or the platform's agreed overload type | Capacity is full right now. Retry after the number of seconds in Retry-After. |
| 429 | insufficient_quota | The key's usage budget is used up (test keys). No Retry-After: retrying does not help; contact us. |
| 500, 503 | server_error | A server-side error, or the service is briefly unavailable. Retry with backoff (honour Retry-After when present). |
| 504 | timeout | The request timed out before any output. Retry. |
Rate limits and retries
We do not queue requests. When the capacity for your traffic is full, the request is refused at once with 429 (rate_limit_exceeded), or 503 for platforms that ask for it, with a Retry-After header in seconds, usually a few seconds. Wait that long and retry; add jitter if you retry many requests at once. Nothing was generated or billed for a refused request.
Limits depend on the capacity agreed for your key, not on a fixed public number of requests per minute. We add GPUs as traffic grows: if you see sustained 429s, or expect a large increase, email us and we will add capacity.
Data handling
- Zero data retention. Prompts, images and outputs exist only in server memory while the request runs. They are never written to disk or logs, never reviewed by people and never used for training, by us or anyone else.
- Prompt cache: model-internal state of recent prefixes, in memory on the serving machine only, evicted as new requests arrive and cleared on restart (see Prompt caching).
- What we keep: operational metadata without content (request id, timings, token counts, sampling settings, counts of messages, tools and images, sanitised error messages, a hashed key) for 180 days; client IP address and user agent for 30 days.
- Where: we serve from EU and US capacity. Each key is served only from the region agreed for it, so EU-only keys stay in the EU. We can add capacity in other regions on request.
Details: data policy, privacy policy and terms of service.
Contact
- Email: info@negbeta.com for API keys, capacity, billing, incidents and anything else. Include the
X-Request-Idof a failing request. - Discord: https://discord.gg/JYRNtAYuFA (real-time chat with us; #support)