Switching from OpenAI direct: migrating an existing integration

Base URL, key, model id. What carries over unchanged, what needs a second look, and how to verify the swap actually took.

The three things that change

Point the SDK at https://token-share.app, swap the key for your token-share key, and use a model id from this catalog. Everything the SDK does around that — request construction, retries, streaming iteration, tool-call parsing — is unchanged, because the wire format is the one it already speaks.

The model id is the part that is not a mechanical substitution. There is no gpt-4o or o3 here; the OpenAI-family ids are gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4 and gpt-5.4-mini, plus gpt-image-2 for images. Read GET /v1/models to see what is live rather than guessing at a mapping.

One consequence catches people: which endpoint a model answers on is fixed by the model, not chosen by you. The gpt-5.x text models answer on /v1/responses. Grok and Gemini answer on /v1/chat/completions. If your integration is built on chat completions and you want to call gpt-5.6-terra, that is a route change, not just an id change.

The official openai package against this gateway. Only baseURL, apiKey and model differ from a direct integration.
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://token-share.app/v1',
  apiKey: process.env.TOKEN_SHARE_KEY,
});

// gpt-5.x text models answer on /v1/responses
const answer = await client.responses.create({
  model: 'gpt-5.6-terra',
  input: 'Explain what changed in this diff.',
});
console.log(answer.output_text);

// Grok and Gemini answer on /v1/chat/completions
const chat = await client.chat.completions.create({
  model: 'gemini-3-flash',
  messages: [{ role: 'user', content: 'Explain what changed in this diff.' }],
});
console.log(chat.choices[0]?.message?.content);

What your error handling should expect

Errors produced after routing keep the shape of the route you called, so an OpenAI-shaped caller gets an OpenAI-shaped error object. Errors raised before routing — bad key, unknown model, empty balance — use the gateway's own shape, which a strict SDK error parser may not recognise. Branch on the HTTP status first and treat the body as diagnostic text.

The statuses themselves are conventional. 401 for credentials, 402 and 403 for funding, 400 for a model the catalog does not know, 429 for capacity, 503 for availability, 502 when every provider tried has failed. The SDK's built-in retry on 429 and 5xx does the right thing here.

Every response carries x-token-share-request-id. If your existing logging captures OpenAI's x-request-id, capture this one in the same place — it is the handle for looking a request up.

Verifying the swap, and what it costs

Verify with the response, not the config. Read x-token-share-request-id off a live call — if it is present, the request went through this gateway. A stale environment variable in a container is the usual reason a migration looks done and is not.

Pricing is a multiplier on the official per-token rate, and it is per model rather than catalog-wide: as low as 0.1×; Claude at 0.25–0.35×; 0.2× for other mainstream pool models. The OpenAI family alone runs from 0.1× on gpt-5.6-sol to 0.5× on gpt-5.6-luna. When you budget, multiply that model's own official rate by that model's own multiplier.

Because the multiplier is uniform across a model's token buckets, the shape of your bill does not change — an output-heavy workload stays output-heavy. What changes is the absolute number, so a cost model you already trust ports over with the multiplier applied.

Common questions

Do I have to rewrite chat completions code to use the Responses API?
Only for the models that answer there. gpt-5.x text ids are on /v1/responses, so calling one from a chat-completions code path means using the responses method instead. If you would rather not touch that code path yet, the Grok and Gemini ids answer on /v1/chat/completions and need no structural change — that is a real trade between migration effort and which models you can reach.
Does streaming work the same way?
Yes — set stream: true and iterate as you do now. The response body is passed through rather than buffered, so time-to-first-token is not traded away. Usage is accounted from the stream independently of your client, so aborting mid-stream does not change what the request cost.
Can I keep using the same code against both OpenAI and this gateway?
Yes, if base URL, key and model id are configuration rather than literals. The wire format is the same, so a single client with two profiles works. Keep the model id in the same profile as the base URL — an id from one catalog sent to the other is the failure you will hit first.