Your First LLM Call from Python

This page walks you through the absolute minimum: install a library, set up your key securely, and send your first prompt.


Step 1: Install the OpenRouter SDK

OpenRouter is compatible with the standard openai Python library, but it also has its own Python SDK. In this example we use the dedicated OpenRouter SDK.

pip install openrouter

This gives us a Python client designed specifically for OpenRouter.


Step 2: Keep Your API Key Safe

Your API key is like a password that controls access to your account and billing. If it leaks, anyone can use your credits — or worse, rack up charges.

:warning: Never hard-code your API key in a Python script. If you commit it to Git, it’s exposed forever (even if you delete it later).

Method 1: Environment Variables

Set your key as an environment variable so it stays outside your code:

On macOS / Linux (terminal):

export OPENROUTER_API_KEY="sk-or-v1-your-key-here"

:bulb: Add this line to your ~/.zshrc or ~/.bashrc to make it permanent. This is simple but assume you will use that variable in every project.

Method 2: Using a .env file (recommended for projects):

Create a file named .env in your project folder:

OPENROUTER_API_KEY=sk-or-v1-your-key-here

Then install python-dotenv to load it automatically:

pip install python-dotenv

:bulb: Add .env to your .gitignore file immediately:

echo ".env" >> .gitignore

Step 3: Hello, World

Create a file called hello.py:

import os
from openrouter import OpenRouter

# Load the key from an environment variable
api_key = os.environ.get("OPENROUTER_API_KEY")
if not api_key:
    raise RuntimeError("OPENROUTER_API_KEY not set")

# Create an OpenRouter client
with OpenRouter(api_key=api_key) as open_router:
    # Send a message
    response = open_router.chat.send(
        model="openai/gpt-4.1-nano",   # cheap and fast for testing
        messages=[
            {   
                "role": "system",
                "content": "You are a concise assistant. Be accurate, friendly, and brief. Do not use markdown headings. Keep the reply under 80 words. Format your answer to be UNIX terminal friendly (new lines, ascii art if needed)"
            },
            {
                "role": "user",
                "content": "Say hello, tell me what model you are, and give one surprisingly useful Python tip."
            }
        ],
        stream=False,
    )

    # Print the reply
    print(response.choices[0].message.content)

Run it:

python hello.py

Expected output (exact wording will vary):

hello!
I am ChatGPT, based on the GPT-4 model.

Python tip: Use `_` as a throwaway variable for unused values in unpacking.

Example:
 
  a, _, c = (1, 2, 3)  # Ignore the second value
 

Step 4: Check the Cost

OpenRouter returns usage information with the response. This includes token counts and the cost of the call in OpenRouter credits.

Add this inside the with OpenRouter(...) block, after printing the reply:

if response.usage:
    print("\nUsage:")
    print(f"Prompt tokens: {response.usage.prompt_tokens}")
    print(f"Completion tokens: {response.usage.completion_tokens}")
    print(f"Total tokens: {response.usage.total_tokens}")

    cost = getattr(response.usage, "cost", None)
    if isinstance(cost, (int, float)):
        print(f"Cost: {cost:.8f} credits")

:bulb: Token count is useful because most models charge based on how many tokens you send and receive. A token is roughly a word fragment, not exactly a word.

Inside the same OpenRouter client, you can also fetch usage using the generation ID. This is useful if you want to audit a call later:

generation = open_router.generations.get_generation(id=response.id)

print("Generation ID:", generation.data.id)
print("Model:", generation.data.model)
print("Prompt tokens:", generation.data.tokens_prompt)
print("Completion tokens:", generation.data.tokens_completion)
print("Total cost (credits):", generation.data.total_cost)

For a single script, response.usage is usually enough. The generation endpoint is useful when you want to audit previous calls or log costs more carefully.

You can also monitor spending in your OpenRouter account dashboard.


Step 5: Switching Models

Check the models available (and their cost) from openrouter.ai/models

One of the best things about OpenRouter: switch models by changing one string.

# Try Claude instead — everything else stays the same
response = open_router.chat.send(
    model="anthropic/claude-3.5-haiku",   # only this line changed
    messages=[
        {"role": "user", "content": "Say hello and tell me what model you are."}
    ],
    stream=False,
)

Available models you can try (check openrouter.ai/models for the full, up-to-date list):

Model string Provider Cost (approx.)
openai/gpt-4.1-nano OpenAI Very cheap
openai/gpt-4o OpenAI Moderate
anthropic/claude-3.5-haiku Anthropic Cheap
anthropic/claude-sonnet-4-20250514 Anthropic Moderate
google/gemini-2.5-flash Google Free tier available
meta-llama/llama-4-maverick Meta Cheap
deepseek/deepseek-chat DeepSeek Very cheap

:bulb: For testing and development, start with openai/gpt-4.1-nano or google/gemini-2.5-flash — they cost fractions of a cent per call.


If you installed python-dotenv, your script can load the .env file automatically, so you don’t need to set environment variables manually:

import os
from dotenv import load_dotenv
from openrouter import OpenRouter

load_dotenv()  # loads .env from the current directory

with OpenRouter(api_key=os.environ["OPENROUTER_API_KEY"]) as open_router:
    response = open_router.chat.send(
        model="openai/gpt-4.1-nano",
        messages=[
            {"role": "user", "content": "Say hello from a .env file."}
        ],
        stream=False,
    )

    print(response.choices[0].message.content)

:arrow_right: This is a convenient approach for small projects and notebooks.


What’s Next?

Now that you can send a single prompt, the next page shows you how to build something genuinely useful: batch-processing images into an HTML gallery with AI-generated descriptions.


Previous submodule: