Use the OpenAI Python SDK and LangChain with a cheaper API (UberLLM)
Set base_url="https://api.uberllm.dev/v1" and api_key to your UberLLM key in OpenAI(...) or ChatOpenAI(...), then pass any UberLLM model id. Chat, streaming, tool calls and JSON output keep working; your other code stays the same.
Steps (OpenAI Python SDK)
- Make a key on your keys page and add credit.
- Save it as an environment variable:
export UBERLLM_API_KEY=ull_your_key
- Point the client at UberLLM:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.uberllm.dev/v1",
api_key=os.environ["UBERLLM_API_KEY"],
)
resp = client.chat.completions.create(
model="deepseek/deepseek-v4.1-flash",
messages=[{"role": "user", "content": "Give me three names for a coffee shop."}],
)
print(resp.choices[0].message.content)
That is the whole change. client.chat.completions.create with stream=True, tools, response_format, temperature and max_tokens works as before. The async client (AsyncOpenAI) takes the same two arguments.
Without touching the code
The OpenAI SDK also reads two environment variables, so a script that calls OpenAI() with no arguments can be moved without editing it:
export OPENAI_BASE_URL=https://api.uberllm.dev/v1
export OPENAI_API_KEY=ull_your_key
python your_script.py
You still need to change the model= string to an UberLLM id such as deepseek/deepseek-v4.1-flash.
Steps (LangChain)
pip install langchain-openai
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="deepseek/deepseek-v4.1-flash",
base_url="https://api.uberllm.dev/v1",
api_key=os.environ["UBERLLM_API_KEY"],
)
print(llm.invoke("Say hello in one sentence.").content)
Chains, agents, bind_tools and .stream() work unchanged. Pass base_url in code rather than relying on environment variables: the argument always wins, so a stray OPENAI_API_BASE on the machine cannot send calls elsewhere.
LangChain.js is the same idea:
import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
model: "deepseek/deepseek-v4.1-flash",
apiKey: process.env.UBERLLM_API_KEY,
configuration: { baseURL: "https://api.uberllm.dev/v1" },
});
Pick a cheaper model for each job
The point of one key for every model is that you can stop paying frontier prices for easy work. A common split:
- Hard reasoning or customer-facing answers: a top model such as
deepseek/deepseek-v4.1-flash. - Bulk summaries, tagging, extraction:
google/gemma-3-27b-itor another model from the batch table below, often 50 to 100 times cheaper per token.
Change only the model= string to move a step from one to the other.
What is not there
UberLLM serves chat completions (/v1/chat/completions), the Responses API (client.responses.create, streaming and tool calls included) and the Anthropic messages route. It keeps no state between calls, so send the whole conversation in input instead of previous_response_id (Responses API reference). It does not serve embeddings or the Assistants API: keep those calls on their current provider.
Troubleshooting
openai.AuthenticationError(401): the key is wrong or empty. Printlen(os.environ["UBERLLM_API_KEY"]); a key isull_plus 43 characters.openai.NotFoundError(404) on/responsesor/embeddings: those routes do not exist here, see above.model_not_found: OpenAI-style ids likegpt-4oneed the maker in front:openai/gpt-4o.openai.RateLimitError(429): the key's per-minute limit (300 by default). Raise it on the keys page, or add retries with backoff.- 402
insufficient_credits: top up on the credits page.