Developer API
One key for text models, images, video and sound. The chat endpoint is OpenAI-compatible, media generation runs through a single endpoint with a per-model parameter schema in the catalog. You pay from your NetRoom balance for actual usage only.
Quickstart
Create an API key
In your NetRoom account, API tab. The nr-... key is shown once at creation — save it right away.
Top up your balance
Requests are paid from your NetRoom balance as you go — no subscription required.
Make your first request
Copy an example below, drop in your key and a model id from the catalog.
curl https://net-room.com/api/v1/chat/completions \
-H "Authorization: Bearer nr-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [{"role": "user", "content": "Hello! What can you do?"}]
}'
from openai import OpenAI
client = OpenAI(
base_url="https://net-room.com/api/v1",
api_key="nr-YOUR_KEY",
)
response = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Hello! What can you do?"}],
)
print(response.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://net-room.com/api/v1",
apiKey: "nr-YOUR_KEY",
});
const response = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
messages: [{ role: "user", content: "Hello! What can you do?" }],
});
console.log(response.choices[0].message.content);
Model ids in the examples are placeholders — take real ids from the GET /api/v1/models catalog.
Authentication
Every request except GET /api/v1/models must carry the header:
Authorization: Bearer nr-YOUR_KEY
- Keys are created and revoked in your NetRoom account. A revoked key stops working within a minute.
- The raw key is stored only on your side: NetRoom keeps its sha256 hash. Lost a key — create a new one and revoke the old one.
- Never publish a key in client-side code, repositories or logs. For browser apps, proxy requests through your own backend.
- A wrong or revoked key returns 401 with code invalid_api_key.
Model catalog and pricing
/api/v1/models
No auth required. Returns a single list of all available models with final NetRoom prices — the only source of pricing, always up to date. The catalog is cached server-side for about 5 minutes.
curl https://net-room.com/api/v1/models
{
"object": "list",
"data": [
{
"id": "openai/gpt-4o-mini",
"object": "model",
"type": "text",
"name": "GPT-4o mini",
"pricing": {"currency": "RUB", "input_per_1k_tokens": 0.5, "output_per_1k_tokens": 1.5}
},
{
"id": "image-model-id",
"object": "model",
"type": "image",
"name": "...",
"pricing": {"currency": "RUB", "per_image": 12.0},
"input_schema": {"type": "object", "required": ["prompt"], "properties": {"...": "..."}}
}
]
}
| Field | Description |
|---|---|
type | text | image | video | sound. Text models are called via /api/v1/chat/completions, everything else via /api/v1/generations. |
pricing | Final NetRoom prices: text models — per 1K input and output tokens; images — per_image; video — per_second; sound — per_1000_chars, per_1000_bytes (UTF-8 bytes of the text: 1 per Latin character, 2 per Cyrillic), per_minute, per_generation or per_second depending on the model. Free text models have zero prices. |
input_schema | Media models only: the JSON Schema of the input field for /api/v1/generations — required fields, allowed values, limits. Requests are validated against exactly this schema, so build your forms and clients from it. |
Model descriptions and current prices are also on the site — in the model catalog.
Text models
/api/v1/chat/completions
An OpenAI-compatible endpoint for every text model in the catalog. Official OpenAI SDKs work as is — just change base_url and the key.
| Field | Type | Description |
|---|---|---|
model | string | Required. A text model id from the catalog. |
messages | array | Required. A non-empty array of {role, content} messages. |
stream | boolean | true — stream the response over SSE. Defaults to false. |
Standard parameters are also supported:
temperaturemax_tokenstop_ptop_kfrequency_penaltypresence_penaltyrepetition_penaltystopseedresponse_formatlogit_biaslogprobstop_logprobstoolstool_choiceparallel_tool_callsreasoning
Parameters a model does not support are ignored on its side.
Regular mode (no streaming)
{
"id": "gen-...",
"object": "chat.completion",
"model": "openai/gpt-4o-mini",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hi! I can help with..."},
"finish_reason": "stop"
}
],
"usage": {"prompt_tokens": 12, "completion_tokens": 34, "total_tokens": 46}
}
usage carries token counters only — the same numbers your balance is billed by.
Streaming (stream: true)
The response arrives as Server-Sent Events (Content-Type: text/event-stream): data: {chunk} lines with deltas in choices[].delta.content and a final data: [DONE] marker.
stream = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Tell me about yourself"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
If an error happens after the stream has started, it arrives inside the stream as a data: {"error": {...}} event followed by data: [DONE]. If the client drops the connection, the part of the response generated so far is billed.
Images as input (vision)
For vision-capable models, images are passed in the standard OpenAI format — content parts of type image_url (a regular URL or a base64 data: URL). Request body limit is 25 MB.
{
"model": "...",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "What is in this photo?"},
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}
]
}]
}
Billing for text requests
Billing happens after the response, by actual token usage and the model's catalog prices. Paid models require a positive balance — otherwise 402 insufficient_balance before the model is even called.
Media generation
/api/v1/generations
One endpoint for images, video and sound. The set of input fields depends on the model — it is described by the model's input_schema in the catalog.
{
"model": "model-id-from-catalog",
"input": { ... }
}
- All media references (frames, reference images) must be public http(s) URLs. Inline base64 is not accepted; the request body limit is 2 MB.
- Requests are validated against input_schema; violations return 400 invalid_request with a list of specific errors.
- The cost is computed server-side from the generation parameters and debited from your balance; with insufficient funds you get 402 insufficient_balance and the generation does not start.
Images
Run synchronously: a 200 response comes back right away with ready URLs. Typical input fields: prompt (required), negative_prompt, aspect_ratio, resolution, width/height, number_results (1-4), output_format, reference_images — on models that support references.
curl https://net-room.com/api/v1/generations \
-H "Authorization: Bearer nr-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "image-model-id",
"input": {"prompt": "a cozy seaside cafe, cinematic light", "aspect_ratio": "16:9"}
}'
{
"id": "img_12345",
"object": "generation",
"type": "image",
"model": "image-model-id",
"status": "succeeded",
"progress": 100,
"outputs": ["https://.../result.jpg"],
"cost": 12.0,
"error": null,
"created_at": "2026-08-15T12:00:00+03:00"
}
Video
Run asynchronously: a 202 response with status queued or processing, the result is fetched by polling. Typical input fields: prompt (required), duration (seconds, allowed values are in the schema), aspect_ratio, resolution, negative_prompt, sound, cfg_scale, first_frame / last_frame (frame URLs for image-to-video), reference_images, reference_videos.
curl https://net-room.com/api/v1/generations \
-H "Authorization: Bearer nr-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "video-model-id",
"input": {
"prompt": "a drone flies over a mountain lake at sunrise",
"duration": 5,
"aspect_ratio": "16:9",
"resolution": "1080p"
}
}'
{
"id": "3f2a9c...",
"object": "generation",
"type": "video",
"model": "video-model-id",
"status": "queued",
"progress": 0,
"outputs": [],
"cost": 45.0,
"error": null,
"created_at": "2026-08-15T12:00:00+03:00",
"estimated_time_seconds": 60
}
Sound
Also asynchronous (202 + polling). Sound models come in three kinds with different input fields — the exact set is always in input_schema:
Text to speech (TTS)
text (required), voice, language, output_format
{"model": "tts-model-id", "input": {"text": "Good afternoon! Your order is confirmed.", "voice": "voice-name"}}
Music
mode (prompt | lyrics | instrumental), prompt and/or lyrics, negative_prompt, seed, output_format
{"model": "music-model-id", "input": {"mode": "prompt", "prompt": "a calm lo-fi beat with vinyl crackle"}}
Sound effects (SFX)
prompt (required), output_format
{"model": "sfx-model-id", "input": {"prompt": "rain on a tin roof, 10 seconds"}}
Generation status
/api/v1/generations/{id}
Status and result of any generation. id comes from the creation response (a string; img_<n> for images), accessible only to the key owner. Statuses: queued -> processing -> succeeded | failed.
curl https://net-room.com/api/v1/generations/3f2a9c... \
-H "Authorization: Bearer nr-YOUR_KEY"
- succeeded — ready files in outputs (a list of URLs).
- failed — the reason is in error; a failed generation is not billed.
- progress — 0-100; for video, estimated_time_seconds from the creation response helps too.
- cost — the generation cost; for asynchronous generations the debit is finalized on successful completion.
The recommended polling interval is 3-5 seconds. Polling several generations too aggressively in parallel can hit the per-minute request limit.
Error format
All errors are JSON of the same shape:
{
"error": {
"message": "A human-readable description of the problem",
"type": "invalid_request_error",
"code": "invalid_request"
}
}
| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | Invalid body, unsupported method, input validation error, or a request rejected by the model. |
| 401 | invalid_api_key | Missing Authorization header, wrong or revoked key. |
| 402 | insufficient_balance | Not enough funds on the NetRoom balance. |
| 404 | model_not_found | No model with this id (check GET /api/v1/models). |
| 404 | not_found | No generation with this id, or it belongs to another account. |
| 429 | rate_limit_exceeded | Per-minute or concurrency limit exceeded; the response carries a Retry-After header (seconds). |
| 502 | upstream_error | A temporary error on the generation side — retry later. |
| 500 | server_error | Internal NetRoom error. |
The type field is an OpenAI-like category (authentication_error, insufficient_quota, invalid_request_error, rate_limit_error, api_error) kept for SDK compatibility; build your program logic on code and the HTTP status.
In streaming mode an error after the stream has started arrives as a data: {"error": {...}} event inside the SSE stream.
Limits
| Limit | Value |
|---|---|
| Rate | 60 requests per minute per key by default (fixed window); an individual key limit may differ. Exceeding it returns 429 with a Retry-After header. |
| Concurrency | At most 4 simultaneous requests per account — across all of the account's keys. |
| Body size | 25 MB for /chat/completions (inline images included), 2 MB for /generations (media by URL only). |
| Keys | Up to 10 active keys per account. |
Handle 429 with exponential backoff starting from the Retry-After value; request long text answers with stream: true.
Request logging and retention
To prevent abuse and to investigate incidents and disputed charges, NetRoom keeps a log of API requests. The log records: the model, request parameters, messages (truncated), the truncated response text, token counters, cost, status, IP address and response time. Inline images (data: URLs) are never stored — a technical stub with the size and a checksum is recorded instead of the content.
Log records are kept for a limited time — 90 days by default — and are then deleted automatically. Balance debit history is kept separately in the account's financial history and is not subject to this period.
One key — every model
Create a key in the API tab of your account and make the first request in five minutes. Validation errors are free, failed generations are not billed.