Streaming, tool calls, and JSON mode on hiapi's OpenAI-compatible chat completions endpoint
Choose a model, enter your prompt, and see the result.
HiAPI Blog
HiAPI
Generate it with HiAPI
grok-4.6 is a reasoning-tuned chat model available through the hiapi API. Unlike hiapi's image and video models, it doesn't use the async /v1/tasks pipeline — it's a synchronous, OpenAI-compatible chat completion, so you get a normal HTTP response (or an SSE stream) with no polling required. This recipe covers a minimal working request, then the parts you actually need in production: streaming, tool calls, JSON mode, and error handling.
Authorization: Bearer sk-... header.curl and Python's requests; the JSON shape is the same regardless of language.https://api.hiapi.ai/v1/chat/completions and use the bare model id grok-4.6.curl https://api.hiapi.ai/v1/chat/completions \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-4.6",
"messages": [
{"role": "user", "content": "Give me one sentence explaining what a Kalman filter does."}
]
}'
A successful response looks like this:
{
"id": "b6c018d3-218e-9b06-aa12-5afc02e21b5c",
"object": "chat.completion",
"model": "grok-4.6",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "A Kalman filter estimates the true state of a system over time by combining noisy measurements with a predictive model, weighting each by its uncertainty."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 24,
"completion_tokens": 31,
"total_tokens": 55
}
}
The answer text is at choices[0].message.content, same as any OpenAI-style chat API.
Here's the same call in Python with requests:
import os
import requests
resp = requests.post(
"https://api.hiapi.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
json={
"model": "grok-4.6",
"messages": [
{"role": "user", "content": "Give me one sentence explaining what a Kalman filter does."}
],
},
timeout=60,
)
resp.raise_for_status()
print(resp.json()["choices"][0]["message"]["content"])
grok-4.6 is a reasoning model: before it writes the final answer, it can spend extra tokens "thinking," and those reasoning tokens are billed as output tokens (see pricing for current rates). You control how much thinking happens with reasoning_effort, one of low, medium, high, or xhigh (the API defaults to high if you omit it):
curl https://api.hiapi.ai/v1/chat/completions \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-4.6",
"messages": [{"role": "user", "content": "Say hi in five words."}],
"reasoning_effort": "low"
}'
Non-streaming responses expose the model's reasoning trace in message.reasoning_content, separate from the final message.content. For short, low-latency tasks, drop to low or medium; for multi-step problems, high or xhigh trades latency and tokens for better answers.
Set "stream": true and read the response as Server-Sent Events instead of waiting for the full completion:
curl -N https://api.hiapi.ai/v1/chat/completions \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-4.6",
"messages": [{"role": "user", "content": "Count from 1 to 5."}],
"stream": true
}'
Each line is a data: {...} chunk. Reasoning tokens stream in choices[0].delta.reasoning_content, then answer tokens stream in choices[0].delta.content, and the stream ends with a literal data: [DONE]:
data: {"id":"...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"reasoning_content":"Listing"},"finish_reason":null}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"1, 2"},"finish_reason":null}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
In Python, requests can iterate the stream line by line:
import json
import requests
with requests.post(
"https://api.hiapi.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
json={
"model": "grok-4.6",
"messages": [{"role": "user", "content": "Count from 1 to 5."}],
"stream": True,
},
stream=True,
timeout=60,
) as resp:
for line in resp.iter_lines():
if not line or not line.startswith(b"data: "):
continue
payload = line[len(b"data: "):]
if payload == b"[DONE]":
break
chunk = json.loads(payload)
delta = chunk["choices"][0]["delta"]
if "content" in delta:
print(delta["content"], end="", flush=True)
grok-4.6 supports OpenAI-style function calling: pass a tools array and let the model decide when to invoke one via tool_choice: "auto".
curl https://api.hiapi.ai/v1/chat/completions \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-4.6",
"messages": [{"role": "user", "content": "What is the weather in Tokyo?"}],
"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"
}'
When the model decides to call the tool, finish_reason comes back as "tool_calls" and the arguments arrive as a JSON string you parse yourself:
{
"choices": [
{
"message": {
"role": "assistant",
"tool_calls": [
{
"id": "call_...",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"city\":\"Tokyo\"}"}
}
]
},
"finish_reason": "tool_calls"
}
]
}
Run your function, then append a role: "tool" message with the result (and the matching tool_call_id) to messages and send the conversation back for a final natural-language answer.
For a response you can parse without a tool call round-trip, set response_format:
curl https://api.hiapi.ai/v1/chat/completions \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-4.6",
"messages": [
{"role": "system", "content": "Respond with a JSON object with keys \"city\" and \"country\"."},
{"role": "user", "content": "Where is the Eiffel Tower?"}
],
"response_format": {"type": "json_object"}
}'
message.content comes back as a JSON string (still inside the normal chat completion envelope), which you parse with json.loads(...) on your end. Always instruct the model in your prompt to actually produce JSON — response_format constrains the output format but doesn't invent the schema for you.
HTTP 401 with {"error":{"code":"permission_denied","message":"...","request_id":"...","type":"hiapi_error"}}. Insufficient balance returns 402; an invalid model id or malformed parameter returns 400. Check the status code first, and log request_id from the error body — it's what you'll want on hand if you ever need to report an issue.429 and 503 with backoff. hiapi enforces per-account rate limits; a 429 means you're over the limit, and a 503 means the model is temporarily unavailable. Both are safe to retry with capped exponential backoff (for example 1s, 2s, 4s, giving up after a handful of attempts) rather than looping tightly.high or xhigh effort takes noticeably longer than low; size your client timeout to the effort level you're using rather than a single global constant.reasoning_content deltas arrive ahead of the final answer.$HIAPI_API_KEY / os.environ["HIAPI_API_KEY"] for exactly that reason.For the full parameter reference, including every field grok-4.6 accepts, see the model page and the grok-4.6 docs.
Is grok-4.6 on hiapi OpenAI SDK-compatible?
The request and response shapes follow the OpenAI chat completions convention (messages, choices[0].message.content, streaming deltas, tool calls), so most OpenAI-compatible tooling that lets you override the base URL and model id should work. Point the base URL at https://api.hiapi.ai/v1 and use grok-4.6 as the model id.
Why is my response slower than other models?
grok-4.6 spends tokens on an internal reasoning pass before answering, and those tokens are billed as output tokens. Lower reasoning_effort to low or medium for latency-sensitive calls, or switch to streaming so you're not waiting for the whole response at once.
What does reasoning_content mean and can I hide it from users?
It's the model's intermediate reasoning trace, returned separately from content (or as delta.reasoning_content while streaming). You can simply not render it in your UI — only content is the final answer.
I got a 401 with permission_denied — what's wrong?
Your Authorization header is missing, malformed, or the key is invalid or revoked. Re-check that you're sending Authorization: Bearer sk-... with a live key from your dashboard.
Can I use grok-4.6 for function calling and JSON mode in the same request?
tools/tool_choice and response_format are independent parameters and can both be set, but if the model decides to call a tool, finish_reason will be "tool_calls" and message.content may be empty on that turn — handle the tool call first, then request the final JSON-formatted answer on the follow-up turn.
How do I control cost?
Reasoning tokens count as output tokens, so cost scales with reasoning_effort and response length. Check current per-token rates on the pricing page before running high-volume workloads.