# First request

> Find an exact model id and send one chat completion, in curl, Python or TypeScript.

## Download and configure

Install Python 3 or Node 22 and download the complete examples from [the recipe archive](https://aihub.belive.sg/docs/examples/tokenhub.zip). The archive has the files shown below, including the browser app and local stream checks. Choose an exact model ID from the public catalogue and export your inference key from a secret store.

```bash
curl --fail "https://aihub.belive.sg/docs/examples/tokenhub.zip" -o tokenhub-examples.zip
unzip tokenhub-examples.zip
cd tokenhub-examples
cp .env.example .env
# Edit .env: set TOKENHUB_API_KEY and TOKENHUB_MODEL.
# Keep .env local. Scripts read process environment, not the .env file itself.
set -a
. ./.env
set +a
# Use the API base for the environment shown by this docs site.
export TOKENHUB_BASE_URL="https://tokenhub-api.belive.sg/api/v1"
```

## 1. Choose a model

The catalogue is public. List it and copy one exact `author/slug` id into `TOKENHUB_MODEL`:

```bash
curl -s "https://tokenhub-api.belive.sg/api/v1/models" | python3 -c 'import json,sys; [print(m["id"]) for m in json.load(sys.stdin)["data"]]'
```

## 2. Send the request

Each program confirms the model exists, sends one message and prints the answer, the token usage and the `X-Generation-Id` request id. The Python and TypeScript versions have no dependencies.

**cURL**

```bash
#!/usr/bin/env bash
# First request with curl. Needs TOKENHUB_BASE_URL, TOKENHUB_API_KEY and TOKENHUB_MODEL.
set -euo pipefail
: "${TOKENHUB_BASE_URL:?set TOKENHUB_BASE_URL}" "${TOKENHUB_API_KEY:?set TOKENHUB_API_KEY}" "${TOKENHUB_MODEL:?set TOKENHUB_MODEL}"

# 1. Confirm the exact model id exists. The catalogue is public; no key is needed.
curl -fsS "$TOKENHUB_BASE_URL/models/$TOKENHUB_MODEL" > /dev/null \
  || { echo "model $TOKENHUB_MODEL is not in the catalogue" >&2; exit 1; }

# 2. Send one chat completion. -D - prints the response headers, including X-Generation-Id,
#    the request id to look up its cost later (GET /generation?id=…).
curl -sS -D - "$TOKENHUB_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $TOKENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"model\": \"$TOKENHUB_MODEL\", \"max_tokens\": 200,
       \"messages\": [{\"role\": \"user\", \"content\": \"Say hello in one sentence.\"}]}"
echo
```

**Python**

```python
"""First request with the Python standard library only. Python 3.10+.

Needs TOKENHUB_BASE_URL, TOKENHUB_API_KEY and TOKENHUB_MODEL in the environment.
Run: python3 first_request.py
"""
import json
import os
import sys
import urllib.error
import urllib.request

BASE = os.environ["TOKENHUB_BASE_URL"].rstrip("/")
KEY = os.environ["TOKENHUB_API_KEY"]
MODEL = os.environ["TOKENHUB_MODEL"]


def request(method, path, body=None, auth=True):
    headers = {"Content-Type": "application/json"}
    if auth:
        headers["Authorization"] = f"Bearer {KEY}"
    data = None if body is None else json.dumps(body).encode()
    req = urllib.request.Request(f"{BASE}{path}", data=data, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req, timeout=120) as resp:
            return resp.headers, json.load(resp)
    except urllib.error.HTTPError as err:
        payload = json.load(err)
        sys.exit(f"HTTP {err.code}: {payload.get('error', {}).get('message', payload)}")


# 1. Confirm the exact model id and that it serves chat. The catalogue needs no key.
_, model = request("GET", f"/models/{MODEL}", auth=False)
print("model:", model["data"]["id"])

# 2. One chat completion.
headers, answer = request("POST", "/chat/completions", {
    "model": MODEL,
    "max_tokens": 200,
    "messages": [{"role": "user", "content": "Say hello in one sentence."}],
})
print(answer["choices"][0]["message"]["content"])
print("usage:", answer.get("usage"))
# The request id. Look its cost up later with GET /generation?id=<this id>.
print("request id:", headers.get("X-Generation-Id"))
```

**TypeScript**

```typescript
// First request with Node 22's built-in fetch. No dependencies.
// Needs TOKENHUB_BASE_URL, TOKENHUB_API_KEY and TOKENHUB_MODEL in the environment.
// Run: node --experimental-strip-types first-request.ts
const base = required("TOKENHUB_BASE_URL").replace(/\/+$/, "");
const key = required("TOKENHUB_API_KEY");
const model = required("TOKENHUB_MODEL");

function required(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`set ${name}`);
  return value;
}

// 1. Confirm the exact model id. The catalogue is public; no key is needed.
const found = await fetch(`${base}/models/${model}`);
if (!found.ok) throw new Error(`model ${model} is not in the catalogue (HTTP ${found.status})`);

// 2. One chat completion.
const res = await fetch(`${base}/chat/completions`, {
  method: "POST",
  headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    model,
    max_tokens: 200,
    messages: [{ role: "user", content: "Say hello in one sentence." }],
  }),
});
const body = await res.json();
if (!res.ok) throw new Error(`HTTP ${res.status}: ${body?.error?.message ?? JSON.stringify(body)}`);
console.log(body.choices[0].message.content);
console.log("usage:", body.usage);
// The request id. Look its cost up later with GET /generation?id=<this id>.
console.log("request id:", res.headers.get("x-generation-id"));
```

## 3. Check what it cost

Pass the printed request id to [the generation lookup](https://aihub.belive.sg/docs/billing) with the same key, or open **Console → Activity**.

```bash
curl "https://tokenhub-api.belive.sg/api/v1/generation?id=req_…" -H "Authorization: Bearer $TOKENHUB_API_KEY"
```

Next: [stream the answer in a chat loop](https://aihub.belive.sg/docs/terminal-chat).
