> ## Documentation Index
> Fetch the complete documentation index at: https://telluvian.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# curl recipes

> Copy-paste requests for every shape of call, including streaming.

Source: https://telluvian.ai/docs/curl

Every example assumes your key is in the environment:

```bash
export TELLUVIAN_API_KEY="sk_live_..."
```

## Basic request

```bash
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?"
    }
  ],
  "include_scores": true
}'
```

This example explicitly enables verification. The response adds two extension fields:

```json
{
  "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.

```bash
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,
  "include_scores": 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:

1. **The first chunk** carries `delta.role` and no content.
2. **Content chunks** carry `delta.content` and one `scores` value for that
   token when available.
3. **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:

```bash
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,
  "include_scores": 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	 1889
```

> **No 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:

```bash
# 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,
  "include_scores": true
}' \
  | grep '^data: {' | sed 's/^data: //' \
  | jq -r 'select(.scores) | .scores | keys | join(", ")' | head -1
```

### Why am I not getting scores?

If no chunk has a `scores` field, check these in order:

1. **`include_scores: true` was not sent.** Omitting the field or setting it
   to `false` disables probing. Check client wrappers and proxies too.
2. **The probe name is different from the one you are filtering on.** Run the
   `keys` command above; if it prints a name, the scores are arriving under
   that key.
3. **You are calling a model you did not mean to.** Confirm the exact `model`
   value your request sent; `GET /v1/models` lists every available model.

If the `keys` command prints nothing for a model listed as supporting probes,
that is on our side rather than yours:
[get in touch](mailto:hello@telluvian.ai) with the model name and roughly when
you called it.

> **Verification must be explicitly enabled**
>
> Omitted `include_scores` defaults to `false`. Add `"include_scores": true`
> when you want scores, and allow enough output tokens for a reasoning model
> to produce visible text. Reasoning-only responses have no visible tokens to score.

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](https://telluvian.ai/docs/scores.md).

## Without verification

```bash
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 verification fee is not
charged. See [`include_scores`](https://telluvian.ai/docs/include-scores.md).

## Multi-turn conversation

Send the whole history; the API is stateless.

```bash
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`.

```bash
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

```bash
curl https://api.telluvian.ai/v1/models \
  -H "Authorization: Bearer $TELLUVIAN_API_KEY"
```

Returns each model with its current price. This endpoint requires a key, like
OpenAI's.

```bash
# 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:

```bash
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'
```

See [Errors and rate limits](https://telluvian.ai/docs/errors.md).
