Skip to content

Migrating an existing integration

One line changes. Nothing downstream does.

The migration is a base URL and a model id. Everything the SDK hands back afterwards — the streaming frames, the usage block, the tool-call structure, the error envelope — has the shape your code already parses, which is why this is an afternoon rather than a sprint.

Priced 36–42% below the model publisher's own rate, with both rates printed on the same row.

  • No platform fee
  • OpenAI SDK compatible
  • Pay as you go
  • 7 models callable today

Send me the migration checklist

The endpoint-by-endpoint compatibility list, the model id mapping, and the things that genuinely do differ — written down rather than discovered during a cutover.

One email, no sequence. Or skip the email and create an account for $5 in credit.

The objection is effort, not price

Nobody stays on an expensive rate card because they like it. They stay because the integration works, the retries are tuned, the error handling has been beaten into shape by production, and none of that is written down anywhere except in the code. The cost of switching is not the switch — it is the risk that something subtle in the response shape breaks a path that only fires at three in the morning.

So the design constraint here was compatibility rather than cleverness. This is an OpenAI-compatible endpoint: the request body is the same body, the streaming response is the same sequence of server-sent events terminated the same way, the usage block appears in the same place with the same field names, and errors come back in the same envelope with codes your handler can branch on. Any framework built on the OpenAI SDK — which is most of them — inherits that compatibility without knowing anything about us.

What follows is the honest version of the migration: the line that changes, the things you should check before you trust it, and the specific behaviours that are not identical. A migration guide that claims everything is identical is a migration guide that gets found out during the cutover.

What the one line is worth

Effort is only half of the decision; the other half is what the effort buys. Every model below is priced under its own publisher's list rate, and the gap is on the row rather than in a footnote. DeepSeek V4 Flash runs $0.090/M input and $0.18/M output against a published $0.14/M and $0.28/M.

Read the output column first if you generate more than you read, and the input column first if you summarise, classify or retrieve. The spread between models in this table is wider than the spread between vendors for any one model, which is the argument for making the switch cheap enough that you can afford to try more than one.

Output rates, ours against the publisher's
Output rates, ours against the publisher'sOutput is where a generation-heavy application spends, so it is the column to check first when the switch is being justified on cost. On output, DeepSeek V4 Flash is the lowest at $0.18 per 1M tokens and Kimi K3 the highest at $9.00 per 1M tokens, a 50x spread. Every rate here runs 36–42% below the model publisher's own.Kimi K2.6$2.30Kimi K2.6, official$4.00Kimi K3$9.00Kimi K3, official$15.00GLM-5.2$2.55GLM-5.2, official$4.40DeepSeek V4 Pro$0.55DeepSeek V4 Pro, official$0.87DeepSeek V4 Flash$0.18DeepSeek V4 Flash, official$0.28
Output is where a generation-heavy application spends, so it is the column to check first when the switch is being justified on cost. On output, DeepSeek V4 Flash is the lowest at $0.18 per 1M tokens and Kimi K3 the highest at $9.00 per 1M tokens, a 50x spread. Every rate here runs 36–42% below the model publisher's own.Our published rates, read from the same table the API bills from.
ModelInputOutputCached inputPublisher’s rateBelow publisher
Kimi K2.6$0.55/M$2.30/M$0.11/M$0.95 / $4.00/M42%
Kimi K3$1.85/M$9.00/M$0.37/M$3.00 / $15.00/M40%
GLM-5.2$0.82/M$2.55/M$0.16/M$1.40 / $4.40/M42%
DeepSeek V4 Pro$0.28/M$0.55/M$0.055/M$0.43 / $0.87/M37%
DeepSeek V4 Flash$0.090/M$0.18/M$0.018/M$0.14 / $0.28/M36%
Every text model callable through the endpoint today.

Cached input is billed automatically when a prefix repeats — no header, no flag, no plan. It is not in this table, so a repeated-prefix workload pays less than the figures above suggest.

Price the switch before you make it

If you are building the case for spending an afternoon on this, the number that wins the argument is a monthly figure rather than a per-token one. Put your volume in and the calculator prices it across every model we serve.

The result is shareable as a URL, which exists specifically because the person who approves the change is usually not the person reading this page. Send the link rather than a screenshot of it.

M
M
0% of input cached

