← Blog
May 25, 2024

OpenRouter: One API for All LLMs

How we use OpenRouter to access Claude, GPT-4, Gemini, and dozens of other models through a single API. Cost optimization and model selection strategies.

openrouterLLMsAPIAIintegration

Managing API keys for OpenAI, Anthropic, Google, Meta, Mistral…

It gets tedious.

OpenRouter provides a single API that routes to any model. We use it extensively.

The Value Proposition

Before OpenRouter

// Different clients for each provider
const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY });
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_KEY });
const google = new GoogleAI({ apiKey: process.env.GOOGLE_KEY });

// Different APIs for each
const gptResponse = await openai.chat.completions.create({...});
const claudeResponse = await anthropic.messages.create({...});
const geminiResponse = await google.generateContent({...});

After OpenRouter

// One client
const openrouter = new OpenRouter({ apiKey: process.env.OPENROUTER_KEY });

// Unified API for any model
const response = await openrouter.chat.completions.create({
  model: 'anthropic/claude-3-opus',  // or 'openai/gpt-4-turbo'
  messages: [...]
});

Available Models

OpenRouter provides access to:

Commercial Models

Open Source Models

Specialized Models

Cost Comparison

OpenRouter often provides better pricing:

Model Direct Pricing OpenRouter Savings
GPT-4 $30/1M tokens $30/1M Same
Claude 3 Opus $75/1M $45/1M 40%
Gemini Pro $0.50/1M $0.125/1M 75%
Llama 3 70B N/A (host yourself) $0.90/1M Easy access

Implementation

Basic Usage

import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://openrouter.ai/api/v1',
  apiKey: process.env.OPENROUTER_API_KEY,
  defaultHeaders: {
    'HTTP-Referer': 'https://aegntic.ai',
    'X-Title': 'Aegntic Platform'
  }
});

async function complete(prompt: string, model = 'anthropic/claude-3-sonnet') {
  const response = await client.chat.completions.create({
    model,
    messages: [{ role: 'user', content: prompt }]
  });
  
  return response.choices[0].message.content;
}

With Streaming

async function* streamComplete(prompt: string, model: string) {
  const stream = await client.chat.completions.create({
    model,
    messages: [{ role: 'user', content: prompt }],
    stream: true
  });
  
  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content;
    if (content) {
      yield content;
    }
  }
}

// Usage
for await (const text of streamComplete('Tell me a story', 'anthropic/claude-3-haiku')) {
  process.stdout.write(text);
}

Model Selection Logic

interface ModelConfig {
  name: string;
  costPer1k: number;
  maxContext: number;
  speed: 'fast' | 'medium' | 'slow';
  quality: 'high' | 'medium' | 'low';
}

const models: Record<string, ModelConfig> = {
  'anthropic/claude-3-opus': {
    name: 'Claude 3 Opus',
    costPer1k: 0.015,
    maxContext: 200000,
    speed: 'slow',
    quality: 'high'
  },
  'anthropic/claude-3-haiku': {
    name: 'Claude 3 Haiku',
    costPer1k: 0.00025,
    maxContext: 200000,
    speed: 'fast',
    quality: 'medium'
  },
  // ... more models
};

function selectModel(requirements: {
  budgetPer1k: number;
  minQuality: 'high' | 'medium' | 'low';
  contextNeeded: number;
}): string {
  const suitable = Object.entries(models)
    .filter(([_, config]) => {
      return config.costPer1k <= requirements.budgetPer1k &&
             qualityRank(config.quality) >= qualityRank(requirements.minQuality) &&
             config.maxContext >= requirements.contextNeeded;
    })
    .sort((a, b) => a[1].costPer1k - b[1].costPer1k);
    
  return suitable[0]?.[0] || 'anthropic/claude-3-haiku';
}

Cost Optimization Strategies

1. Tiered Model Usage

async function smartComplete(prompt: string, complexity: 'simple' | 'medium' | 'complex') {
  const model = {
    simple: 'anthropic/claude-3-haiku',      // $0.25/1M
    medium: 'anthropic/claude-3-sonnet',     // $3/1M
    complex: 'anthropic/claude-3-opus'       // $15/1M
  }[complexity];
  
  return complete(prompt, model);
}

2. Automatic Fallback

async function completeWithFallback(prompt: string, models: string[]) {
  for (const model of models) {
    try {
      return await complete(prompt, model);
    } catch (error) {
      console.warn(`Model ${model} failed, trying next...`);
    }
  }
  throw new Error('All models failed');
}

// Usage
const result = await completeWithFallback(prompt, [
  'anthropic/claude-3-opus',
  'openai/gpt-4-turbo',
  'google/gemini-pro'
]);

3. Caching Responses

import { createHash } from 'crypto';

const cache = new Map<string, string>();

async function cachedComplete(prompt: string, model: string) {
  const hash = createHash('sha256')
    .update(`${model}:${prompt}`)
    .digest('hex');
    
  if (cache.has(hash)) {
    return cache.get(hash);
  }
  
  const result = await complete(prompt, model);
  cache.set(hash, result);
  return result;
}

Monitoring Usage

Track Costs

interface UsageLog {
  model: string;
  promptTokens: number;
  completionTokens: number;
  cost: number;
  timestamp: Date;
}

async function trackUsage(
  model: string, 
  usage: { prompt_tokens: number; completion_tokens: number }
) {
  const pricing = await getModelPricing(model);
  const cost = 
    (usage.prompt_tokens * pricing.promptCost) +
    (usage.completion_tokens * pricing.completionCost);
    
  await logUsage({
    model,
    promptTokens: usage.prompt_tokens,
    completionTokens: usage.completion_tokens,
    cost,
    timestamp: new Date()
  });
}

Dashboard

Track:

Benefits We’ve Seen

1. Simplified Development

One SDK, one API key, one interface.

2. Cost Reduction

Smart model routing reduced our LLM costs by 40%.

3. Reliability

When one provider has issues, route to another.

4. Experimentation

Easy to try new models without new integrations.


OpenRouter is central to our AI infrastructure. For how we orchestrate model selection, see MCP Revolution.

All posts Work with me