Migration tutorial
OpenAI-compatible APIs

Migrating From DeepSeek Direct to a Multi-Provider API After the Price Hike

A price change is a useful architecture test. If every request is hard-coded to one base URL, a rate-card change becomes an application incident. This guide shows a measured migration from direct DeepSeek calls to a provider-neutral layer that can route to DeepSeek, OpenRouter, AIWave, or another approved endpoint.

The goal is not to hide provider prices or promise that one gateway is always less expensive. The goal is to make route selection, token accounting, retries, and reviewable configuration explicit. The dated DeepSeek rows used here are the 2026-08-17 official peak/off-peak snapshot; verify the official documentation before changing a production budget.

1. Freeze the current contract before moving it

Start with a request inventory. Capture model name, input and output token counts, streaming mode, tool calls, JSON mode, timeout, retry count, and the response fields your application actually reads. Export a small replay set with redacted content. This is your migration oracle: a route is not equivalent merely because it returns HTTP 200.

CapabilityDeepSeek directOpenRouterAIWaveMigration decision
Base URLDeepSeek OpenAI-compatible endpointGateway endpoint with provider routingAIWave OpenAI-compatible endpointInject via environment, never inline
BillingOfficial peak/off-peak rows; billed by providerGateway/provider route price; inspect the selected modelUnified AIWave route price, no peak-window calculation for its published DeepSeek routesRecord provider, model, and rate snapshot
Model coverageDeepSeek modelsMultiple providers behind one API25+ Chinese AI models through one accountUse an internal capability map
Failure surfaceProvider limits, timeouts, rate changesGateway plus upstream limitsGateway plus selected-channel limitsBound retries and classify errors
Data handlingReview direct provider policyReview gateway and upstream policyReview AIWave Zero Data Retention documentation for your routeRecord an approved route policy

OpenRouter and AIWave are not interchangeable labels. OpenRouter is a gateway with its own routing and billing presentation; AIWave is a multi-model API focused on Chinese model access and a unified dollar bill. Keep both as explicit provider adapters so the choice can be tested rather than implied.

2. Put the provider behind one client

The first code change should be boring: move the base URL, key, and model map into configuration. This lets you replay the same request against two routes without editing business logic.

import os
from openai import OpenAI

PROVIDERS = {
    "deepseek": {"base_url": "https://api.deepseek.com", "model": "deepseek-v4-flash", "key": os.environ.get("DEEPSEEK_API_KEY")},
    "aiwave": {"base_url": "https://api.aiwave.live/v1", "model": "deepseek-v4-flash", "key": os.environ.get("AIWAVE_API_KEY")},
}

def client_for(provider):
    cfg = PROVIDERS[provider]
    return OpenAI(api_key=cfg["key"], base_url=cfg["base_url"])

response = client_for("aiwave").chat.completions.create(model=PROVIDERS["aiwave"]["model"], messages=[{"role":"user","content":"Summarize this incident."}])
print(response.choices[0].message.content)

Use the actual base URL and model aliases documented by your account. The important property is dependency injection: tests can pass a fake client, and production can select a provider from policy rather than from scattered string literals.

3. Add a bounded fallback chain

A fallback is a controlled second attempt, not an infinite retry. Classify errors first. A validation error should not be sent to three providers; a timeout or an upstream 5xx may be retriable. Keep a deadline so a user does not wait through every route.

import time

def complete_with_fallback(request, routes, deadline_s=18):
    started = time.monotonic(); visited = set(); last_error = None
    for route in routes:
        if route in visited or time.monotonic() - started > deadline_s: break
        visited.add(route)
        try:
            result = send(route, request, timeout=max(2, deadline_s - (time.monotonic() - started)))
            return result, {"route": route, "attempts": len(visited)}
        except (TimeoutError, UpstreamUnavailable) as exc:
            last_error = exc
    raise RuntimeError(f"bounded fallback exhausted: {last_error}")

Log the route, model, attempt number, latency, token usage, and error class. Do not log the prompt body by default. A small event ledger lets finance compare direct peak exposure with gateway usage without copying sensitive content into a new system.