An agent or chat loop resends the same system prompt on every turn. That repeated prefix is billed at the cached rate, and on most models that is a fifth of the standard input rate. Set this to zero if every request you send is different.

Cheapest for this shape of work
$12.60/mo on DeepSeek V4 Flash
Against the official rate
$19.6036%

That is $7.00 a month, or $84.00 a year, on the same tokens through the same model.

An estimate, not a quote — but it is computed from the same rate table the API bills from, so the only assumptions in it are the ones you set above. Third-party figures are those providers’ own published rates; where a provider does not publish one, the cell is blank rather than guessed.

The actual diff

Three changes, in the order to make them. The first is the only one that touches code you did not write yourself.

  1. 1

    Base URL

    Set the client's base URL to https://router.xark.io/api/v1. In the OpenAI SDK that is a constructor argument in every language binding; in a framework it is usually one configuration value. Nothing else about the client changes, including how you pass the key.

  2. 2

    Model id

    Ids are namespaced by publisher — moonshotai/kimi-k3, z-ai/glm-5.2, deepseek/deepseek-v4-flash. The bare model name is also accepted, so an integration that predates the namespacing keeps resolving. Unknown ids fail with a specific error rather than a substitution.

  3. 3

    Shadow, then cut over

    Run both clients against a slice of live traffic, diff the outputs on your own evaluation set, and compare cost per request in the dashboard. Cut over when the comparison holds. Rolling back is the same one line in reverse, which is the property that makes this safe to try.

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://router.xark.io/api/v1"   # ← the only line that changes
)

response = client.chat.completions.create(
    model="moonshotai/kimi-k3",
    messages=[{"role": "user", "content": "Hello"}]
)
The same change in Python, Node and cURL.

Everything after that line is unchanged: the same method, the same message array, the same streaming iterator, the same usage fields on the response.

Send me the migration checklist

The endpoint-by-endpoint compatibility list, the model id mapping, and the things that genuinely do differ — written down rather than discovered during a cutover.

One email, no sequence. Or skip the email and create an account for $5 in credit.

What is identical, and what is not

The compatible surface is the one your code touches on every request. The differences are real but small, and all of them are things you would rather read here than find in an incident.

Identical: request and response shape
Chat completions take the same body and return the same object, including the choices array, finish reasons and the usage block. Streaming emits the same server-sent event sequence and the same terminating frame.
Identical: tool and function calling
Tool definitions, tool_choice, and the assistant message carrying tool calls all match the shape the SDK expects, so an agent loop written against OpenAI runs unmodified.
Identical: error envelope
Errors return the same nested error object with a type and a code. Rate limiting comes back as a 429 with rate_limit_exceeded, which is deliberately distinct from the 429 with insufficient_credits you get when the balance is empty.
Different: the model catalogue
Open-weight models only, 7 callable of 24 catalogued. If your code names a closed model, that path has no destination here and has to stay where it is.
Different: no automatic fallback
We do not retry a failed request against a different model. Substituting a model changes the output, the price and the licence terms of what you receive, so the request fails with an honest status your code can branch on instead.
Different: no bring-your-own-key
Routing your own negotiated provider contract through us is designed and on the roadmap but not shipped. Today every request bills against your prepaid balance at the published rate.

The rollback plan, written before the migration

The reason a one-line migration is safe is not that it always works. It is that it is symmetrical: reverting is the same edit in the other direction, with no schema to unwind, no data to move and no contract to exit. Keep the old client construction behind whatever configuration switch you already use for environments, and the rollback is a deploy rather than a project.

Keep the old provider's key valid for a billing cycle after the cutover. It costs nothing on a pay-as-you-go account and it removes the only genuinely irreversible step from the plan.

What engineers say about the switch

Reserved for quotes from engineers who have done the migration and agreed to describe how long it took.

No customer quote below has been approved for publication yet. Every card in this section is sample copy held in place until a real one replaces it, and none of it is offered as an endorsement.

  • Sample — pending customer approval

    Sample placeholder text, awaiting an approved customer quote about how long the base-URL change actually took.

    Backend engineer, B2B SaaS

  • Sample — pending customer approval

    Sample placeholder text, awaiting an approved customer quote about running a coding agent against a repeated system prompt.

    Founding engineer, developer tools company

  • Sample — pending customer approval

    Sample placeholder text, awaiting an approved customer quote about moving open-weight traffic off a general-purpose router.

    Infrastructure engineer, analytics company

