← Back to research
Guides12 min readPublished 2026-07-20

One Python Client for GPT, Claude and Gemini with LumeAPI

Use one Python client and LumeAPI key for GPT, Claude, and Gemini. Add model selection, token-cost estimates, request logs, and bounded fallback in production.

By LumeAPI Engineering Team

Multi-Model API hub →

Last verified: July 20, 2026

A Python backend can call GPT, Claude, and Gemini through one OpenAI-compatible LumeAPI client. The base URL, API key, and USD wallet remain the same; the application selects an exact model ID for each request. This removes duplicate provider clients and billing integrations while keeping model choice explicit and auditable.

The same LumeAPI account also covers listed image and video models. Those modalities can require different parameters or asynchronous job flows, so 'one platform' should not be mistaken for one identical request schema across every modality.

Current example models and rates

The following model IDs and USD rates were listed by LumeAPI on July 20, 2026, per 1 million input/output tokens:

FamilyModel IDLumeAPI input / outputPage reference input / outputDisplayed difference
GPTgpt-5.4-mini$0.225 / $1.35$0.75 / $4.5070% lower
Claudeclaude-sonnet-4-6$1.80 / $9.00$3.00 / $15.0040% lower
Geminigemini-3.5-flash$0.90 / $5.40$1.50 / $9.0040% lower

These examples show why 'discounts up to 70%' must be stated carefully. The GPT example reaches 70% against its displayed reference; the Claude and Gemini examples show 40%. Always verify the current model catalog and AI API pricing.

Install and configure one client

bash
python -m pip install --upgrade openai

Set one LumeAPI key:

bash
export LUMEAPI_KEY='your-lumeapi-key'

Create multi_model.py:

python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ['LUMEAPI_KEY'],
    base_url='https://api.lumeapi.site/v1',
    timeout=30.0,
    max_retries=2,
)

MODELS = {
    'gpt': 'gpt-5.4-mini',
    'claude': 'claude-sonnet-4-6',
    'gemini': 'gemini-3.5-flash',
}

def generate(prompt: str, family: str) -> str:
    try:
        model = MODELS[family]
    except KeyError as exc:
        raise ValueError(f'Unsupported model family: {family}') from exc

    response = client.chat.completions.create(
        model=model,
        messages=[{'role': 'user', 'content': prompt}],
    )
    return response.choices[0].message.content or ''

print(generate('Give me three names for an API monitoring service.', 'gpt'))

Changing family changes the model, not the client, key, base URL, or response access path.

Call all three model families

A simple evaluation loop can send the same task to each model:

python
import time

prompt = 'Classify this support ticket as billing, technical, or account: card charged twice.'

for family, model in MODELS.items():
    started = time.perf_counter()
    response = client.chat.completions.create(
        model=model,
        messages=[
            {'role': 'system', 'content': 'Return one category only.'},
            {'role': 'user', 'content': prompt},
        ],
    )
    latency_ms = round((time.perf_counter() - started) * 1000)
    print({
        'family': family,
        'model': model,
        'latency_ms': latency_ms,
        'input_tokens': response.usage.prompt_tokens if response.usage else None,
        'output_tokens': response.usage.completion_tokens if response.usage else None,
        'text': response.choices[0].message.content,
    })

This is an integration test, not enough evidence to name a winner. Use a representative dataset and score task success, format validity, latency, errors, and cost. LumeAPI call logs can then be used to review the gateway-side model, tokens, cost, and latency for each request.

Centralize model policy

Do not let every feature choose arbitrary model strings. Define a model allowlist and map product tasks to internal aliases:

python
TASK_MODELS = {
    'short_reply': 'gpt-5.4-mini',
    'careful_review': 'claude-sonnet-4-6',
    'fast_interactive': 'gemini-3.5-flash',
}

def generate_for_task(task: str, prompt: str) -> str:
    try:
        model = TASK_MODELS[task]
    except KeyError as exc:
        raise ValueError(f'Unknown task: {task}') from exc

    response = client.chat.completions.create(
        model=model,
        messages=[{'role': 'user', 'content': prompt}],
    )
    return response.choices[0].message.content or ''

The mapping is an application policy, not a universal claim that one family is always best for that task. Update it only after evaluating your own data. A server-side allowlist also prevents user input from selecting an unapproved or unexpectedly expensive model.

Estimate cost before sending traffic

Store dated rates next to their verification date:

python
RATES = {
    'gpt-5.4-mini': {'input': 0.225, 'output': 1.35},
    'claude-sonnet-4-6': {'input': 1.80, 'output': 9.00},
    'gemini-3.5-flash': {'input': 0.90, 'output': 5.40},
}