4. Route by task, not by vendor loyalty

Use a capability map with a quality gate. Flash can handle short classification, extraction, and ordinary summaries; Pro is a better candidate for long reasoning chains and difficult code review. A second provider can cover capacity or policy constraints. The route decision should be deterministic enough to test.

ROUTES = {"short_summary":["aiwave:deepseek-v4-flash","direct:deepseek-v4-flash"],"long_reasoning":["aiwave:deepseek-v4-pro","direct:deepseek-v4-pro"],"structured_extract":["aiwave:deepseek-v4-flash","aiwave:qwen3-coder"]}

def route_for(task, tokens):
    if task == "long_reasoning" or tokens > 90000: return ROUTES["long_reasoning"]
    return ROUTES.get(task, ROUTES["short_summary"])

At AIWave, one account can cover DeepSeek, Kimi, Qwen, and GLM routes. That is useful when you need a second model without introducing a second application contract. The model catalog and API documentation are the right places to confirm aliases and request behavior.

5. Make the price change measurable

For direct DeepSeek, tag each event with the Beijing peak-window flag and the dated rate snapshot. For an AIWave route, tag the event with the unified AIWave price. Do not compare only request counts: output tokens are three times input tokens in the dated peak rows, and cache-hit input has its own rate.

Ledger fieldWhy it matters
provider, modelSeparates direct, gateway, and AIWave routes.
input_tokens, output_tokensPrevents request count from standing in for spend.
cache_hit_tokensApplies the correct cache row instead of the miss row.
peak_window, rate_dateMakes the August 17 schedule auditable.
fallback_attempt, latency_msShows the operational cost of resilience.

A migration is complete when the same replay set has equivalent quality, acceptable latency, and a cost report that can explain every route. Update pricing assumptions and your own provider snapshots together, then review the diff with engineering and finance.

6. A staged rollout plan

  1. Shadow: send redacted replay traffic to the candidate route and compare structure, tool calls, and token counts.
  2. Canary: move a small, observable percentage of one task class.
  3. Guardrail: cap output tokens, retries, and daily spend per route.
  4. Expand: increase traffic only after quality and latency SLOs hold.
  5. Reconcile: compare provider invoices with your event ledger.

Do not make the migration a one-night rewrite. The price change is a reason to introduce a provider boundary that will help with future model releases, capacity events, and policy reviews. Link the implementation to the AIWave engineering archive so future maintainers can find the assumptions.

7. Questions the replay set must answer

Can both routes stream the same event shape? Do tool calls preserve names and JSON arguments? Does a long context stay within the same limit? What happens when a provider returns a rate-limit response after partial output? How are cache-hit tokens reported? Put each question in a test, attach a pass/fail result, and retain the redacted request ID.

A migration that cannot answer these questions is only a URL swap. A migration that can answer them is a provider boundary with an audit trail. That boundary is what makes the next price change an ordinary configuration review rather than an emergency rewrite.

FAQ

Can I keep the OpenAI SDK when migrating?

Yes. Keep the client abstraction and change the base URL, key source, model map, and error policy. Test streaming, tool calls, and JSON output separately.

Should OpenRouter or AIWave be the first fallback?

Use the provider that matches your requirements for routing control, billing, model coverage, and data handling. Measure both with the same workload replay.

How should I handle DeepSeek peak windows?

Treat the direct provider's Beijing-time windows as a routing signal, then record the actual billed route. A unified AIWave route can simplify estimation when predictable pricing matters.

Do I need to rewrite prompts?

Usually not for basic chat completions, but validate system messages, tool schemas, streaming events, context limits, and reasoning controls before switching production traffic.

How do I prevent a fallback loop?

Attach a request ID, cap attempts, keep a visited-provider set, and stop after a bounded deadline. Log the final provider and reason for every fallback.

Continue the implementation path: Chat Completions documentation, model catalog, AIWave pricing, AIWave engineering articles, and RAG use cases.