Search⌘ K
AI Features

Making Your First Request

Explore the fundamental process of making your first API request with OpenRouter. Learn to obtain an API key, construct requests compatible with multiple providers, switch models by changing a simple parameter, and understand the response structure for effective integration and cost management.

In the last lesson, we established why OpenRouter is a strategic choice that solves ecosystem fragmentation by acting as a unified routing layer. Now, we move from theory to practice.

This lesson walks through the fundamental mechanics of making your first API call. We will get an API key, construct a request, send it to two different providers with a single line of code change, and learn how to interpret the response.

Getting your API key

Before you can make any requests, you need to authenticate your application. This is done using an API key. You can create a new key from your OpenRouter settings page. Once you create a key, its full value is only shown once. Copy it immediately and store it securely, as you would a password.

For security and operational hygiene, follow these best practices when managing your keys:

  • Assign descriptive names: Name your keys clearly to track their usage (e.g., prod-text-generation-v1-2024-q3).

  • Set credit limits: Apply a specific spend limit to each key to cap financial exposure and prevent runaway costs.

  • Keep keys out of code: Never hardcode your API key in your source code. Use an environment variable (OPENROUTER_API_KEY) or a secrets management system.

  • Use guardrails: For fine-grained control, you can apply “Guardrails” to a key. This allows you to enforce budgets, restrict usage to an approved list of models, or enforce a zero-data-retention policy.

The anatomy of an API request

All standard chat completion requests are sent to a single endpoint:

https://openrouter.ai/api/v1/chat/completions

A request consists of two main parts: the headers and the body.

Headers: Authentication and attribution

The request must include a few key headers to be processed correctly.

  • Authorization: Bearer YOUR_API_KEY: This is required for authentication. Replace YOUR_API_KEY with the key you just generated.

  • Content-Type: application/json: This tells the server that you are sending data in JSON format.

While not required, OpenRouter highly recommends including two additional headers for app attribution:

  • HTTP-Referer: https://your-app.com: Your application’s primary URL.

  • X-Title: Your App Name: Your application’s display name.

Including these headers allows your app to appear on OpenRouter’s public leaderboards and gives you access to more detailed analytics about your application’s model usage.

Body: The power of compatibility

OpenRouter’s request body is fully compatible with the OpenAI API. Any library, framework, or tool built to work with OpenAI will work with OpenRouter with minimal changes.

The body is a JSON object with two primary fields:

  • model: A string identifying the model you want to use (e.g., openai/gpt-5.2).

  • messages: An array of message objects that form the conversation history.

Let’s put this all together and make a call.

The code below accesses paid models. By default, a free API key will result in 403 errors.

Making the call and switching models

Here is a simple Python script using the requests library to call OpenRouter. Notice how the model name is stored in a variable.

Python 3.10.4
import os
import json
import requests
# Load your API key from an environment variable
api_key = os.getenv("OPENROUTER_API_KEY")
your_site_url = "http://localhost:3000" # Your website or app URL
your_app_name = "My First OpenRouter App" # Your app name
def call_openrouter(model_name: str, prompt: str):
"""Sends a request to the OpenRouter API and prints the response."""
response = requests.post(
url="https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"HTTP-Referer": your_site_url,
"X-Title": your_app_name,
},
json={
"model": model_name,
"messages": [
{"role": "user", "content": prompt}
]
}
)
if response.status_code == 200:
print(f"--- Response from {model_name} ---")
print(json.dumps(response.json(), indent=2))
else:
print(f"Error: Received status code {response.status_code}")
print(response.text)
# 1. Call OpenAI's GPT-5.2
call_openrouter(
model_name="openai/gpt-5.2",
prompt="What is the capital of France?"
)
# 2. Call Anthropic's Claude Haiku 4.5 with the *exact same code*
call_openrouter(
model_name="anthropic/claude-haiku-4.5",
prompt="What is the capital of France?"
)

This script demonstrates the core value proposition of OpenRouter. We called two different models from two different providers by changing a single string: the same endpoint, the same headers, the same code.

Deconstructing the response

When you run the script, you will get back a JSON response that also follows the OpenAI structure.

{
"id": "gen-1772775724-guljboPk6lJyej3tglzy",
"object": "chat.completion",
"created": 1772775724,
"model": "openai/gpt-5.2-20251211",
"provider": "OpenAI",
"choices": [
{
"index": 0,
"logprobs": null,
"finish_reason": "stop",
"native_finish_reason": "completed",
"message": {
"role": "assistant",
"content": "Paris.",
"refusal": null,
"reasoning": null
}
}
],
"usage": {
"prompt_tokens": 13,
"completion_tokens": 6,
"total_tokens": 19,
"cost": 0,
"is_byok": true,
"prompt_tokens_details": {
"cached_tokens": 0,
"cache_write_tokens": 0,
"audio_tokens": 0,
"video_tokens": 0
},
"cost_details": {
"upstream_inference_cost": 0.00010675,
"upstream_inference_prompt_cost": 2.275e-05,
"upstream_inference_completions_cost": 8.4e-05
},
"completion_tokens_details": {
"reasoning_tokens": 0,
"image_tokens": 0,
"audio_tokens": 0
}
}
}
A sample response

Let’s look at the key parts:

  • model: This field confirms which model ultimately served the request. Notice how the provider (anthropic) is part of the model’s unique name.

  • choices: This is an array containing the model’s response(s). The main content is inside choices[0].message.content.

  • usage: This object is vital for cost management. It tells you exactly how many tokens were consumed for the prompt and the completion. Because pricing is per-token, this is the ground truth for how much a request costs.

Finding detailed routing information

The model field tells you which model and provider succeeded. To find out which providers were attempted during a fallback, query the /api/v1/generation endpoint using the id from the response. It returns raw metadata, including an array of every provider that was attempted and the HTTP status code each returned. This is useful for debugging complex production routing and fallback strategies.

Copy the generation ID from the output of the previous widget and set it as the generation_id variable in the code widget below:

Python 3.10.4
import os
import json
import requests
# Load your API key from an environment variable
api_key = os.getenv("OPENROUTER_API_KEY")
your_site_url = "http://localhost:3000" # Your website or app URL
your_app_name = "My First OpenRouter App" # Your app name
generation_id = "" # Add your generation ID here
def get_generation_metadata(generation_id: str) -> dict:
"""Fetches routing metadata for a completed request using its generation ID."""
response = requests.get(
url=f"https://openrouter.ai/api/v1/generation?id={generation_id}",
headers={
"Authorization": f"Bearer {api_key}",
},
)
if response.status_code == 200:
return response.json()
else:
raise RuntimeError(
f"Failed to fetch generation metadata: {response.status_code} {response.text}"
)
# Query the generation endpoint for routing details
metadata = get_generation_metadata(generation_id)
print(json.dumps(metadata, indent=2))

Conclusion

One endpoint, one authorization header, one model parameter. That consistency is what makes it practical to swap providers without touching the rest of your code. In the next lesson, we will explore the full model marketplace: how models are named, what metadata they expose, and how to select the right one for a given task.