The migration, unedited

A screen recording of the change being made in a real project and the first request succeeding, with nothing cut between the edit and the response.

Video not published yet

One-line migration, start to finish

Planned: a single take showing the edit, the run and the response object, with no claims added in narration.

This space is reserved at its final size, so publishing the recording will not move anything below it.

Compatibility questions

The specific ones, answered specifically. Vague reassurance about compatibility is how a migration goes wrong.

Does the official OpenAI SDK work unmodified?

Yes. Set base_url in Python or baseURL in Node and everything else is unchanged: the same client methods, the same request body, the same response object, the same streaming iterator. The SDK does not need to know which host it is talking to.

What about LangChain, LlamaIndex or the Vercel AI SDK?

They are built on the OpenAI-compatible surface, so they work through the same configuration change — whichever base-URL option that framework exposes. Nothing framework-specific is required on our side, and no plugin has to be installed.

Does streaming behave the same way?

Yes. Responses stream as the same sequence of server-sent events, chunks carry the same delta structure, and the stream terminates with the same sentinel frame. Code that accumulates deltas or renders tokens as they arrive needs no changes.

Do function and tool calls work?

Yes. Tool definitions, tool_choice and the assistant message carrying tool calls all match the shape the SDK expects, which is what makes an existing agent loop run unmodified. This is the compatibility that matters most for agent workloads and it is tested as such.

Is the usage block on the response?

Yes, in the same place with the same field names, so any cost accounting you already do keeps working. Per-request cost is also recorded in the dashboard, which is usually the easier place to compare two providers during a shadow period.

What do error responses look like?

The same nested error envelope with a type and a machine-readable code. Rate limiting returns 429 with rate_limit_exceeded; an empty balance returns 429 with insufficient_credits. They are distinct on purpose, because the correct response to the first is to back off and the correct response to the second is to top up.

How do I map my current model ids?

Ids are namespaced by publisher, like z-ai/glm-5.2. The bare model name and the site's URL slug both resolve too, so a lookup that predates the namespacing keeps working. Matching is exact and case-sensitive — a lookup that quietly normalises eventually resolves two different models to the same row.

What happens if I request a model you do not serve?

The request fails with a specific error naming the model. We do not substitute. 7 of 24 catalogued models are callable today and every model page and pricing row states which is which, so the mapping can be checked before the code is written rather than after.

Can I run both providers at once during the migration?

Yes, and it is the recommended approach. Instantiate a second client with the new base URL, route a percentage of traffic to it, and compare outputs and cost per request before moving the rest. Both accounts are pay as you go, so running in parallel costs only the traffic you send.

How do I roll back?

Change the base URL back. There is no schema migration, no data to move and no contract to exit, which is what makes the switch worth attempting on a slice of traffic rather than planning as a project. Keep the previous provider's key valid for one billing cycle and the rollback stays a deploy.

Are there rate limits I should design around?

Rate limiting returns a standard 429 with a machine-readable code, so existing backoff logic applies unchanged. Your prepaid balance is the other ceiling and it is enforced on every request, which means a runaway loop stops at what you have already paid rather than at an invoice.

Will the outputs be identical to what I get today?

For the same model with the same weights, the answer should be very close, but quantisation, context handling and sampling defaults can differ between any two hosts of the same weights. That is the reason for a shadow period against your own evaluation set rather than a claim from us that nothing changes.

How long does this actually take?

The edit is one line. The honest estimate for the whole exercise is however long your evaluation set takes to run twice, plus the shadow period you are comfortable with. New accounts get $5 in credit and no card is required, so the first pass costs nothing.

Try it on one service before you commit anything

The change is symmetrical and the account is pay as you go, so the worst case is an afternoon and a rollback deploy.

We guarantee at least 20% below the model publisher's own rate on every model we serve. The smallest discount in the catalogue today is 36%, so the guarantee has room in it by design.

Send me the migration checklist

The endpoint-by-endpoint compatibility list, the model id mapping, and the things that genuinely do differ — written down rather than discovered during a cutover.

One email, no sequence. Or skip the email and create an account for $5 in credit.

Related: Migration guide · API reference · Model catalogue