Skip to content
openrouter-team-setup logo

OpenRouter Team Setup

openrouter-team-setup

Configure OpenRouter for multi-user teams with per-user keys, budget

SKILL.md

Full skill instructions

OpenRouter Team Setup

Overview

OpenRouter supports team usage through per-user API keys with individual credit limits, management keys for programmatic key provisioning, and usage attribution via headers. This skill covers key provisioning, per-user budgets, usage tracking, and governance policies for multi-user deployments.

Key Provisioning via Management API

import os, requests

MGMT_KEY = os.environ["OPENROUTER_MGMT_KEY"]  # Management key (cannot call completions)

def create_team_key(name: str, credit_limit: float = 25.0) -> dict:
    """Create a new API key for a team member."""
    resp = requests.post(
        "https://openrouter.ai/​api/​v1/​keys",
        headers={"Authorization": f"Bearer {MGMT_KEY}"},
        json={"name": name, "limit": credit_limit},
    )
    resp.raise_for_status()
    data = resp.json()["data"]
    return {
        "key": data["key"],       # sk-or-v1-... (shown once)
        "hash": data["key_hash"], # For later identification
        "name": name,
        "limit": credit_limit,
    }

def list_team_keys() -> list[dict]:
    """List all keys with usage and limits."""
    resp = requests.get(
        "https://openrouter.ai/​api/​v1/​keys",
        headers={"Authorization": f"Bearer {MGMT_KEY}"},
    )
    return [
        {
            "name": k.get("name"),
            "hash": k.get("key_hash"),
            "usage": k.get("usage", 0),
            "limit": k.get("limit"),
            "is_free_tier": k.get("is_free_tier", False),
        }
        for k in resp.json().get("data", [])
    ]

def delete_team_key(key_hash: str):
    """Revoke a team member's key."""
    resp = requests.delete(
        f"https://openrouter.ai/​api/​v1/​keys/​{key_hash}",
        headers={"Authorization": f"Bearer {MGMT_KEY}"},
    )
    resp.raise_for_status()

# Provision keys for the team
for member in ["alice-backend", "bob-frontend", "carol-ml"]:
    key_info = create_team_key(member, credit_limit=50.0)
    print(f"Created key for {member}: {key_info['key'][:20]}...")

Shared Key with User Attribution

from openai import OpenAI

# Alternative: single shared key with user identification via headers
def get_client_for_user(user_id: str) -> OpenAI:
    """Create a client that attributes usage to a specific user."""
    return OpenAI(
        base_url="https://openrouter.ai/​api/​v1",
        api_key=os.environ["OPENROUTER_API_KEY"],
        default_headers={
            "HTTP-Referer": "https://my-app.com",
            "X-Title": f"my-app:{user_id}",  # User shows in dashboard
        },
    )

# Each user's requests appear under their X-Title in the dashboard
alice_client = get_client_for_user("alice")
response = alice_client.chat.completions.create(
    model="openai/​gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
    max_tokens=100,
)

Per-User Budget Enforcement

import sqlite3, time

def init_team_db(db_path: str = "team_usage.db"):
    conn = sqlite3.connect(db_path)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS user_usage (
            user_id TEXT NOT NULL,
            date TEXT NOT NULL,
            total_cost REAL DEFAULT 0,
            request_count INTEGER DEFAULT 0,
            PRIMARY KEY (user_id, date)
        )
    """)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS user_budgets (
            user_id TEXT PRIMARY KEY,
            daily_limit REAL NOT NULL,
            model_allowlist TEXT  -- JSON array of allowed model IDs
        )
    """)
    conn.commit()
    return conn

def check_user_budget(conn, user_id: str) -> bool:
    """Check if user is within their daily budget."""
    today = time.strftime("%Y-%m-%d")
    row = conn.execute(
        "SELECT u.total_cost, b.daily_limit FROM user_usage u "
        "JOIN user_budgets b ON u.user_id = b.user_id "
        "WHERE u.user_id = ? AND u.date = ?",
        (user_id, today),
    ).fetchone()

    if not row:
        return True  # No usage yet today
    return row[0] < row[1]

def record_user_usage(conn, user_id: str, cost: float):
    """Record a request's cost for a user."""
    today = time.strftime("%Y-%m-%d")
    conn.execute(
        """INSERT INTO user_usage (user_id, date, total_cost, request_count)
           VALUES (?, ?, ?, 1)
           ON CONFLICT(user_id, date) DO UPDATE SET
           total_cost = total_cost + ?, request_count = request_count + 1""",
        (user_id, today, cost, cost),
    )
    conn.commit()

Team Usage Report

def team_usage_report(conn) -> list[dict]:
    """Generate a team usage report for the current week."""
    rows = conn.execute("""
        SELECT u.user_id, SUM(u.total_cost) as weekly_cost,
               SUM(u.request_count) as requests,
               b.daily_limit
        FROM user_usage u
        JOIN user_budgets b ON u.user_id = b.user_id
        WHERE u.date >= date('now', '-7 days')
        GROUP BY u.user_id
        ORDER BY weekly_cost DESC
    """).fetchall()

    return [
        {
            "user": row[0],
            "weekly_cost": round(row[1], 4),
            "requests": row[2],
            "daily_limit": row[3],
        }
        for row in rows
    ]

