From Portkey

Migrate from Portkey

Switch from Portkey to doteb. Same OpenAI-compatible API, no virtual keys or special headers, with managed and self-hosted deployment options.

Published

Portkey wraps your provider calls in virtual keys, config IDs, and x-portkey-* headers. doteb keeps the OpenAI-compatible interface but drops the extra ceremony: standard Bearer auth, provider keys managed in a dashboard, and managed or self-hosted deployment options. Migration is mostly a base URL change.

Quick Migration

Both services are OpenAI-compatible, so the core change is the base URL and dropping Portkey's custom headers:

1- const baseURL = "https://api.portkey.ai/v1";2- // plus x-portkey-api-key and x-portkey-virtual-key headers3+ const baseURL = "https://api.doteb.com/v1";4
5- const apiKey = process.env.PORTKEY_API_KEY;6+ const apiKey = process.env.LLM_GATEWAY_API_KEY;  // standard Bearer auth

Why Teams Switch to doteb

What You Get Portkey doteb
OpenAI-compatible API Yes Yes
Custom headers / virtual keys Required for provider routing Not needed
Self-hosting Gateway/router only Full platform deployment
Automatic provider routing Manual config Live scoring, automatic
Response caching Simple + semantic Built-in, one toggle
Image & video generation Limited Same API as chat
Pricing Usage/seat-based tiers 5% platform fee, or 0% with your keys

Want a feature-by-feature breakdown? See doteb vs Portkey.

Migration Steps

1. Get Your doteb API Key

Sign up at doteb.com/signup and create an API key from your dashboard.

2. Map Your Models

doteb supports two model ID formats:

Root Model IDs (without provider prefix) — uses smart routing to automatically select the best provider based on uptime, throughput, price, and latency:

1gpt-5.22claude-opus-4-5-202511013gemini-3-flash-preview

Provider-Prefixed Model IDs — routes to a specific provider with automatic failover if uptime drops:

1openai/gpt-5.22anthropic/claude-opus-4-5-202511013google-ai-studio/gemini-3-flash-preview

In Portkey, the provider is usually selected by the virtual key or config attached to the request. With doteb, you select the provider in the model ID itself (or let smart routing choose) — there's no separate virtual key to manage.

For more on routing behavior, see the routing documentation.

3. Update Your Code

Python with OpenAI SDK

