HelipodDocs

OpenAI compatible

Chat completions with the OpenAI SDKs, curl, streaming, tools, JSON output, reasoning and token usage.

Point any OpenAI client at https://ai.helipod.io/v1 and use a Helipod AI key. The request and response follow the OpenAI Chat Completions API.

POST/v1/chat/completions
Create a chat completion. Charged to your AI balance by the tokens used.

Request

The body is an OpenAI chat completion request. The gateway reads these fields itself and passes the rest to the model unchanged:

Prop

Type

About the output cap:

  • No cap sent. The gateway applies a default of 16,384 tokens, or the model's own output limit when that is lower.
  • Cap above the model's limit. It is lowered to the model's limit instead of failing the request.
  • The cap sets the hold. Before the call, the gateway reserves enough balance to pay for the full cap. A very large cap with a small balance can answer 402 even if the real answer would be short. See Pricing and billing.

Other standard fields (temperature, top_p, stop, tools, tool_choice, response_format, reasoning_effort, stream_options, user and so on) are passed to the model. Whether a model honours a given option depends on the model; see the capabilities in Models & pricing in the dashboard.

Examples

curl https://ai.helipod.io/v1/chat/completions \
  -H "Authorization: Bearer hel_ai_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-5.3",
    "max_completion_tokens": 2000,
    "messages": [
      { "role": "system", "content": "You are a concise assistant." },
      { "role": "user", "content": "Explain what a VPS is in two sentences." }
    ]
  }'

Response

A standard chat completion:

{
  "id": "chatcmpl-8f3c2a1b9d7e4f60a1b2c3d4",
  "object": "chat.completion",
  "created": 1792130400,
  "model": "glm-5.3",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "A VPS is a virtual server with its own operating system and root access...",
        "reasoning_content": "The user wants a short definition..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 1250,
    "completion_tokens": 310,
    "total_tokens": 1560,
    "prompt_tokens_details": { "cached_tokens": 1024 },
    "completion_tokens_details": { "reasoning_tokens": 240 }
  }
}

The model field in the body comes from the model's own answer and can be spelled differently from the id you sent. The x-helipod-model response header always carries the catalog id the request was billed as. See Response headers.

Token usage

FieldMeaningBilled as
usage.prompt_tokensThe whole prompt, including cached tokens.Input, minus the cached part
usage.prompt_tokens_details.cached_tokensThe part of the prompt read from the model's prompt cache.Cached input (cheaper)
usage.completion_tokensEverything the model wrote, including reasoning.Output
usage.completion_tokens_details.reasoning_tokensThe reasoning part of completion_tokens, when the model reports it.Output (already inside completion_tokens)

So a request costs (prompt_tokens - cached_tokens) x input price + cached_tokens x cached price + completion_tokens x output price, with prices per 1M tokens. Prompt caching is automatic on models that support it: repeat the same long prefix (system prompt, documents, tool definitions) at the start of your messages and later requests read it from the cache.

Streaming

Set "stream": true to receive server-sent events (text/event-stream), each data: line a chat.completion.chunk, ending with data: [DONE].

curl -N https://ai.helipod.io/v1/chat/completions \
  -H "Authorization: Bearer hel_ai_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash",
    "stream": true,
    "stream_options": { "include_usage": true },
    "messages": [{ "role": "user", "content": "Write a haiku about Jakarta rain." }]
  }'

Good to know:

  • Usage at the end. The last chunk before [DONE] carries usage. Send stream_options.include_usage to be sure your client keeps it.
  • Reasoning arrives separately. Reasoning models stream their thinking in delta.reasoning_content before the answer in delta.content. Show it or hide it, but do not treat it as the answer.
  • Keep-alive while the model starts. If the model has not started answering after about 10 seconds, the gateway answers 200 right away and sends SSE comment lines (: waiting for the model, then : keep-alive every 15 seconds) until it does. SSE clients ignore comment lines.
  • Errors after the 200 (while the gateway was waiting for a slow model) arrive as a final event, data: {"error": {...}} followed by data: [DONE]. A stream that ends without data: [DONE] did not finish. See Limits and errors.
  • Stopping early. If you close the connection mid-stream, the request is charged for what was generated up to then.

Tools (function calling)

tools, tool_choice and parallel_tool_calls are passed through in the OpenAI format. Use models with the tools capability.

const res = await client.chat.completions.create({
  model: "kimi-k3",
  messages: [{ role: "user", content: "What is the weather in Bandung?" }],
  tools: [
    {
      type: "function",
      function: {
        name: "get_weather",
        description: "Current weather for a city",
        parameters: {
          type: "object",
          properties: { city: { type: "string" } },
          required: ["city"],
        },
      },
    },
  ],
});

const call = res.choices[0].message.tool_calls?.[0];
// call.function.name === "get_weather", call.function.arguments === '{"city":"Bandung"}'

Run the function yourself, then send the result back as a tool message with the matching tool_call_id, in the same conversation. When a reasoning model made the tool call, send its assistant message back unchanged, including reasoning_content.

JSON output

response_format is passed through: { "type": "json_object" } for any valid JSON object, or { "type": "json_schema", "json_schema": { ... } } for a fixed shape, on models with the structured_output capability. Also say in the prompt that you want JSON.

{
  "model": "glm-5.3",
  "response_format": { "type": "json_object" },
  "messages": [
    { "role": "user", "content": "Return {\"city\": string, \"province\": string} for Bandung as JSON." }
  ]
}

Reasoning

Most models reason before they answer. Reasoning tokens count as output, are billed at the output price, and use up max_completion_tokens. Give reasoning models a generous cap (2,000 tokens or more), or the visible answer can be cut short or empty with finish_reason: "length".

Where a model supports it, reasoning_effort ("low", "medium", "high") is passed through to control how much it thinks. The reasoning text, when the model returns it, is in message.reasoning_content (or delta.reasoning_content when streaming).

Images

Models with the vision capability accept OpenAI image_url content parts, with an https:// URL or a data:image/...;base64,... URL. The request body can be up to 8 MB.

{
  "role": "user",
  "content": [
    { "type": "text", "text": "What is in this picture?" },
    { "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
  ]
}

Response headers

HeaderMeaning
x-request-idThe gateway's id for this request, for example req_6f1d2c9e-.... The same id appears in Usage in the dashboard. Log it and include it when you contact support.
x-helipod-modelThe catalog id the request was routed to and billed as.
x-helipod-model-sunsetOnly for a model retiring within 30 days: the retirement time (ISO 8601).
x-helipod-model-replacementWith the sunset header, when there is one: the model id to move to.

In a browser, only x-request-id can be read by your script (it is the only header exposed through CORS). Server-side clients see all of them.

On this page