OpenAI-compatible API setup: your first request

One base URL, one key, three request shapes. Which endpoint a model answers on is a property of the model, not a setting you choose.

One host, three request shapes

Everything is served from https://token-share.app. What differs per model is the path and the auth header, and both are fixed by the model rather than by you: Anthropic-shaped models answer on /v1/messages with x-api-key, OpenAI-shaped models answer on /v1/responses with Authorization: Bearer, and Grok and Gemini answer on /v1/chat/completions, also with Authorization: Bearer.

The catalog names the route for every id. claude-sonnet-5 is /v1/messages, gpt-5.6-terra is /v1/responses, grok-4.6 and gemini-3-flash are /v1/chat/completions. Sending a model to the wrong shape does not silently work — the request is rejected before it leaves the proxy.

One key covers all three. There is no per-provider credential to manage, no organization header, and no separate account per vendor.

The same key against all three shapes. Only the path, the auth header and the model id change.
export TOKEN_SHARE_KEY="sk-..."

# Anthropic-shaped
curl https://token-share.app/v1/messages \
  -H "x-api-key: $TOKEN_SHARE_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "max_tokens": 256,
    "messages": [{"role": "user", "content": "Say hello in one line."}]
  }'

# OpenAI-shaped
curl https://token-share.app/v1/responses \
  -H "Authorization: Bearer $TOKEN_SHARE_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "gpt-5.6-terra",
    "input": "Say hello in one line."
  }'

# Chat completions
curl https://token-share.app/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN_SHARE_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "grok-4.6",
    "messages": [{"role": "user", "content": "Say hello in one line."}]
  }'

Discovering what you can call

GET /v1/models returns the catalog. It is answered by the proxy itself rather than forwarded, so the list you read is exactly the allowlist your requests are checked against — a model that appears there is callable, and one that does not will be refused.

The response shape follows your headers. Send anthropic-version and you get the Anthropic listing shape; send nothing and you get the OpenAI one. GET /v1/models/{id} returns a single entry the same way.

Model ids are ours, not the upstream vendor's marketing names. They are what the allowlist compares against, so copy them from the catalog rather than from a vendor changelog.

Confirming a request actually worked

Every response carries x-token-share-request-id. It is generated per request and exposed via CORS, so it is the handle to quote when something needs looking into. Log it next to your own request id and a support question stops being a guessing game.

You can also send x-client-request-id with your own correlation value. It is kept alongside the trace for debugging and is never used as the trace key, so replaying the same value does not collide with your earlier requests.

A rejected request is rejected early and cheaply. Auth, model allowlist and balance checks all run before anything is forwarded upstream, which is why a bad model id costs you nothing but a round trip.

Common questions

Can I point the official OpenAI or Anthropic SDK at this?
Yes. Set the SDK's base URL to https://token-share.app and its key to your token-share key, then use a model id from this catalog. The endpoint paths and header names are the ones each SDK already sends, which is what makes the swap a configuration change rather than a rewrite.
What happens if I send a model id to the wrong endpoint?
The proxy checks the model against the catalog allowlist before forwarding, so an id it does not recognise on that path comes back as an HTTP 400 naming the model. Check the id's route in GET /v1/models — the catalog records one route per model.
Do I need the anthropic-version header?
Send it on /v1/messages, as the Anthropic SDK does; 2023-06-01 is the value the examples here use. On /v1/models it doubles as a format switch: present, and the catalog answers in the Anthropic listing shape instead of the OpenAI one.