1from openai import OpenAI2
3# Before (Portkey via OpenAI SDK)4from portkey_ai import createHeaders5
6client = OpenAI(7    base_url="https://api.portkey.ai/v1",8    api_key="dummy",9    default_headers=createHeaders(10        api_key=os.environ["PORTKEY_API_KEY"],11        virtual_key=os.environ["PORTKEY_VIRTUAL_KEY"],12    ),13)14
15# After (doteb) - no custom headers, no virtual key16client = OpenAI(17    base_url="https://api.doteb.com/v1",18    api_key=os.environ["LLM_GATEWAY_API_KEY"],19)20
21response = client.chat.completions.create(22    model="gpt-5.2",  # or "openai/gpt-5.2" to target a specific provider23    messages=[{"role": "user", "content": "Hello!"}],24)

Python with the Portkey SDK

If you use the native portkey-ai client, swap to the OpenAI SDK pointed at doteb:

1# Before (native Portkey SDK)2from portkey_ai import Portkey3
4portkey = Portkey(5    api_key=os.environ["PORTKEY_API_KEY"],6    virtual_key=os.environ["PORTKEY_VIRTUAL_KEY"],7)8
9response = portkey.chat.completions.create(10    model="gpt-5.2",11    messages=[{"role": "user", "content": "Hello!"}],12)13
14# After (doteb via the standard OpenAI SDK)15from openai import OpenAI16
17client = OpenAI(18    base_url="https://api.doteb.com/v1",19    api_key=os.environ["LLM_GATEWAY_API_KEY"],20)21
22response = client.chat.completions.create(23    model="gpt-5.2",24    messages=[{"role": "user", "content": "Hello!"}],25)

TypeScript/JavaScript

1import OpenAI from "openai";2
3// Before (Portkey via OpenAI SDK)4import { createHeaders } from "portkey-ai";5
6const client = new OpenAI({7  baseURL: "https://api.portkey.ai/v1",8  apiKey: "dummy",9  defaultHeaders: createHeaders({10    apiKey: process.env.PORTKEY_API_KEY,11    virtualKey: process.env.PORTKEY_VIRTUAL_KEY,12  }),13});14
15// After (doteb) - standard Bearer auth, no extra headers16const llmgateway = new OpenAI({17  baseURL: "https://api.doteb.com/v1",18  apiKey: process.env.LLM_GATEWAY_API_KEY,19});20
21const completion = await llmgateway.chat.completions.create({22  model: "gpt-5.2", // or "openai/gpt-5.2" to target a specific provider23  messages: [{ role: "user", content: "Hello!" }],24});

cURL

1# Before (Portkey)2curl https://api.portkey.ai/v1/chat/completions \3  -H "x-portkey-api-key: $PORTKEY_API_KEY" \4  -H "x-portkey-virtual-key: $PORTKEY_VIRTUAL_KEY" \5  -H "Content-Type: application/json" \6  -d '{7    "model": "gpt-5.2",8    "messages": [{"role": "user", "content": "Hello!"}]9  }'10
11# After (doteb) - single Authorization header12curl https://api.doteb.com/v1/chat/completions \13  -H "Authorization: Bearer $LLM_GATEWAY_API_KEY" \14  -H "Content-Type: application/json" \15  -d '{16    "model": "gpt-5.2",17    "messages": [{"role": "user", "content": "Hello!"}]18  }'19# Use "openai/gpt-5.2" to target a specific provider

4. Replace Virtual Keys and Configs

Portkey routes through virtual keys (one per provider credential) and config objects. doteb replaces both:

  • Virtual keys → Provider Keys. Add your provider API keys once in the dashboard under Settings > Provider Keys. Bring your own keys and pay a 0% gateway markup, or use doteb's default keys.
  • Configs → model IDs + routing. Provider selection, fallbacks, and load balancing are handled by the model ID and built-in smart routing — there's no separate config object to maintain.

Streaming Support

Streaming works identically:

1from openai import OpenAI2
3client = OpenAI(4    base_url="https://api.doteb.com/v1",5    api_key=os.environ["LLM_GATEWAY_API_KEY"],6)7
8stream = client.chat.completions.create(9    model="openai/gpt-5.2",10    messages=[{"role": "user", "content": "Write a story"}],11    stream=True,12)13
14for chunk in stream:15    if chunk.choices[0].delta.content:16        print(chunk.choices[0].delta.content, end="")

Function/Tool Calling

Tool calling carries over unchanged — it's standard OpenAI-format tools:

1tools = [{2    "type": "function",3    "function": {4        "name": "get_weather",5        "description": "Get the weather for a location",6        "parameters": {7            "type": "object",8            "properties": {"location": {"type": "string"}},9            "required": ["location"],10        },11    },12}]13
14response = client.chat.completions.create(15    model="openai/gpt-5.2",16    messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],17    tools=tools,18)

What Changes After Migration

  • No more virtual keys or x-portkey-* headers — standard Bearer auth and provider keys in a dashboard
  • Automatic routing — providers scored on live uptime, throughput, price, and latency instead of static configs
  • Caching is one toggle — no semantic-cache setup required to get repeat-request savings
  • Generative media included — image and video models through the same API and billing
  • Full-platform self-hosting — run the full deployment, not just the router

Self-Hosting doteb

Prefer to run it yourself? Follow the self-hosting guide or use the deployment package supplied for your environment.

See the self-hosting guide for production deployment with a single Docker image.

Full Comparison

Want a detailed breakdown of all features? Check out our doteb vs Portkey comparison page.

Need Help?