def estimate_cost(model: str, input_tokens: int, output_tokens: int) -> float:
    rates = RATES[model]
    return (
        input_tokens / 1_000_000 * rates['input']
        + output_tokens / 1_000_000 * rates['output']
    )

for model in MODELS.values():
    print(model, f'${estimate_cost(model, 2000, 500):.6f}')

With 2,000 input and 500 output tokens, this table produces estimates of $0.001125 for GPT-5.4 mini, $0.008100 for Claude Sonnet 4.6, and $0.004500 for Gemini 3.5 Flash. That is not a quality ranking. The cheapest failed task is still wasted spend, and a more expensive model may reduce retries or escalation.

Rates change. Keep the verification date, update the table from the live catalog, and use actual wallet/call-log data for billing reconciliation.

Add application logs without exposing prompts

Record operational metadata, not secrets or sensitive content:

python
import json
import time

def generate_with_log(task: str, prompt: str) -> str:
    model = TASK_MODELS[task]
    started = time.perf_counter()
    response = client.chat.completions.create(
        model=model,
        messages=[{'role': 'user', 'content': prompt}],
    )
    latency_ms = round((time.perf_counter() - started) * 1000)

    record = {
        'task': task,
        'model': model,
        'latency_ms': latency_ms,
        'input_tokens': response.usage.prompt_tokens if response.usage else None,
        'output_tokens': response.usage.completion_tokens if response.usage else None,
        'request_id': getattr(response, '_request_id', None),
    }
    print(json.dumps(record))
    return response.choices[0].message.content or ''

Use these application logs together with LumeAPI's per-call logs. Avoid logging API keys, authorization headers, full prompts, model output, or user data unless a documented policy permits it.

Implement a bounded fallback

Fallback should be explicit and limited. Retry only failures that another model can reasonably recover from, and never repeat an external side effect without idempotency.

python
import openai

FALLBACKS = {
    'gpt-5.4-mini': 'gemini-3.5-flash',
    'gemini-3.5-flash': 'gpt-5.4-mini',
}

def generate_with_fallback(prompt: str, primary: str) -> str:
    models = [primary]
    if primary in FALLBACKS:
        models.append(FALLBACKS[primary])

    last_error = None
    for model in models:
        try:
            response = client.chat.completions.create(
                model=model,
                messages=[{'role': 'user', 'content': prompt}],
            )
            return response.choices[0].message.content or ''
        except (openai.RateLimitError, openai.APITimeoutError) as exc:
            last_error = exc

    raise RuntimeError('Primary and fallback models failed.') from last_error

This example shows application-controlled fallback for a read-only generation task. It does not claim that LumeAPI automatically chooses the best model. For a fuller policy, read routing GPT, Claude, and Gemini and the LLM API gateway page.

When one API shape is not enough

A shared Chat Completions shape reduces integration work, but models still differ in behavior and supported features. Test:

  • prompt and system-instruction behavior;
  • streaming event handling;
  • tool-call schemas and loop termination;
  • structured output validation;
  • context and output limits;
  • error bodies and retry rules;
  • latency and cost under realistic concurrency.

Image and video models also belong to the LumeAPI account and wallet, but generation parameters, media inputs, output retrieval, and async status polling can differ. Use their model documentation rather than forcing them through a text-only abstraction.

Common multi-model mistakes

One model ID is used for every task

Make the choice explicit in TASK_MODELS and review it with evaluation data.

Model names come from user input

Use a server-side allowlist. Otherwise a caller may select unavailable, untested, or expensive models.

Costs are compared without output length

Input and output rates differ. Include both, plus retries and validation failures.

Fallback repeats a side effect

Use idempotency and durable workflow state for email, payment, order, or write tools. The fallback example above is for read-only text generation.

The team cannot identify which model answered

Log the exact model on every request and review LumeAPI's call logs. A unified endpoint should improve observability, not hide routing decisions.

Why this architecture matches LumeAPI

LumeAPI's strongest fit is operational consolidation: one key, one endpoint for compatible text calls, one USD wallet, and one place to review request model, tokens, cost, and latency across mainstream model families. Supported GPT rates on the site can be up to 70% below their displayed references, while the Claude and Gemini examples above show 40%.

That same account can extend from LLM text calls to listed image and video generation without opening another platform account. Keep modality-specific interfaces and limitations visible, and link every application feature to the relevant model docs.

Sources and verification

All Python blocks were syntax-checked on July 20, 2026. No customer inference key was used, so this guide does not claim observed model quality, latency, or automatic intelligent routing.