OpenRouter Tutorial: Access 500+ LLMs with One API Key and SDK

TL;DR — OpenRouter tutorial: 500+ models, one API key. OpenRouter Docs: "Unified API, hundreds of models, single endpoint, automatic fallbacks. OpenAI SDK as drop-in replacement. Send requests to /api/v1/chat/completions." OpenRouter Blog: "Get key at openrouter.ai/settings/keys (sk-or- prefix). Point base URL to https://openrouter.ai/api/v1. Pick model slug provider/model. Two value changes, rest of code stays. 300+ models, 70+ providers." OpenRouter Blog: "Two routing layers: provider-level failover (automatic, 30-second outage deprioritization) and model-level fallback (models array, triggers on errors/moderation/rate-limits). Auto Router by NotDiamond. Billed for model that runs." BYOK Docs: "Bring your own provider keys. Prioritized or fallback. Multiple keys per provider. Model/API key/member filters. Drops fee to 5%." Quickstart: "600+ models. Change one string to switch providers. Streaming with stream: true." Learn more with LLM gateway, OpenRouter vs LiteLLM, LiteLLM tutorial, and switch provider.

OpenRouter Docs provides the overview: "OpenRouter provides a unified API that gives you access to hundreds of AI models through a single endpoint, while automatically handling fallbacks and selecting the most cost-effective options."

OpenRouter Blog explains the simplicity: "Any tool that speaks the OpenAI Chat Completions API works with OpenRouter, because OpenRouter exposes that same API. You change two values, the base URL and the key, then pick a model, and the rest of your code stays put. That single endpoint fronts 300+ models across 70+ providers."

OpenRouter Architecture