Team Key Dashboard Script

#!/​bin/​bash
# Show all team keys with usage

echo "=== OpenRouter Team Keys ==="
curl -s https://openrouter.ai/​api/​v1/​keys \
  -H "Authorization: Bearer $OPENROUTER_MGMT_KEY" | \
  jq -r '.data[] | "\(.name)\t$\(.usage // 0 | tostring)\t/\t$\(.limit // "unlimited" | tostring)"' | \
  column -t -s $'\t'

echo ""
echo "=== Total Usage ==="
curl -s https://openrouter.ai/​api/​v1/​keys \
  -H "Authorization: Bearer $OPENROUTER_MGMT_KEY" | \
  jq '.data | map(.usage // 0) | add | "Total spend: $\(.)"'

Model Governance

# Define which models each tier can use
MODEL_ALLOWLISTS = {
    "free": ["google/​gemma-2-9b-it:free"],
    "basic": ["openai/​gpt-4o-mini", "meta-llama/​llama-3.1-8b-instruct"],
    "pro": ["openai/​gpt-4o-mini", "openai/​gpt-4o", "anthropic/​claude-3.5-sonnet"],
    "enterprise": None,  # None = all models allowed
}

def enforce_model_policy(user_tier: str, requested_model: str) -> str:
    """Enforce model allowlist based on user tier."""
    allowlist = MODEL_ALLOWLISTS.get(user_tier)
    if allowlist is None:
        return requested_model  # Enterprise: unrestricted
    if requested_model in allowlist:
        return requested_model
    # Downgrade to best allowed model
    return allowlist[-1]

Error Handling

ErrorCauseFix
Management key 403Using API key instead of management keyManagement keys are separate -- create one at openrouter.ai/​keys
User exceeds budgetNo per-user limits setCreate individual keys with credit limits
Attribution missingNo X-Title headerEnforce header in shared client wrapper
Key sprawlToo many keys to trackImplement key lifecycle management; revoke unused keys

Enterprise Considerations

  • Use management keys for programmatic key provisioning -- they can create/​list/​delete API keys but cannot make completions
  • Set per-key credit limits to prevent any single user from exhausting shared budget
  • Use X-Title header with user identifiers for dashboard-level attribution
  • Implement model allowlists per user tier to control access to expensive models
  • Build weekly usage reports for cost visibility and anomaly detection
  • Rotate team keys on a schedule; revoke keys for departed team members immediately

References

More DevOps & CI/CD skills

Project scaffolding, deployment configuration, and CI/CD setup for Google ADK agents.

6K 472.6K
View

Set up tracing, logging, and monitoring for deployed ADK agents across Cloud Trace, BigQuery, and third-party platforms.

6K 472.6K
View

Enterprise Azure infrastructure architect generating Bicep or Terraform from workload descriptions.

1.5K 454.3K
View
azure-kubernetes logo
DevOps & CI/CD

azure-kubernetes

Plan and configure production-ready Azure Kubernetes Service clusters with Day-0 and Day-1 best practices.

1.5K 447.1K
View

Raw mechanical interfaces fusing Swiss typographic print with military terminal aesthetics. Rigid grids, extreme type scale contrast, utilitarian color, analog degradation effects. For data-heavy dashboards, portfolios, or editorial sites that need to feel like declassified blueprints.

92.7K 354.9K
View
just-scrape logo
DevOps & CI/CD

just-scrape

Web search, scraping, extraction, crawling, and monitoring via ScrapeGraph AI CLI.

69 245K
View

Skill for working with Firebase Hosting (Classic). Use this when you want to deploy static web apps, Single Page Apps (SPAs), or simple microservices. Do NOT use for Firebase App Hosting.

462 159.7K
View

Deploy and manage web apps with Firebase App Hosting. Use this skill when deploying Next.js/Angular apps with backends.

462 159.1K
View
deploy-to-vercel logo
DevOps & CI/CD

deploy-to-vercel

Deploy applications and websites to Vercel. Use when the user requests deployment actions like "deploy my app", "deploy and give me the link", "push this live", or "create a preview deployment".

31.9K 146.6K
View
programmatic-seo logo
DevOps & CI/CD

programmatic-seo

Build SEO-optimized pages at scale using templates, data, and proven playbook patterns.

53.3K 140.1K
View

Design and build isolated, reusable Convex backend components with clear boundaries and app-facing wrappers.

63 123.5K
View

Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. "deploy to vercel", "set up vercel", "add environment variables to vercel".

31.9K 116.1K
View

Coding & apps AI tools

Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View
Imagica logo
Coding & apps

Imagica

Build a no-code AI app in minutes.

Freemium
View
E
Coding & apps

Emergent.sh

An IDE for code migration from legacy to modern frameworks through coding agents.

Freemium
View
Wonder Dynamics logo
Coding & apps

Wonder Dynamics

Automate CGI animation in live-action scenes

Paid
View
M
Coding & apps

Mixo

Launch a website in seconds with AI.

Paid
View
AI Code Convert logo
Coding & apps

AI Code Convert

Streamline Your Coding Experience with AI Code Helper

Free
View