InnoAIAPI Guide

InnoAI API Guide

InnoAI is a private AI inference API. It is compatible with the OpenAI API, so existing OpenAI SDKs and tools work by changing the base URL and the API key.

Base URL: https://api.innoai.net/v1

All requests and responses are JSON over HTTPS, except audio uploads (multipart form data) and generated audio (binary).

Quick start

curl https://api.innoai.net/v1/chat/completions \
  -H "Authorization: Bearer $INNOAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "fast",
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'

Authentication

Every API request needs an API key in the Authorization header:

Authorization: Bearer ina_...

OpenAI compatibility

Use the official OpenAI SDKs with the InnoAI base URL.

Python

from openai import OpenAI

client = OpenAI(base_url="https://api.innoai.net/v1", api_key="ina_...")

resp = client.chat.completions.create(
    model="fast",
    messages=[{"role": "user", "content": "Summarise the water cycle in one sentence."}],
)
print(resp.choices[0].message.content)

Node.js

import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api.innoai.net/v1", apiKey: process.env.INNOAI_API_KEY });

const resp = await client.chat.completions.create({
  model: "fast",
  messages: [{ role: "user", content: "Summarise the water cycle in one sentence." }],
});
console.log(resp.choices[0].message.content);

Responses follow the OpenAI formats. Some responses carry additional fields (for example stop_reason or token_ids); ignore fields you do not use. The model field in a response is always the model name you requested. system_fingerprint is always null.

Models

ModelTypeBest forThinkingMax output tokens
fastchatQuick answers, extraction, drafting, high volumeOff32,768
deepchatHarder problems that benefit from step-by-step reasoningOn131,072

Both models use the same request format. Choosing the model is how you choose whether the model thinks first: deep returns its reasoning in a separate reasoning field and reports reasoning tokens in usage.

Your plan may set a lower maximum output (see Limits).

List models

GET /v1/models returns the models your key may use, with their capabilities and prices.

curl https://api.innoai.net/v1/models -H "Authorization: Bearer $INNOAI_API_KEY"
{
  "object": "list",
  "data": [
    {
      "id": "deep", "object": "model", "owned_by": "innoai", "type": "chat",
      "served_model": "InnoAI Deep 2026-09",
      "description": "Harder problems that benefit from step-by-step reasoning",
      "context_tokens": 262144,
      "max_output_tokens": 131072,
      "max_request_bytes": 20971520,
      "modalities": {"input": ["text"], "output": ["text"]},
      "reasoning": true,
      "unit": "tokens",
      "pricing": {
        "usd": {"input_per_1m": "10.000000", "output_per_1m": "20.000000", "reasoning_per_1m": "20.000000", "per_request": "0.000000"},
        "credits": {"input_per_1k": 2.0, "output_per_1k": 6.0, "reasoning_per_1k": 6.0, "per_request": 0.0}
      }
    }
  ]
}
FieldMeaning
typeModel type; decides which endpoint accepts it (see Endpoints)
served_modelPublic label of the model that actually answers. Also sent as the x-served-model response header; quote it with x-request-id on receipts
context_tokensContext window in tokens, null if not published
max_output_tokensLargest max_tokens you can get: the model's cap, narrowed by your plan
max_request_bytesRequest body limit for this model
modalitiesInput and output modalities (text, audio, embedding)
reasoningWhether the model thinks before answering (reasoning tokens are billed at their own rate)
unitWhat usage is metered in: tokens, audio seconds or characters
pricing.usdPay-as-you-go prices in USD, as 6-decimal strings. Keys depend on the type: per 1M tokens (input_per_1m, output_per_1m, reasoning_per_1m), audio_per_minute for speech-to-text, chars_per_1m for text-to-speech, plus per_request
pricing.creditsSubscription-plan credit prices per 1K tokens / per audio second / per 1K characters, plus per_request

The list only contains models your key may use, so two keys can see different lists.

Endpoints

MethodPathModel typeStatus
POST/v1/chat/completionschatAvailable (fast, deep)
POST/v1/completionschat or completionAvailable (fast, deep)
GET/v1/models–Available
POST/v1/embeddingsembeddingComing soon
POST/v1/rerankrerankComing soon
POST/v1/decidedecisionComing soon
POST/v1/entitiesentity extractionComing soon
POST/v1/classifyclassificationComing soon
POST/v1/guardsafety guardComing soon
POST/v1/audio/transcriptionsspeech-to-textComing soon
POST/v1/audio/speechtext-to-speechComing soon

Each endpoint accepts only models of its type. Sending a chat model to /v1/embeddings, for example, returns 400 with "model does not support this endpoint". "Coming soon" endpoints are live in the API but no model of that type is offered yet; they appear in /v1/models when one is.

