LangChain
ChatOpenAI with one base URL and one key, in Python and JavaScript. Claude, GPT, Gemini and DeepSeek through the same class.
Python
LangChain talks to UplinkSwitch through its OpenAI integration. Install it, create a key in the console and export it as UPLINKSWITCH_API_KEY.
pip install langchain-openaiimport os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="claude-sonnet-5-5",
base_url="https://api.uplinkswitch.com/v1",
api_key=os.environ["UPLINKSWITCH_API_KEY"],
default_headers={
"HTTP-Referer": "https://uplinkswitch.ai",
"X-Title": "LangChain",
},
)
print(llm.invoke("Explain what an API gateway does in one sentence.").content)base_url stops at /v1. The model id is the one on the Models page, with no vendor prefix. Swap claude-sonnet-5-5 for gpt-6.1-sol or deepseek-v4-pro-0813 and nothing else changes. default_headers is optional: the gateway records X-Title as the client app against each request on the console's Usage page, so put your own app name there.
JavaScript and TypeScript
In LangChain.js the base URL and the headers go inside configuration, which is passed to the OpenAI client.
npm install @langchain/openai @langchain/coreimport { ChatOpenAI } from '@langchain/openai';
const llm = new ChatOpenAI({
model: 'claude-sonnet-5-5',
apiKey: process.env.UPLINKSWITCH_API_KEY,
configuration: {
baseURL: 'https://api.uplinkswitch.com/v1',
defaultHeaders: {
'HTTP-Referer': 'https://uplinkswitch.ai',
'X-Title': 'LangChain',
},
},
});
const msg = await llm.invoke('Explain what an API gateway does in one sentence.');
console.log(msg.content);Tool calls
bind_tools sends OpenAI's tools schema, and the calls come back on msg.tool_calls. Claude Sonnet 5.5, GPT-6.1 Sol and DeepSeek V4 Pro each called the tool below with {'city': 'Paris'} in our run.
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
return f"Sunny in {city}"
msg = llm.bind_tools([get_weather]).invoke("What's the weather in Paris?")
print(msg.tool_calls)
# [{'name': 'get_weather', 'args': {'city': 'Paris'}, 'id': '...', 'type': 'tool_call'}]Agents
create_agent runs the tool loop for you: it calls the model, runs each tool the model asks for, sends the result back and stops when the model answers. Pass it the same llm.
pip install langchain langchain-openaifrom langchain.agents import create_agent
from langchain_core.tools import tool
@tool
def word_count(text: str) -> int:
"""Count the words in a piece of text."""
return len(text.split())
agent = create_agent(model=llm, tools=[word_count])
result = agent.invoke(
{"messages": [{"role": "user", "content": "How many words are in 'the quick brown fox jumps'?"}]}
)
print(result["messages"][-1].text)On Claude Sonnet 5.5, GPT-6.1 Sol and DeepSeek V4 Pro the agent called word_count once and answered 5, in two model calls. Each model call in the loop is a separate request, listed with its charge on the console's Usage page.
Structured output
Pass method="function_calling" to with_structured_output. It turns the schema into a forced tool call, and Claude Sonnet 5.5, GPT-6.1 Sol and DeepSeek V4 Pro each returned a filled Capital object.
from pydantic import BaseModel
class Capital(BaseModel):
country: str
capital: str
parser = llm.with_structured_output(Capital, method="function_calling")
print(parser.invoke("What is the capital of France?"))
# country='France' capital='Paris'The default method is json_schema, which sends response_format. GPT-6.1 Sol returned the object with it; Claude Sonnet 5.5 and DeepSeek V4 Pro returned a plain sentence, and Pydantic raised ValidationError: Invalid JSON.
Streaming
stream yields chunks as the model writes them. All three models streamed in our run.
for chunk in llm.stream("Count from 1 to 5."):
print(chunk.content, end="", flush=True)Models and prices
| Model | Model id | Input / 1M | Output / 1M | Developer list, in / out |
|---|---|---|---|---|
| Claude Sonnet 5.5 | claude-sonnet-5-5 | $1.20 | $3.00 | $2.00 / $10.00 |
| GPT-6.1 Sol | gpt-6.1-sol | $0.60 | $3.00 | $2.00 / $10.00 |
| DeepSeek V4 Pro 0813 | deepseek-v4-pro-0813 | $0.792 | $1.188 | $1.32 / $3.96 |
| Claude Opus 5.5 | claude-opus-5-5 | $2.80 | $10.00 | $4.00 / $20.00 |
| Gemini 3.1 Pro Preview | gemini-3.1-pro-preview | $1.20 | $7.20 | $2.00 / $12.00 |
| Grok 4.6 | grok-4.6 | $0.60 | $1.80 | $2.00 / $6.00 |
USD per million tokens, live from GET /v1/catalog.
A chain call that sends 4,000 input tokens (prompt, retrieved context and history) and gets 500 back costs $0.0063 on Claude Sonnet 5.5, against $0.013 at Anthropic's list price. A thousand of them a day is $6.30. Credit is prepaid from $10 with no subscription.
Check it
Confirm the key before you run a chain:
curl https://api.uplinkswitch.com/v1/models \
-H "Authorization: Bearer $UPLINKSWITCH_API_KEY" \
-H "HTTP-Referer: https://uplinkswitch.ai" \
-H "X-Title: LangChain"A list of model ids means the key works. Every snippet on this page ran against the live API on 2026-10-19 with langchain 1.4.6, langchain-openai 1.7.2 and @langchain/openai 1.6.2: invoke, stream, bind_tools, create_agent and with_structured_output on Claude Sonnet 5.5, GPT-6.1 Sol and DeepSeek V4 Pro.
Errors
AuthenticationError: Error code: 401withThe API key is not valid.means the key is wrong or revoked. With no key at all the message isNo API key was sent.NotFoundError: Error code: 404withNo such route.meansbase_urllost its/v1.- A 404 with
No such modelmeans the model id is not in the catalog. Copy it from the Models page. - A 402 with the code
insufficient_balancemeans the prepaid balance ran out. Requests work again after a top-up.