curl recipes
Copy-paste requests for every shape of call, including streaming.
Every example assumes your key is in the environment:
export TELLUVIAN_API_KEY="sk_live_..."Basic request
curl https://api.telluvian.ai/v1/chat/completions \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-31B-it",
"messages": [{"role": "user", "content": "When was the Eiffel Tower built?"}]
}'The response is standard Chat Completions plus two extension fields:
{
"id": "chatcmpl-...",
"object": "chat.completion",
"model": "google/gemma-4-31B-it",
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": "The Eiffel Tower opened in 1889."},
"finish_reason": "stop"
}],
"usage": {"prompt_tokens": 9, "completion_tokens": 7, "total_tokens": 16},
"tokens": ["The", " Eiffel", " Tower", " opened", " in", " 1889", "."],
"scores": {"hallucination": [0.01, 0.02, 0.02, 0.05, 0.03, 0.61, 0.01]}
}tokens[i] lines up with scores.hallucination[i], and joining tokens gives
back content exactly.
Streaming
Set "stream": true. Use curl -N to disable buffering, or you will see the
whole response arrive at once and streaming will look broken.
curl -N https://api.telluvian.ai/v1/chat/completions \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-31B-it",
"messages": [{"role": "user", "content": "Tell me about Ada Lovelace."}],
"stream": true
}'Server-sent events, one JSON object per data: line:
data: {"choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"choices":[{"index":0,"delta":{"content":"The"},"finish_reason":null}],"scores":{"hallucination":0.01}}
data: {"choices":[{"index":0,"delta":{"content":" Eiffel"},"finish_reason":null}],"scores":{"hallucination":0.02}}
data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":9,"completion_tokens":7,"total_tokens":16}}
data: [DONE]Three chunk shapes, and code that assumes every chunk has content will crash on two of them:
- The first chunk carries
delta.roleand no content. - Content chunks carry
delta.contentand onescoresvalue for that token. - The final chunk carries
finish_reason,usage, and no content.
The stream ends with the literal line data: [DONE], which is not JSON — parse
for it before attempting JSON.parse.
Usage is always on the last chunk
You do not need stream_options. Token counts arrive on the final chunk of
every stream, and they are what your request is billed from.
Streaming, scores only
Pull just the per-token scores out of a stream with jq. This reads the probe
name out of each chunk rather than hardcoding it, so it works whichever probe
the model is serving:
curl -sN https://api.telluvian.ai/v1/chat/completions \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-31B-it",
"messages": [{"role": "user", "content": "Tell me about Ada Lovelace."}],
"stream": true
}' \
| grep '^data: {' \
| sed 's/^data: //' \
| jq -r '(.scores // {}) as $s
| ($s | to_entries | map(.value | tostring) | join("\t")) as $vals
| select($vals != "")
| "\($vals)\t\(.choices[0].delta.content // "")"'0.01 The
0.02 Eiffel
0.02 Tower
0.61 1889No output means no scores, not a broken command
Chunks without a score omit scores entirely, so a filter that selects on it
prints nothing at all rather than reporting an error — which looks exactly
like a mistyped pipeline. If you get no score column, drop the jq and look
at the raw data: lines to see whether the scores are there.
Scores are keyed by probe name. hallucination is the usual one, but a model
can expose a different probe or several at once, so it is worth checking what
you are getting rather than assuming:
# Which probes does this model report?
curl -sN https://api.telluvian.ai/v1/chat/completions \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-31B-it",
"messages": [{"role": "user", "content": "hi"}],
"stream": true
}' \
| grep '^data: {' | sed 's/^data: //' \
| jq -r 'select(.scores) | .scores | keys | join(", ")' | head -1Why am I not getting scores?
If no chunk has a scores field, check these in order:
include_scoresis being set tofalsesomewhere in your stack — a shared client wrapper, a proxy, or a framework that injects defaults. Omitting the field is not a cause: it defaults totrue.- The probe name is different from the one you are filtering on. Run the
keyscommand above; if it prints a name, the scores are arriving under that key. - You are calling a model you did not mean to. Confirm the exact
modelvalue your request sent;GET /v1/modelslists every available model along with its probe mode.
If the keys command prints nothing for a model listed as supporting probes,
that is on our side rather than yours —
get in touch with the model name and roughly when
you called it.
Omitting include_scores does not turn scoring off
include_scores defaults to true, so leaving it out gives you scores.
Adding "include_scores": true explicitly is harmless but changes nothing —
if that appeared to fix missing scores, something else changed too, usually
the model being called. See include_scores.
A single null score is different, and expected: occasionally one token's
reading is unavailable, most often the last token of a response cut short by
max_tokens. Alignment is preserved and only that token is affected. See
Scores.
Without scoring
curl https://api.telluvian.ai/v1/chat/completions \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-31B-it",
"messages": [{"role": "user", "content": "Hello"}],
"include_scores": false
}'scores and tokens come back null, and the probe surcharge is not charged.
See include_scores.
Multi-turn conversation
Send the whole history; the API is stateless.
curl https://api.telluvian.ai/v1/chat/completions \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-31B-it",
"messages": [
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "When was the Eiffel Tower built?"},
{"role": "assistant", "content": "It opened in 1889."},
{"role": "user", "content": "Who designed it?"}
]
}'Sampling controls
The usual OpenAI parameters are supported — max_tokens, temperature,
top_p, stop, seed, presence_penalty, frequency_penalty.
curl https://api.telluvian.ai/v1/chat/completions \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-31B-it",
"messages": [{"role": "user", "content": "List three facts about Mars."}],
"max_tokens": 200,
"temperature": 0.2,
"stop": ["\n\n"]
}'Listing models
curl https://api.telluvian.ai/v1/models \
-H "Authorization: Bearer $TELLUVIAN_API_KEY"Returns each model with its current price and probe mode. This endpoint requires a key, like OpenAI's.
# Just the ids and prices
curl -s https://api.telluvian.ai/v1/models \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
| jq -r '.data[] | "\(.id)\t\(.pricing.prompt)\t\(.pricing.completion)"'Checking your rate limit
Rate-limit state is in the response headers:
curl -i https://api.telluvian.ai/v1/chat/completions \
-H "Authorization: Bearer $TELLUVIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"google/gemma-4-31B-it","messages":[{"role":"user","content":"hi"}]}' \
| grep -i 'x-ratelimit\|retry-after'