Every request body must be a JSON object with a model field (for audio uploads, a model form field). The maximum request size is 20 MB.

Chat completions

POST /v1/chat/completions

Request

{
  "model": "fast",
  "messages": [
    {"role": "system", "content": "You are concise."},
    {"role": "user", "content": "Say hello in five words."}
  ],
  "max_tokens": 40
}

Response (fast)

{
  "id": "chatcmpl-01a13230-1197-7034-898d-cba9d33c008a",
  "object": "chat.completion",
  "created": 1791843373,
  "model": "fast",
  "choices": [{
    "index": 0,
    "message": {"role": "assistant", "content": "Greetings to you, my friend.", "reasoning": null},
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 15,
    "completion_tokens": 8,
    "total_tokens": 23,
    "completion_tokens_details": {"reasoning_tokens": 0}
  }
}

Response (deep): the thinking is in message.reasoning, the answer in message.content. Reasoning tokens are included in completion_tokens and broken out in completion_tokens_details.reasoning_tokens.

{
  "id": "chatcmpl-01a13230-129d-74b1-8711-e2ba20f89ce2",
  "object": "chat.completion",
  "model": "deep",
  "choices": [{
    "index": 0,
    "message": {"role": "assistant", "content": "12 × 12 = **144**", "reasoning": "12 × 12 = 144"},
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 16,
    "completion_tokens": 26,
    "total_tokens": 42,
    "completion_tokens_details": {"reasoning_tokens": 11}
  }
}

Streaming

Set "stream": true to receive server-sent events. With deep, reasoning arrives first as delta.reasoning, then the answer as delta.content. The last event before data: [DONE] always carries usage (with an empty choices list): InnoAI turns on stream_options.include_usage for every streamed request.

curl -N https://api.innoai.net/v1/chat/completions \
  -H "Authorization: Bearer $INNOAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"model": "deep", "stream": true, "messages": [{"role": "user", "content": "What is 2+2?"}]}'
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"deep","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"deep","choices":[{"index":0,"delta":{"reasoning":"Simple"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"deep","choices":[{"index":0,"delta":{"content":"2 + 2 = **4**"},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"deep","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":14,"total_tokens":28,"completion_tokens_details":{"reasoning_tokens":3}}}

data: [DONE]

Python:

stream = client.chat.completions.create(model="deep", stream=True,
    messages=[{"role": "user", "content": "What is 2+2?"}])
for chunk in stream:
    if chunk.choices:
        delta = chunk.choices[0].delta
        print(getattr(delta, "reasoning", None) or delta.content or "", end="")
    elif chunk.usage:
        print("\n", chunk.usage)

Completions

POST /v1/completions takes a prompt instead of messages and works with fast and deep.

{"model": "fast", "prompt": "The capital of France is", "max_tokens": 8}
{
  "id": "cmpl-01a13230-141f-72de-9409-93faa476b80d",
  "object": "text_completion",
  "model": "fast",
  "choices": [{"index": 0, "text": " one of the most popular tourist destinations in", "finish_reason": "length"}],
  "usage": {"prompt_tokens": 5, "completion_tokens": 8, "total_tokens": 13}
}

Embeddings, rerank, decide, entities, classify, guard

Coming soon. These endpoints accept a JSON object with a model of the matching type; the request and response formats will be documented here when a model is available. Embeddings will follow the OpenAI embeddings format.

Audio transcriptions (speech-to-text)

Coming soon. POST /v1/audio/transcriptions will take multipart/form-data with the audio file and a model field, as in the OpenAI API. Usage is metered in seconds of audio.

Audio speech (text-to-speech)

Coming soon. POST /v1/audio/speech will take JSON with model and input (the text), as in the OpenAI API, and stream back audio. Usage is metered in characters of input.

Parameters InnoAI sets for you

Limits

Limits come from your plan (and any narrower settings your administrator applies to your account or key). The dashboard shows your current limits and usage.

LimitHow it worksWhen exceeded
Requests per minuteRolling one-minute window429 rate_limit_error, with Retry-After
Tokens per minute / per dayInput + output tokens, counted when a request completes429 rate_limit_error, with Retry-After
Concurrent requestsYour requests in flight at once429 rate_limit_error "too many concurrent requests", Retry-After: 1
Max output tokensOutput is clamped (see above)Not an error
Session budgetCredits per session. A session starts with your first request and lasts 5 hours; the next request after it ends starts a new one429 usage_limit_error, limit: "session"
Weekly budgetCredits used in the last 7 days (rolling, hourly granularity)429 usage_limit_error, limit: "weekly"
Monthly capCredits per calendar month in your account's time zone429 usage_limit_error, limit: "monthly"
Daily / monthly spend cap (pay-as-you-go)USD spent today / this calendar month, if your account sets a cap429 usage_limit_error, limit: "daily_spend" / "monthly_spend"
Prepaid balance (pay-as-you-go)Balance must be above zero (or your account's minimum)402 insufficient_balance
Model capacityShared capacity across all customers. When a model is busy, your request waits in a queue (currently up to 10 seconds)429 capacity_error, Retry-After: 5

Usage-limit errors include resets_at (UTC, RFC 3339) and a Retry-After header with the seconds until then:

{"error": {"message": "session usage limit reached for your plan (resets at the time in resets_at)",
           "type": "usage_limit_error", "limit": "session", "resets_at": "2026-10-13T03:16:13Z"}}

Budgets are checked before a request starts and charged when it finishes, so a request that starts within budget always completes; the counters can briefly go past the limit by the cost of requests already in flight.

Response headers

HeaderWhenMeaning
x-request-idRequests that reached a modelUnique request id. Quote it when contacting support.
x-served-modelRequests that reached a modelPublic label of the model that produced the response (same as served_model in /v1/models). The body's model field still echoes the name you requested.
x-ratelimit-session-limit / -remaining / -resetPlans with a session budgetSession budget in credits, credits remaining, and when the session ends (UTC).
x-ratelimit-weekly-limit / -remaining / -resetPlans with a weekly budgetWeekly budget, remaining, and when credit is next returned (UTC; the current time if nothing has been used).
x-ratelimit-monthly-limit / -remaining / -resetPlans with a monthly capMonthly cap, remaining, and when the month resets (UTC).
x-balance-remaining-usdPay-as-you-go accountsAvailable balance in USD (6 decimals) when the request was admitted, before its own cost.
Retry-After429 responsesSeconds to wait before retrying.

Credit values in headers have up to 3 decimals, e.g. x-ratelimit-session-remaining: 49999.961.

Billing and usage

Each account is on one of two billing types.

Subscription plans have a flat price and credit budgets (session, weekly and/or monthly). Each request costs credits based on what it used:

deep costs more credits per token than fast.

Pay-as-you-go accounts have a prepaid USD balance. Each completed request is debited at the model's USD prices (per 1M input, output and reasoning tokens; per minute of audio; per 1M characters; per request), exact to $0.000001. Requests are refused with 402 when the balance runs out; the request that crosses zero may take the balance slightly below zero by at most its own cost. Your administrator adds credit; the dashboard shows your balance, spend this month, cost per model and recent charges.

How usage is measured: tokens are the counts reported by the model in usage. If a stream ends before usage is reported (for example, you disconnect), the request is still charged: input is estimated from the request size and output as one token per event received. Requests rejected before reaching a model (400, 401, 402, 404, 413, 429) are not charged, and neither are requests the model rejects with an error. A stream that breaks partway is charged for what was produced.

Errors

Errors from InnoAI have this shape:

{"error": {"message": "rate limit exceeded", "type": "rate_limit_error"}}

Messages never repeat any part of your request.

StatustypeMeaningWhat to do
400invalid_request_errorBody is not a JSON object, model is missing, or the model does not support this endpointFix the request
401authentication_errorMissing, invalid, revoked, disabled or expired keyCheck the key; create a new one if needed
402insufficient_balancePay-as-you-go balance used up. Includes balance_usdAsk your administrator to add credit
403billing_not_allowedThe key is restricted to a billing source your account is not set up forAsk your administrator
404not_found_errorUnknown model, or not allowed for this keyUse a model from GET /v1/models
404not_found_errorUnknown endpoint path ("unknown endpoint")Check the path against the Endpoints table
413invalid_request_errorRequest body too large for this modelSend a smaller body
429rate_limit_errorRequests/tokens per minute, tokens per day, or concurrency exceededWait Retry-After seconds, then retry
429usage_limit_errorPlan budget or spend cap reached; limit is session, weekly, monthly, daily_spend or monthly_spendWait until resets_at, or change plan
429capacity_errorThe model is at capacity right nowRetry after Retry-After (5 s) with backoff
500api_errorInternal errorRetry with backoff
502api_errorModel unavailableRetry with backoff
504api_errorModel timed out (no data for 300 seconds)Retry; prefer streaming for long outputs

If the model itself rejects a request (for example, an invalid message format), its error is passed through with its own status code; these follow the OpenAI error format. Internal detail (hosts, paths, stack traces) is removed; a backend fault that would only expose its internals is reported as "The model rejected the request (HTTP nnn)". If you close the connection before a response finishes, InnoAI records the request as client-closed (status 499 in your usage) and charges what was used up to that point.

Privacy

Best practices

Changelog