flowchart TD subgraph App["Your Application"] SDK["OpenAI SDK or OpenRouter SDK
base_url: openrouter.ai/api/v1
api_key: sk-or-v1-xxx"] end subgraph Gateway["OpenRouter Gateway (Cloudflare Edge)"] Endpoint["Unified Endpoint
/api/v1/chat/completions
OpenAI-compatible"] AutoRouter["Auto Router
NotDiamond picks model per prompt
openrouter/auto"] ProviderRouting["Provider Routing
inverse-square weighting
30s outage deprioritization
filter: price/latency/throughput/ZDR"] ModelFallback["Model Fallback
models array in priority order
triggers on errors/moderation/rate-limits"] ProviderFailover["Provider Failover
automatic within model
5xx or rate-limit → next provider"] Credits["Credits & Billing
5.5% fee on credits
5% with BYOK
first 1M requests free"] end subgraph FreeTier["Free Tier"] FreeModels["20+ Free Models
DeepSeek R1, Llama 3.3, Gemma 3
50 req/day (1K with $10)"] end subgraph BYOK["Bring Your Own Key"] Prioritized["Prioritized Keys
tried before OpenRouter endpoints
primary provider keys"] FallbackKeys["Fallback Keys
tried after OpenRouter endpoints
backup keys"] Filters["Filters
model filter, API key filter
member filter"] end subgraph Providers["70+ Providers, 500+ Models"] OpenAI["OpenAI
gpt-4o, o1, o3"] Anthropic["Anthropic
claude-sonnet-4, claude-opus-4"] Google["Google
gemini-2.0-flash, gemini-pro"] Others["70+ Others
Mistral, Meta, Cohere
AWS Bedrock, Azure"] end SDK --> Endpoint Endpoint --> AutoRouter Endpoint --> ProviderRouting Endpoint --> ModelFallback ProviderRouting --> ProviderFailover FreeTier --> Gateway BYOK --> ProviderRouting ProviderFailover --> OpenAI ProviderFailover --> Anthropic ProviderFailover --> Google ProviderFailover --> Others Credits --> Gateway

Routing Options

Situation Use Why
Cheapest reliable Default load balancing Inverse-square weighting handles it
Must hit one provider provider.order + allow_fallbacks: false Hard stop, no silent fallback
Latency-sensitive :nitro suffix Routes for throughput
Cost-capped batch :floor / max_price Routes for price, enforces ceiling
Cross-model resilience models fallback array Survives a whole model going down
Don't know what prompts openrouter/auto NotDiamond picks per prompt

Pricing Tiers

Tier Fee Free Requests Notes
Pay-as-you-go credits 5.5% First 1M/month free $0.80 minimum per purchase
Bring Your Own Key 5.0% First 1M/month free Use your provider keys
Free models $0 50/day (1K with $10) 20 req/min rate limit

Implementation

from dataclasses import dataclass
from typing import Optional
from enum import Enum

class RoutingOption(Enum):
    DEFAULT = "default"
    PROVIDER_ORDER = "provider_order"
    NITRO = "nitro"
    FLOOR = "floor"
    MODELS_FALLBACK = "models_fallback"
    AUTO = "auto"

@dataclass
class OpenRouterTutorialGuide:
    """OpenRouter tutorial implementation guide."""

    def get_setup(self) -> dict:
        """Setup instructions."""
        return {
            "step_1": "Create API key at openrouter.ai/settings/keys",
            "key_format": "sk-or-v1-xxx (starts with sk-or-)",
            "step_2": "Point base_url to https://openrouter.ai/api/v1",
            "step_3": "Set api_key to your OpenRouter key",
            "step_4": "Pick model slug in provider/model format",
            "step_5": "Send chat completion request",
            "browse_models": "openrouter.ai/models or GET /api/v1/models",
            "optional_headers": {
                "HTTP-Referer": "Site URL for rankings on openrouter.ai",
                "X-OpenRouter-Title": "Site name for rankings",
            },
        }

    def get_python_openai_sdk(self) -> str:
        """Python with OpenAI SDK."""
        return (
            "from openai import OpenAI\n"
            "\n"
            "client = OpenAI(\n"
            "    base_url='https://openrouter.ai'\n"
            "        '/api/v1',\n"
            "    api_key='sk-or-v1-xxx',\n"
            "    default_headers={\n"
            "        'HTTP-Referer':\n"
            "            'https://myapp.com',\n"
            "        'X-OpenRouter-Title':\n"
            "            'My App',\n"
            "    },\n"
            ")\n"
            "\n"
            "response = client.chat.completions\\\n"
            "    .create(\n"
            "    model='openai/gpt-4o',\n"
            "    messages=[\n"
            "        {'role': 'user',\n"
            "         'content': 'Hello!'}\n"
            "    ],\n"
            ")\n"
            "print(response.choices[0]\n"
            "      .message.content)"
        )

    def get_python_openrouter_sdk(self) -> str:
        """Python with OpenRouter SDK."""
        return (
            "pip install openrouter\n"
            "\n"
            "from openrouter import OpenRouter\n"
            "\n"
            "client = OpenRouter(\n"
            "    api_key='sk-or-v1-xxx',\n"
            ")\n"
            "\n"
            "response = client.chat.send(\n"
            "    model='anthropic/claude'\n"
            "        '-sonnet-4',\n"
            "    messages=[\n"
            "        {'role': 'user',\n"
            "         'content': 'Hello!'}\n"
            "    ],\n"
            ")\n"
            "print(response.choices[0]\n"
            "      .message.content)"
        )

    def get_typescript(self) -> str:
        """TypeScript with OpenAI SDK."""
        return (
            "import OpenAI from 'openai';\n"
            "\n"
            "const openai = new OpenAI({\n"
            "  baseURL: 'https://openrouter.ai'\n"
            "    + '/api/v1',\n"
            "  apiKey: 'sk-or-v1-xxx',\n"
            "  defaultHeaders: {\n"
            "    'HTTP-Referer':\n"
            "      'https://myapp.com',\n"
            "    'X-OpenRouter-Title':\n"
            "      'My App',\n"
            "  },\n"
            "});\n"
            "\n"
            "const response = await openai\n"
            "  .chat.completions.create({\n"
            "  model: 'openai/gpt-4o',\n"
            "  messages: [\n"
            "    { role: 'user',\n"
            "      content: 'Hello!' }\n"
            "  ],\n"
            "});\n"
            "console.log(response.choices[0]\n"
            "  .message.content);"
        )

    def get_streaming(self) -> str:
        """Streaming responses."""
        return (
            "from openai import OpenAI\n"
            "\n"
            "client = OpenAI(\n"
            "    base_url='https://openrouter.ai'\n"
            "        '/api/v1',\n"
            "    api_key='sk-or-v1-xxx',\n"
            ")\n"
            "\n"
            "stream = client.chat.completions\\\n"
            "    .create(\n"
            "    model='openai/gpt-4o',\n"
            "    messages=[\n"
            "        {'role': 'user',\n"
            "         'content': 'Tell me a story'}\n"
            "    ],\n"
            "    stream=True,\n"
            ")\n"
            "\n"
            "for chunk in stream:\n"
            "    content = chunk.choices[0]\\\n"
            "        .delta.content\n"
            "    if content:\n"
            "        print(content, end='',\n"
            "              flush=True)"
        )

    def get_model_fallback(self) -> str:
        """Model-level fallback."""
        return (
            "from openai import OpenAI\n"
            "\n"
            "client = OpenAI(\n"
            "    base_url='https://openrouter.ai'\n"
            "        '/api/v1',\n"
            "    api_key='sk-or-v1-xxx',\n"
            ")\n"
            "\n"
            "# Model fallback: try primary,\n"
            "# then each fallback in order\n"
            "response = client.chat.completions\\\n"
            "    .create(\n"
            "    model='openai/gpt-4o',\n"
            "    messages=[\n"
            "        {'role': 'user',\n"
            "         'content': 'Hello!'}\n"
            "    ],\n"
            "    extra_body={\n"
            "        'models': [\n"
            "            'openai/gpt-4o',\n"
            "            'anthropic/claude'\n"
            "                '-sonnet-4',\n"
            "            'google/gemini-2.0'\n"
            "                '-flash',\n"
            "        ],\n"
            "    },\n"
            ")\n"
            "# Billed for model that ran\n"
            "# response.model shows which\n"
            "print(f'Served by: '\n"
            "      f'{response.model}')"
        )

    def get_provider_routing(self) -> str:
        """Provider-level routing preferences."""
        return (
            "from openai import OpenAI\n"
            "\n"
            "client = OpenAI(\n"
            "    base_url='https://openrouter.ai'\n"
            "        '/api/v1',\n"
            "    api_key='sk-or-v1-xxx',\n"
            ")\n"
            "\n"
            "# Provider routing preferences\n"
            "response = client.chat.completions\\\n"
            "    .create(\n"
            "    model='openai/gpt-4o',\n"
            "    messages=[\n"
            "        {'role': 'user',\n"
            "         'content': 'Hello!'}\n"
            "    ],\n"
            "    extra_body={\n"
            "        'provider': {\n"
            "            'order': [\n"
            "                'OpenAI',\n"
            "                'Azure',\n"
            "            ],\n"
            "            'allow_fallbacks': False,\n"
            "            'require_parameters': True,\n"
            "            'data_collection':\n"
            "                'deny',\n"
            "            'quantizations': [\n"
            "                'fp8',\n"
            "                'bf16',\n"
            "            ],\n"
            "        },\n"
            "    },\n"
            ")"
        )

    def get_free_models(self) -> dict:
        """Free models usage."""
        return {
            "free_models": [
                "DeepSeek R1 (deepseek/deepseek-r1:free)",
                "Llama 3.3 70B (meta-llama/llama-3.3-70b:free)",
                "Gemma 3 (google/gemma-3:free)",
            ],
            "limits": {
                "requests_per_day": "50 (1,000 with $10 credits)",
                "requests_per_minute": 20,
                "caveat": (
                    "Failed attempts count against "
                    "daily quota. Popular free models "
                    "can get rate-limited at peak times."
                ),
            },
            "usage": (
                "response = client.chat.completions\\\n"
                "    .create(\n"
                "    model='openrouter/free',\n"
                "    messages=[\n"
                "        {'role': 'user',\n"
                "         'content': 'Hello!'}\n"
                "    ],\n"
                ")"
            ),
        }

    def get_byok(self) -> dict:
        """Bring Your Own Key."""
        return {
            "what": (
                "Use your own provider keys with "
                "OpenRouter routing and failover"
            ),
            "setup": [
                "Add provider key in OpenRouter settings",
                "Choose prioritized or fallback",
                "Optional: always use for this provider",
                "Optional: model filter, API key filter, member filter",
            ],
            "prioritized": (
                "Tried before OpenRouter endpoints. "
                "Use for primary provider keys."
            ),
            "fallback": (
                "Tried after OpenRouter endpoints. "
                "Use for backup keys."
            ),
            "default_behavior": (
                "If all BYOK keys fail, OpenRouter "
                "falls back to shared endpoints. "
                "Toggle 'Always use for this provider' "
                "to prevent fallback."
            ),
            "multiple_keys": (
                "Configure multiple BYOK keys for "
                "same provider. Each key produces "
                "its own endpoint copy."
            ),
            "filters": [
                "Model filter — restrict to specific models",
                "API key filter — restrict which OpenRouter keys can use",
                "Member filter — restrict which team members can use",
            ],
            "azure_byok": (
                "Foundry config: api_key, resource_name, "
                "resource_type (ai_foundry or openai)"
            ),
            "fee_reduction": "BYOK drops platform fee from 5.5% to 5%",
        }

    def get_auto_router(self) -> dict:
        """Auto Router by NotDiamond."""
        return {
            "what": (
                "NotDiamond picks the best model "
                "for each prompt automatically"
            ),
            "usage": (
                "response = client.chat.completions\\\n"
                "    .create(\n"
                "    model='openrouter/auto',\n"
                "    messages=[\n"
                "        {'role': 'user',\n"
                "         'content': 'Hello!'}\n"
                "    ],\n"
                ")"
            ),
            "best_for": (
                "When you don't know what prompts "
                "users will send — NotDiamond "
                "selects optimal model per prompt"
            ),
        }

OpenRouter Tutorial Checklist

  • [ ] Create API key at openrouter.ai/settings/keys (starts with sk-or-)
  • [ ] Point base_url to https://openrouter.ai/api/v1
  • [ ] Set api_key to your OpenRouter key
  • [ ] Pick model slug in provider/model format (e.g., openai/gpt-4o, anthropic/claude-sonnet-4)
  • [ ] Browse models at openrouter.ai/models or GET /api/v1/models
  • [ ] Optional headers: HTTP-Referer (site URL), X-OpenRouter-Title (site name)
  • [ ] OpenAI SDK works as drop-in replacement — change base_url and api_key only
  • [ ] OpenRouter SDK: pip install openrouter (Python), @openrouter/sdk (TypeScript)
  • [ ] 500+ models across 70+ providers through one endpoint
  • [ ] Two value changes (base_url, api_key) — rest of code stays put
  • [ ] Streaming: pass stream=True, iterate over chunks with delta.content
  • [ ] Model slug is the only thing you change to switch models
  • [ ] Latest aliases: ~openai/gpt-latest, ~anthropic/claude-sonnet-latest
  • [ ] Provider-level failover: automatic within model, 30-second outage deprioritization
  • [ ] Provider-level failover: on by default (allow_fallbacks: true)
  • [ ] Model-level fallback: pass models array in priority order via extra_body
  • [ ] Model fallback triggers: context-length errors, moderation flags, rate-limiting, downtime
  • [ ] Billed for model that actually runs, not ones that errored — check response.model
  • [ ] Auto Router: use openrouter/auto as model slug — NotDiamond picks per prompt
  • [ ] Provider routing: provider.order with allow_fallbacks: false for hard stop
  • [ ] Provider routing: filter by price, throughput, latency, data policy, ZDR, quantization
  • [ ] :nitro suffix for throughput-optimized routing
  • [ ] :floor / max_price for cost-capped routing
  • [ ] Default load balancing: inverse-square weighting across providers
  • [ ] Free tier: 20+ models at $0, 50 req/day (1,000 with $10 credits), 20 req/min
  • [ ] Free models: DeepSeek R1, Llama 3.3 70B, Gemma 3 — use :free suffix or openrouter/free
  • [ ] Free tier caveats: failed attempts count against quota, peak rate-limits possible
  • [ ] Credits: 5.5% platform fee, $0.80 minimum per purchase
  • [ ] BYOK: 5% fee (reduced from 5.5%), use your own provider keys
  • [ ] BYOK: prioritized (tried first) or fallback (tried after OpenRouter endpoints)
  • [ ] BYOK: default falls back to shared endpoints if all keys fail
  • [ ] BYOK: toggle "Always use for this provider" to prevent fallback
  • [ ] BYOK: multiple keys per provider, each with own endpoint copy
  • [ ] BYOK: model filter, API key filter, member filter for granular control
  • [ ] BYOK: Azure Foundry config with api_key, resource_name, resource_type
  • [ ] First 1M requests each month free (waived)
  • [ ] Failed requests not billed
  • [ ] Token usage in response: promptTokens, completionTokens
  • [ ] OpenRouter keys start with sk-or- (not sk- which is OpenAI)
  • [ ] Store API key in environment variable, not in source code
  • [ ] Read LLM gateway guide for gateway concepts
  • [ ] Read OpenRouter vs LiteLLM for comparison
  • [ ] Read LiteLLM tutorial for self-hosted alternative
  • [ ] Read switch LLM provider for provider migration
  • [ ] Test: API key works with OpenAI SDK pointed at OpenRouter
  • [ ] Test: model switching works by changing only model slug
  • [ ] Test: streaming returns chunks with delta.content
  • [ ] Test: model fallback switches to backup on primary failure
  • [ ] Test: free model returns response within rate limits
  • [ ] Test: BYOK key is tried before OpenRouter shared endpoints
  • [ ] Document API key, model slugs, routing config, and fallback strategy

FAQ

How do you set up OpenRouter?

Create an API key at openrouter.ai/settings/keys, point your OpenAI SDK base_url to https://openrouter.ai/api/v1, and pick a model slug. OpenRouter Docs: "OpenRouter provides a unified API giving access to hundreds of AI models through a single endpoint, automatically handling fallbacks and selecting most cost-effective options. Send standard HTTP requests to /api/v1/chat/completions. Use OpenAI SDK as drop-in replacement by setting base_url." OpenRouter Blog: "Get a key at openrouter.ai/settings/keys. Keys start with sk-or-. Point base URL at https://openrouter.ai/api/v1. Pick a model slug in provider/model form like openai/gpt-4o or anthropic/claude-sonnet-4. The slug is the only thing you change to switch models." Steps: (1) Create key at openrouter.ai/settings/keys. (2) Set base_url to https://openrouter.ai/api/v1. (3) Set api_key to sk-or-v1-xxx. (4) Pick model slug (provider/model format). (5) Send chat completion request. (6) Browse models at openrouter.ai/models.

How do you use OpenRouter with the OpenAI SDK?

Point the OpenAI SDK's base_url to OpenRouter's endpoint and use your OpenRouter API key. OpenRouter Docs: "You can use the OpenAI SDK pointed at OpenRouter as a drop-in replacement. Useful if you have existing code built on the OpenAI SDK and want to access OpenRouter model catalog without changing code structure." OpenRouter Blog: "Any tool that speaks the OpenAI Chat Completions API works with OpenRouter. Change two values: base URL and key. Then pick a model. The rest of your code stays put. That single endpoint fronts 300+ models across 70+ providers." Python: client = OpenAI(base_url='https://openrouter.ai/api/v1', api_key='sk-or-v1-xxx'). TypeScript: const openai = new OpenAI({baseURL: 'https://openrouter.ai/api/v1', apiKey: 'sk-or-v1-xxx'}). Optional headers: HTTP-Referer (site URL for rankings), X-OpenRouter-Title (site name). Model slug format: provider/model (e.g., openai/gpt-4o, anthropic/claude-sonnet-4).

How does OpenRouter model routing and fallback work?

OpenRouter has two layers: provider-level failover (automatic within a model) and model-level fallback (configurable across models). OpenRouter Blog: "One OpenAI-compatible endpoint, one API key, 400+ models across 70+ providers. Provider-level: if chosen provider returns 5xx or rate-limits, OpenRouter falls through to next provider serving that model. On by default (allow_fallbacks: true). Any provider with outage in last 30 seconds gets deprioritized. Model-level: pass models array in priority order. Fallbacks trigger on context-length errors, moderation flags, rate-limiting, and downtime. Billed for model that actually runs, not ones that errored." OpenRouter Docs: "Auto Router powered by NotDiamond picks a model per prompt. Use openrouter/auto as model slug." Routing options: default load balancing (inverse-square weighting), provider.order with allow_fallbacks: false (hard stop), :nitro (throughput), :floor/max_price (price ceiling), models array (cross-model resilience), openrouter/auto (NotDiamond per-prompt).

What free models does OpenRouter offer?

OpenRouter has 20+ free models at $0, capped at 50 requests/day (1,000/day after adding $10 in credits). OpenRouter Blog: "OpenRouter has a free tier. 20+ models run at $0, capped at 50 requests a day and 20 a minute, rising to 1,000 a day once you add $10 in credits. Two caveats: failed attempts still count against daily quota, and popular free models can get rate-limited at peak times. Even so, enough to take an agent end to end before you spend anything on inference." OpenRouter Docs: "Use a free model and stay within the free tier. Free models are the :free variants on the models page." Free models include: DeepSeek R1, Llama 3.3 70B, Gemma 3. Use model slug with :free suffix or openrouter/free for auto-selection. 50 req/day free, 1,000 req/day with $10 credits, 20 req/min rate limit.

How does Bring Your Own Key (BYOK) work with OpenRouter?

BYOK lets you use your own provider keys with OpenRouter's routing and failover on top. OpenRouter BYOK Docs: "OpenRouter supports both OpenRouter credits and BYOK. When you use credits, rate limits are managed by OpenRouter. BYOK keys can be prioritized (tried before OpenRouter endpoints) or fallback (tried after). By default, if all BYOK keys fail, OpenRouter falls back to shared endpoints. Toggle Always use for this provider to prevent fallback. Configure multiple BYOK keys for same provider. Model filter restricts key to specific models. API key filter restricts which OpenRouter keys can use it. Member filter restricts which team members can use it." BYOK setup: (1) Add provider key in settings. (2) Choose prioritized or fallback. (3) Optional: always use for this provider. (4) Optional: model filter, API key filter, member filter. (5) Azure BYOK: Foundry config with api_key, resource_name, resource_type. (6) BYOK drops platform fee from 5.5% to 5%.


Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →