cd /news/large-language-models/one-endpoint-many-models-switching-b… · home › topics › large-language-models › article
[ARTICLE · art-156179] src=dev.to ↗ pub= topic=large-language-models verified=true sentiment=· neutral

One endpoint, many models: switching between GPT, Claude and DeepSeek and logging what every call costs

A developer built a small Python harness that routes requests to GPT, Claude and DeepSeek through a single OpenAI-format Chat Completions endpoint, using the openai SDK's configurable base_url and per-model pricing tables to log the dollar cost of every call. The harness computes spend from the usage object's prompt, cached and completion token counts, and the developer notes that tokenizers and default thinking modes make naive per-token estimates unreliable — Anthropic's Claude 4.7+ tokenizer yields roughly 30% more tokens for the same text, and DeepSeek's Flash model runs in thinking mode by default. A sample 2,000-token prompt with a 500-token answer costs $0.00375 on GPT-5.4 mini, $0.009 on Claude Sonnet 5 and $0.0012 on DeepSeek Flash at peak hours, per list prices checked October 23, 2026.

by read4 min views5 publishedOct 23, 2026

The OpenAI Chat Completions format has quietly become the common plug of the LLM world. DeepSeek documents an OpenAI-format base URL for its own API, most gateways speak it, and the official openai Python SDK lets you point base_url anywhere. So switching from GPT to Claude to DeepSeek looks like changing one string.

It mostly is. But "mostly" hides the part that shows up on your invoice: the same prompt is a different number of tokens on different models, and some models think before answering and bill you for it. Below is a small harness for comparing models on real prompts instead of on vibes.

Keep the endpoint and key in environment variables and treat the model as a plain argument.

import os
from openai import OpenAI

client = OpenAI(
    base_url=os.environ["LLM_BASE_URL"],  # provider or gateway, e.g. https://api.deepseek.com
    api_key=os.environ["LLM_API_KEY"],
)

def ask(model: str, prompt: str, **extra):
    resp = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        **extra,
    )
    return resp.choices[0].message.content, resp.usage

One thing does change between endpoints: model IDs. Talking to OpenAI directly you write gpt-5.4-mini; many gateways namespace it as openai/gpt-5.4-mini. Keep IDs in config, not scattered through the code.

usage into money Every response carries a usage object, and that is the only number worth trusting. In the OpenAI format, prompt_tokens already includes cached tokens (broken out in prompt_tokens_details.cached_tokens), and completion_tokens already includes reasoning tokens (broken out in completion_tokens_details.reasoning_tokens). So the math is: fresh input at the input price, cached input at the cached price, all completion tokens at the output price.

Here are list prices checked on October 23, 2026:

PRICES = {  # USD per 1M tokens, provider list prices as of 2026-10-23
    "openai/gpt-5.4-mini":        {"in": 0.75, "cached": 0.075, "out": 4.50},
    "anthropic/claude-sonnet-5":  {"in": 2.00, "cached": 0.20,  "out": 10.00},
    "deepseek/deepseek-v4-flash": {"in": 0.30, "cached": 0.006, "out": 1.20},  # peak hours
}

def cost_usd(model: str, usage) -> float:
    p = PRICES[model]
    details = getattr(usage, "prompt_tokens_details", None)
    cached = (getattr(details, "cached_tokens", 0) or 0) if details else 0
    fresh = usage.prompt_tokens - cached
    return (fresh * p["in"] + cached * p["cached"]
            + usage.completion_tokens * p["out"]) / 1_000_000

A quick sanity check with a 2,000-token prompt and a 500-token answer: GPT-5.4 mini comes to $0.00375 per call, Claude Sonnet 5 to $0.009, DeepSeek Flash to $0.0012 at peak hours. Over 10,000 calls that is $37.50, $90 and $12.

Those numbers are almost certainly wrong for your workload. The 2,000 tokens will not be 2,000 on every model: Anthropic says the tokenizer in Claude 4.7 and later produces roughly 30% more tokens for the same text. And the 500-token answer assumes no hidden reasoning, while DeepSeek's docs say its Flash model runs in thinking mode by default.

The table also skips Anthropic's cache writes (1.25x the input price) and DeepSeek's off-peak rates (half of peak; peak is 01:00–04:00 and 06:00–10:00 UTC on weekdays). Add fields if they matter to you.

Take 20 to 50 prompts from real traffic, run each through every candidate model, and write one CSV row per call:

import csv, time

prompts = [line.strip() for line in open("prompts.txt", encoding="utf-8") if line.strip()]

with open("compare.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerow(["model", "prompt_tokens", "completion_tokens",
                "reasoning_tokens", "seconds", "usd", "answer"])
    for model in PRICES:
        for prompt in prompts:
            t0 = time.perf_counter()
            text, usage = ask(model, prompt)
            dt = time.perf_counter() - t0
            cd = getattr(usage, "completion_tokens_details", None)
            reasoning = (getattr(cd, "reasoning_tokens", 0) or 0) if cd else 0
            w.writerow([model, usage.prompt_tokens, usage.completion_tokens,
                        reasoning, round(dt, 2), round(cost_usd(model, usage), 6), text])

Before judging quality, look at three columns. prompt_tokens shows the tokenizer difference on your own text, reasoning_tokens shows which models think when you did not ask them to, and usd gives a real cost per call. The answer sits in the same row, so you judge quality next to price.

Parameters are less portable than the endpoint. Some models reject options that others accept, reasoning models especially around temperature and output limits. Keep a small overrides dict per model and pass it through **extra instead of sprinkling if model.startswith(...) around.

Caching differs too: OpenAI caches prefixes automatically, Anthropic needs cache_control. If cached_tokens stays at zero on a long system prompt, you pay full price for it on every call.

Prices move. Anthropic's pricing page currently notes that Claude Sonnet 5's launch price, announced as introductory, has become the standard one. Date your price table and re-check it before you trust last month's numbers.

And if you go through a gateway, its bill is the truth, not the list price. List prices are fine for comparing models; for real spend, reconcile against the gateway's usage export.

Under 50 lines, and arguments about which model is cheaper turn into a CSV you can sort.

Disclosure: I'm building Heli (getheli.ru), an OpenAI-compatible gateway for developers in Russia that bills in rubles. The code above works with it if you set LLM_BASE_URL=https://getheli.ru/v1, but nothing here depends on it.

This post was drafted with the help of an AI assistant and reviewed and edited by me.

── more in #large-language-models 4 stories · sorted by recency
── more on @openai 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/one-endpoint-many-mo…] indexed:0 read:4min 2026-10-23 · —