OpenRouter Hello World
Overview
Send a minimal chat completion request through OpenRouter, understand the response format, try different models, and verify the full round-trip works. All requests go to the single endpoint POST https://openrouter.ai/api/v1/chat/completions.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTER_API_KEY — see the openrouter-install-auth skill for setup
curl and jq for the command-line request, or Python 3.8+ / Node.js 18+ with the OpenAI SDK (pip install openai / npm install openai)
- A free-tier model works for every step here (no credits required for
:free models)
Instructions
- Export your key:
export OPENROUTER_API_KEY="sk-or-v1-...".
- Send the minimal cURL request below and confirm you get a
choices[0].message.content back.
- Read the Response Format section to identify the four key fields (
id, model, usage, finish_reason).
- Repeat the same request from your app language using the Python or TypeScript example.
- Swap model IDs per Try Different Models to confirm multi-model access works with the same code.
- Query
GET /api/v1/generation?id=gen-... per Check Generation Cost to verify cost tracking on the request you just sent.
Minimal Request (cURL)
curl -s https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-2-9b-it:free",
"messages": [{"role": "user", "content": "Say hello in three languages"}],
"max_tokens": 100
}' | jq .
Response Format
{
"id": "gen-abc123xyz",
"model": "google/gemma-2-9b-it:free",
"object": "chat.completion",
"created": 1711234567,
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! Bonjour! Hola!"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 8,
"total_tokens": 20
}
}
Key fields:
id (gen-...) -- use this to query generation stats via GET /api/v1/generation?id=gen-abc123xyz
model -- confirms which model actually served the request
usage -- token counts for cost calculation
finish_reason -- stop (complete), length (hit max_tokens), tool_calls (function call)
Python Example
from openai import OpenAI
import os
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://your-app.com", "X-Title": "My App"},
)
response = client.chat.completions.create(
model="google/gemma-2-9b-it:free",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is OpenRouter in one sentence?"},
],
max_tokens=100,
)
print(response.choices[0].message.content)
print(f"Model: {response.model}")
print(f"Tokens: {response.usage.prompt_tokens} prompt + {response.usage.completion_tokens} completion")
TypeScript Example
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey: process.env.OPENROUTER_API_KEY,
defaultHeaders: { "HTTP-Referer": "https://your-app.com", "X-Title": "My App" },
});
const res = await client.chat.completions.create({
model: "google/gemma-2-9b-it:free",
messages: [{ role: "user", content: "What is OpenRouter in one sentence?" }],
max_tokens: 100,
});
console.log(res.choices[0].message.content);
console.log(`Model: ${res.model} | Tokens: ${res.usage?.total_tokens}`);
Try Different Models
models_to_try = [
"google/gemma-2-9b-it:free",
"meta-llama/llama-3.1-8b-instruct",
"anthropic/claude-3.5-sonnet",
"openai/gpt-4o",
"openrouter/auto",
]
for model_id in models_to_try:
try:
r = client.chat.completions.create(
model=model_id,
messages=[{"role": "user", "content": "Hi"}],
max_tokens=10,
)
print(f"{model_id}: {r.choices[0].message.content}")
except Exception as e:
print(f"{model_id}: {e}")
Check Generation Cost
curl -s "https://openrouter.ai/api/v1/generation?id=gen-abc123xyz" \
-H "Authorization: Bearer $OPENROUTER_API_KEY" | jq '{
model: .data.model,
tokens_prompt: .data.tokens_prompt,
tokens_completion: .data.tokens_completion,
total_cost: .data.total_cost
}'
Output
A successful round-trip produces:
- A chat completion JSON with
choices[0].message.content holding the model's reply, a gen-... request id, the model that actually served the request, and usage token counts
- Console output from the Python/TypeScript examples: the reply text plus model name and prompt/completion token counts
- A cost record from the generation endpoint:
tokens_prompt, tokens_completion, and total_cost for the request
Examples
End-to-end run with the minimal cURL request:
$ curl -s https://openrouter.ai/api/v1/chat/completions ... | jq .choices[0].message.content
"Hello! Bonjour! Hola!"
The Python and TypeScript sections above are the same request in SDK form; expected console output:
OpenRouter is a unified API gateway that routes requests to 400+ LLMs.
Model: google/gemma-2-9b-it:free
Tokens: 21 prompt + 17 completion
More worked examples (cURL with full expected response, SDK variants): references/examples.md.
Error Handling
| HTTP | Cause | Fix |
|---|
| 401 | Invalid or missing API key | Verify sk-or-v1-... key is exported |
| 402 | Insufficient credits for paid model | Add credits or use a :free model |
| 404 | Wrong base URL or invalid model ID | Use https://openrouter.ai/api/v1; check model ID at /api/v1/models |
| 400 | Malformed JSON or missing messages | Ensure messages array has objects with role and content |
Enterprise Considerations
- Always set
max_tokens to prevent unbounded completions
- Use
HTTP-Referer and X-Title headers for usage attribution in dashboards
- Query
/api/v1/generation?id= for async cost auditing
- Test with free models first, then switch to paid models for production
References