---
title: "OpenAI, Anthropic and Gemini SDK setup"
canonical: https://uplinkswitch.ai/guides/sdks
generated: 2026-11-04T07:23:29.629Z
source: https://uplinkswitch.ai/guides/sdks
base_url: https://api.uplinkswitch.com/v1
---

[›uplinkswitch](https://uplinkswitch.ai/)

[api.uplinkswitch.com/v1](https://uplinkswitch.ai/models)[Console](https://uplinkswitch.ai/console)

[›uplinkswitch](https://uplinkswitch.ai/)

# OpenAI, Anthropic and Gemini SDKs

A base URL and your key. The rest of your code stays.

Checked 2026-10-21

·[All guides](https://uplinkswitch.ai/guides)·[Models](https://uplinkswitch.ai/models)

At the end of this page your existing OpenAI SDK, Anthropic SDK or Gemini SDK code runs unchanged, with its base URL set to `https://api.uplinkswitch.com/v1` and one key in place of three. The OpenAI SDK reads `OPENAI_BASE_URL`, the Anthropic SDK reads `ANTHROPIC_BASE_URL`, and the Gemini SDK takes the base URL in a constructor option. Every model on the [model list](https://uplinkswitch.ai/models) is callable from the same key, and each request's charge lands on your prepaid balance with a signed receipt in the response headers. The same two values are what the [Claude Code](https://uplinkswitch.ai/guides/claude-code) and [Cursor](https://uplinkswitch.ai/guides/cursor) guides set, so one key covers your code and your editor.

## Environment variables

Both SDKs read these, so no code changes.

shell

```
# OpenAI SDK (Python and TypeScript read these)
export OPENAI_BASE_URL="https://api.uplinkswitch.com/v1"
export OPENAI_API_KEY="sk-uplink-live-..."

# Anthropic SDK (Python and TypeScript read these)
export ANTHROPIC_BASE_URL="https://api.uplinkswitch.com/v1"
export ANTHROPIC_API_KEY="sk-uplink-live-..."
```

The Python and TypeScript clients of each SDK read the same two names, so a service that already runs on `OPENAI_API_KEY` switches with one line in its environment. Keys begin with `sk-uplink-live-`; the key page in the [console](https://uplinkswitch.ai/console) shows the prefix after creation and the full key once. Neither SDK reads a header from the environment, so the `HTTP-Referer` and `X-Title` headers in the examples below, which name the app against each request in our log, go in the constructor.

## TypeScript

The constructor takes the same two values when you would rather not use environment variables. `openai` on npm, any version that has `chat.completions.create`:

openai.ts

```
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://api.uplinkswitch.com/v1',
  apiKey: process.env.UPLINKSWITCH_API_KEY,
  defaultHeaders: {
    'HTTP-Referer': 'https://uplinkswitch.ai',
    'X-Title': 'your app name',
  },
});

const completion = await client.chat.completions.create({
  model: 'gpt-6.1-sol',
  messages: [{ role: 'user', content: 'Say ok.' }],
});
console.log(completion.choices[0].message.content);
```

With `OPENAI_BASE_URL` and `OPENAI_API_KEY` exported, `new OpenAI()` with no arguments reads them and the base URL and key lines go away.

The Anthropic SDK, `@anthropic-ai/sdk` on npm, sends to `/v1/messages` and needs `max_tokens` on every call:

anthropic.ts

```
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({
  baseURL: 'https://api.uplinkswitch.com/v1',
  apiKey: process.env.UPLINKSWITCH_API_KEY,
  defaultHeaders: {
    'HTTP-Referer': 'https://uplinkswitch.ai',
    'X-Title': 'your app name',
  },
});

const message = await client.messages.create({
  model: 'claude-sonnet-5-5',
  max_tokens: 256,
  messages: [{ role: 'user', content: 'Say ok.' }],
});
const first = message.content[0];
console.log(first.type === 'text' ? first.text : first.type);
```

`ANTHROPIC_BASE_URL` and `ANTHROPIC_API_KEY` work there too, so `new Anthropic()` with no arguments is enough once the shell block above is in place. The Messages format answers only Claude ids; the section on formats below says what the other ids return.

## Gemini SDK

The base URL goes in `http_options`.

gemini.py

```
from google import genai

client = genai.Client(
    api_key="sk-uplink-live-...",
    http_options={
        "base_url": "https://api.uplinkswitch.com/v1",
        "headers": {
            "HTTP-Referer": "https://uplinkswitch.ai",
            "X-Title": "your app name",
        },
    },
)
```

The client then sends Google's format under `/v1beta` and the Gemini ids answer it. The same ids also answer the OpenAI format, so a project that already uses the OpenAI SDK can call `gemini-3.1-pro-preview` without adding a second client.

## Vercel AI SDK

The provider package sets the base URL and reads your key from `UPLINKSWITCH_API_KEY`. Any model id from the [Models page](https://uplinkswitch.ai/models) works, with `generateText` and `streamText` alike. Source: [github.com/uplinkswitch](https://github.com/uplinkswitch/uplinkswitch-ai-sdk-provider).

shell

```
npm install ai @uplinkswitch/ai-sdk-provider
```

ai-sdk.ts

```
import { createUplinkSwitch } from '@uplinkswitch/ai-sdk-provider';
import { generateText } from 'ai';

const uplinkswitch = createUplinkSwitch({
  // reads UPLINKSWITCH_API_KEY
  headers: {
    'HTTP-Referer': 'https://uplinkswitch.ai',
    'X-Title': 'Vercel AI SDK',
  },
});

const { text } = await generateText({
  model: uplinkswitch('gpt-6.1-sol'),
  prompt: 'Explain what an API gateway does in one sentence.',
});
```

Without the package, `@ai-sdk/openai-compatible` works the same way with the base URL set by hand:

openai-compatible.ts

```
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { generateText } from 'ai';

const uplinkswitch = createOpenAICompatible({
  name: 'uplinkswitch',
  apiKey: process.env.UPLINKSWITCH_API_KEY,
  baseURL: 'https://api.uplinkswitch.com/v1',
  headers: {
    'HTTP-Referer': 'https://uplinkswitch.ai',
    'X-Title': 'Vercel AI SDK',
  },
});

const { text } = await generateText({
  model: uplinkswitch('gpt-6.1-sol'),
  prompt: 'Explain what an API gateway does in one sentence.',
});
```

Either way the Claude ids go through the same provider, since they answer the OpenAI chat completions format; there is no separate Anthropic provider to configure for them.

## Check it

First call: the model list. It returns the ids this key can call, which is the whole catalog unless the key carries an allowlist, plus any aliases your org defined, each with an `alias_of` field naming the real id.

shell

```
curl https://api.uplinkswitch.com/v1/models -H "Authorization: Bearer $UPLINKSWITCH_API_KEY"
```

Then one chat completion with `-i`, so the response headers print above the body:

shell

```
curl -i https://api.uplinkswitch.com/v1/chat/completions \
  -H "Authorization: Bearer $UPLINKSWITCH_API_KEY" \
  -H "content-type: application/json" \
  -H "HTTP-Referer: https://uplinkswitch.ai" \
  -H "X-Title: your app name" \
  -d '{"model": "claude-haiku-5-5", "messages": [{"role": "user", "content": "Say ok."}]}'
```

Three headers matter. `x-uplink-request-id` is the id that looks the request up afterwards. `x-uplink-charge` is the exact dollar amount taken from your balance, on the order of $0.0002 for a 2,000-in, 500-out turn on Claude Haiku 5.5. `x-uplink-receipt` is a signed statement of which model answered, the billed token counts, the charge and SHA-256 hashes of the request and the response.

Any request's receipt is also served on its own route, and the signature checks on the [verifier page](https://uplinkswitch.ai/statements/verify) without a key:

shell

```
curl https://api.uplinkswitch.com/v1/requests/REQUEST_ID/receipt \
  -H "Authorization: Bearer $UPLINKSWITCH_API_KEY"
```

A streamed response carries the request id but not the charge or the receipt. Its token counts and charge are at `GET /v1/requests/{id}` about a minute after the stream ends, and the receipt route works for it from then on.

## What a request costs

Prices are per million tokens, live from the catalog. The last column is one chat turn of 2,000 input tokens and 500 output tokens, a short system prompt plus a question in and a paragraph out, with nothing counted at the cached rate.

| Model | Input / 1M | Output / 1M | List input | List output | Under list (in / out) | Turn, 2,000 in + 500 out |
| --- | --- | --- | --- | --- | --- | --- |
| [Claude Sonnet 5.5](https://uplinkswitch.ai/models/claude-sonnet-5-5) | $1.20 | $3.00 | $2.00 | $10.00 | 40% / 70% | $0.0039 |
| [Claude Opus 5.5](https://uplinkswitch.ai/models/claude-opus-5-5) | $2.80 | $10.00 | $4.00 | $20.00 | 30% / 50% | $0.0106 |
| [Claude Haiku 5.5](https://uplinkswitch.ai/models/claude-haiku-5-5) | $0.0582 | $0.1455 | $0.10 | $0.50 | 41% / 70% | $0.0002 |
| [GPT-6.1 Sol](https://uplinkswitch.ai/models/gpt-6.1-sol) | $0.60 | $3.00 | $2.00 | $10.00 | 70% / 70% | $0.0027 |
| [GPT-6 Luna](https://uplinkswitch.ai/models/gpt-6-luna) | $0.0625 | $0.20 | $0.10 | $0.50 | 37% / 60% | $0.0002 |
| [Gemini 3.1 Pro Preview](https://uplinkswitch.ai/models/gemini-3.1-pro-preview) | $1.20 | $7.20 | $2.00 | $12.00 | 40% / 40% | $0.006 |
| [DeepSeek V4 Pro 0813](https://uplinkswitch.ai/models/deepseek-v4-pro-0813) | $0.792 | $1.188 | $1.32 | $3.96 | 40% / 70% | $0.0022 |
| [Kimi K3](https://uplinkswitch.ai/models/kimi-k3) | $0.90 | $4.50 | $3.00 | $15.00 | 70% / 70% | $0.0041 |

USD per million tokens, live from GET /v1/catalog. List: the price on the developer’s own page. A dash means the catalog has no list price on record for that model.

A longer context changes the input side only: a 20,000-token prompt costs ten times the input figure above and the same output figure. Cached input, when your SDK sends the same prefix twice, bills at each model's cached rate, shown on its model page. The [LLM API pricing comparison](https://uplinkswitch.ai/blog/llm-api-pricing-2026) carries the full list next to each developer's page, and [the compare pages](https://uplinkswitch.ai/compare) put two models side by side.

The [real-model check](https://uplinkswitch.ai/proof) today, one test prompt per model with the token counts public: Claude Sonnet 5.5, steady since Oct 12; Claude Opus 5.5, steady since Oct 13; Claude Haiku 5.5, steady since Oct 19; GPT-6.1 Sol, steady since Oct 31; GPT-6 Luna, steady since Nov 1; Gemini 3.1 Pro Preview, steady since Nov 1; DeepSeek V4 Pro 0813, steady since Nov 4; Kimi K3, steady since Oct 12. Each verdict is the live one from the proof page, not a typed claim.

## Tool calling, JSON and streaming

Tools go out in OpenAI's `tools` schema and calls come back as tool events, with arguments streaming incrementally, on every model in the table, so an agent loop written against the OpenAI SDK runs on a Claude, Gemini or DeepSeek id without a branch.

A request that asks for JSON or a schema, through `response_format` or the header `x-uplink-require: json`, pays only for an answer that matches. A miss is retried once and the missed answer is free, so a parse failure never reaches your balance. This holds for non-streamed requests; a stream is billed as sent.

Streams pass through in each format's own SSE events: OpenAI chunks on `/v1/chat/completions`, Anthropic events on `/v1/messages`, Google's on `/v1beta`.

stream.py

```
from openai import OpenAI

client = OpenAI(  # reads OPENAI_BASE_URL and OPENAI_API_KEY
    default_headers={
        "HTTP-Referer": "https://uplinkswitch.ai",
        "X-Title": "your app name",
    },
)
with client.chat.completions.stream(
    model="gpt-6.1-sol",
    messages=[{"role": "user", "content": "Count to five."}],
) as s:
    for event in s:
        if event.type == "content.delta":
            print(event.delta, end="")
```

## Which format for which model

Every model takes the OpenAI format. The Claude models also take Anthropic's, and the Gemini models Google's. Each model on the [Models page](https://uplinkswitch.ai/models) lists its own; the table's models, live from the catalog:

| Model | Formats |
| --- | --- |
| [Claude Sonnet 5.5](https://uplinkswitch.ai/models/claude-sonnet-5-5) | anthropic, openai |
| [Claude Opus 5.5](https://uplinkswitch.ai/models/claude-opus-5-5) | anthropic, openai |
| [Claude Haiku 5.5](https://uplinkswitch.ai/models/claude-haiku-5-5) | openai, anthropic |
| [GPT-6.1 Sol](https://uplinkswitch.ai/models/gpt-6.1-sol) | openai |
| [GPT-6 Luna](https://uplinkswitch.ai/models/gpt-6-luna) | openai |
| [Gemini 3.1 Pro Preview](https://uplinkswitch.ai/models/gemini-3.1-pro-preview) | openai |
| [DeepSeek V4 Pro 0813](https://uplinkswitch.ai/models/deepseek-v4-pro-0813) | openai |
| [Kimi K3](https://uplinkswitch.ai/models/kimi-k3) | openai |

wire\_formats from GET /v1/catalog. openai: /v1/chat/completions. anthropic: /v1/messages. gemini: Google’s format under /v1beta.

A non-Claude id sent to `/v1/messages` returns a 400 whose message says the model is served in the openai format; switch that call to the OpenAI SDK and keep the id. Gemini ids take Google's format under `/v1beta` from the Gemini SDK and the OpenAI format from everything else.

## Keys, limits and errors

-   The key goes in `Authorization: Bearer` or in `x-api-key`; a key in the URL is refused, and a request carrying two different keys returns 400.
-   Keys carry their environment in the prefix, `sk-uplink-live-` or `sk-uplink-test-`, so a glance at a log line says which one a request used.
-   Every key has a requests-per-minute limit and a concurrency limit, and can carry a tokens-per-minute limit; over any of them the answer is 429 `rate_limited` with a `retry-after` header that says how long to wait.
-   A per-key monthly spend limit, set in the console, returns 402 `monthly_limit_reached` once the key's settled charges and open holds reach it; the counter restarts on the first of the UTC month.
-   A key can carry its own budget: a hard cap in dollars, taken from your balance, that does not reset. Once the key's charges and open holds reach it the answer is 402 `key_budget_exhausted` until the budget is raised. The next section shows how to hand such keys out by API.
-   A key narrowed to a model allowlist returns 403 `scope_denied` for any other id, and the ids it may call are exactly what `GET /v1/models` lists for it.
-   An admin key, the one that manages keys, gets 403 `admin_key` on every model route. It is for `/v1/keys` only.
-   401 `invalid_api_key`: the key is wrong, expired or revoked. Create a new one in the [console](https://uplinkswitch.ai/console); the [Claude API key post](https://uplinkswitch.ai/blog/claude-api-key) walks the steps.
-   404 `model_not_found`: the id is not in the catalog. Copy it from `GET /v1/models`; ids are the developer's own, with no prefix.
-   402 `insufficient_balance`: the prepaid balance is at zero. Top up in the console and the same request goes through. Nothing here is free except the catalog and the estimate route; the [free AI API post](https://uplinkswitch.ai/blog/free-ai-api) lists what is.
-   Errors come back in the caller's wire format, so the OpenAI SDK raises its usual `APIStatusError` with the code in the body and the Anthropic SDK its own, and every error response still carries `x-uplink-request-id`.

## Keys with their own budget: the key API

If you resell access, run bots for clients or hand keys to a team, give each key a budget instead of sharing your balance. A budget is a hard cap for that key alone, carved out of your prepaid balance: the key stops at its budget, the balance is still what is debited, and refunds and receipts work as for any other request. Set it in the console when you create a key, or do the whole thing by API. Reselling access needs Uplink Switch LLC's written authorization first: email [hello@uplinkswitch.com](mailto:hello@uplinkswitch.com) to ask.

The key API needs an admin key. Create one in the console (Keys, then tick _Admin key_): it manages keys through `GET`, `POST`, `PATCH` and `DELETE /v1/keys` and cannot call models, so a leaked admin key spends nothing. It cannot create another admin key either; only an owner in the console can.

Create a key with a $5 budget

```
# $ADMIN_KEY is an admin key, created once in the console (Keys, "Admin key").
curl https://api.uplinkswitch.com/v1/keys \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H "content-type: application/json" \
  -d '{"name": "customer-7", "budget_usd": 5, "rpm": 60, "allowed_models": ["claude-haiku-5-5"]}'

# 201: the full key, once, with its record.
# {"key": "sk-uplink-live-...", "data": {"id": "k7m2...", "budget_micro": 5000000,
#   "budget_spent_micro": 0, "budget_remaining_micro": 5000000, "scope": "api", ...}}
```

Usage, changes, revocation

```
# What the key has used today, this month and in all, and what is left of its budget.
curl https://api.uplinkswitch.com/v1/keys/KEY_ID/usage -H "Authorization: Bearer $ADMIN_KEY"

# Raise the budget (never under what is spent), narrow the models, pause or resume.
curl -X PATCH https://api.uplinkswitch.com/v1/keys/KEY_ID \
  -H "Authorization: Bearer $ADMIN_KEY" -H "content-type: application/json" \
  -d '{"budget_usd": 10, "status": "active"}'

# Revoke for good.
curl -X DELETE https://api.uplinkswitch.com/v1/keys/KEY_ID -H "Authorization: Bearer $ADMIN_KEY"
```

`POST /v1/keys` takes `name`, and optionally `allowed_models`, `rpm`, `tpm`, `monthly_cap_usd`, `budget_usd` and `expires_at` (Unix milliseconds). `PATCH` takes the same fields plus `status`; a budget may go up or down but never under what the key has already spent, and `null` removes it. `GET /v1/keys/:id/usage` returns requests and spend for today, this month and lifetime, and the budget with what is left. The full shapes are in the [OpenAPI document](https://api.uplinkswitch.com/v1/openapi.json) under the `keys` tag.

## API reference

Every route, its request and response shapes and the authentication headers are in the OpenAPI 3.1 document at [api.uplinkswitch.com/openapi.json](https://api.uplinkswitch.com/openapi.json). Model ids and prices come from [GET /v1/catalog](https://api.uplinkswitch.com/v1/catalog), which needs no key.

Get a key in the [console](https://uplinkswitch.ai/console): prepaid credit, no subscription, 30-day price lock.

## Questions

### 

Create it in the console under Keys. The full key is shown once at creation, so copy it then; afterwards the console shows only the prefix and the spend on it. Send it as Authorization: Bearer sk-uplink-live-... or as an x-api-key header; the SDKs do that for you once the environment variable is set.

### 

No. Every request bills per token from prepaid credit, and the price table on this page is the rate per million tokens; a 2,000-in, 500-out turn on Claude Sonnet 5.5 is $0.0039. What needs no key: GET /v1/catalog for prices and POST /v1/estimate for a cost before you send. The free AI API post lists what is free and what is not.

### 

No. Credit is prepaid, there is no monthly fee, and the per-token price is locked for 30 days from each top-up. The same key carries a monthly spend limit you set, so a loop that runs away stops at 402 monthly\_limit\_reached rather than at your card limit.

### 

No. A non-Claude id sent to /v1/messages returns a 400 saying the model is served in the openai format. Point the OpenAI SDK at the same base URL for GPT, Gemini, DeepSeek, Kimi and the rest; the Claude ids answer both formats, so one OpenAI client can cover the whole list.

### 

Yes. The @uplinkswitch/ai-sdk-provider package speaks the OpenAI chat completions format, and every Claude id answers it, so uplinkswitch("claude-sonnet-5-5") works with generateText, streamText and tool calls the same as a GPT id. Without the package, @ai-sdk/openai-compatible with the base URL set by hand does the same.

### 

No. Two values change: the base URL becomes https://api.uplinkswitch.com/v1 and the key becomes your UplinkSwitch key. Model ids are the developer ids, claude-sonnet-5-5, gpt-6.1-sol, gemini-3.1-pro-preview, and requests and responses keep each SDK’s own shape, including streaming and tool calls.

### 

The OpenAPI 3.1 document at api.uplinkswitch.com/openapi.json lists every route, its request and response shapes, the error codes and the authentication headers. GET /v1/catalog returns every model with its price, list price, wire formats, context window and maximum output, with no key.

## Related

-   [Claude Code setup](https://uplinkswitch.ai/guides/claude-code)
-   [How to get a Claude API key](https://uplinkswitch.ai/blog/claude-api-key)
-   [Cursor setup](https://uplinkswitch.ai/guides/cursor)
-   [Model catalog](https://uplinkswitch.ai/models)
-   [OpenClaw setup](https://uplinkswitch.ai/guides/openclaw)
-   [LangChain setup](https://uplinkswitch.ai/guides/langchain)
-   [Strands Agents setup](https://uplinkswitch.ai/guides/strands-agents)
-   [LiteLLM setup](https://uplinkswitch.ai/guides/litellm)
-   [Vercel AI SDK setup](https://uplinkswitch.ai/guides/vercel-ai-sdk)
-   [Open WebUI setup](https://uplinkswitch.ai/guides/open-webui)
-   [Model comparisons](https://uplinkswitch.ai/compare)
