Connect CrewAI to a custom OpenAI-compatible endpoint.
Updated 2026-07-16
CrewAI's LLM class takes base_url and api_key directly. Prefix the model id with openai/ so the request speaks chat completions, point base_url at https://api.apisrouter.com/v1, and every agent in the crew can hold a different catalog model under one key.
Quick answer: LLM with an openai/ prefix and base_url.
CrewAI configures models through its LLM class: pass model with an openai/ prefix on the id, base_url pointing at https://api.apisrouter.com/v1, and api_key. Hand the instance to an Agent and every task that agent executes routes through the gateway over standard /v1/chat/completions. The prefix carries the routing decision. CrewAI's model layer rides on LiteLLM, which resolves the provider client from the model string; openai/claude-sonnet-4-6 forces the OpenAI-protocol client aimed at your base_url, with claude-sonnet-4-6 forwarded as the model field. The same three values can come from the environment instead (OPENAI_API_BASE, OPENAI_API_KEY, OPENAI_MODEL_NAME) for deployments where code stays generic.
import os
from crewai import Agent, LLM
llm = LLM(
model="openai/claude-sonnet-4-6", # prefix + any catalog id
base_url="https://api.apisrouter.com/v1",
api_key=os.environ["APISROUTER_API_KEY"],
)
researcher = Agent(
role="Researcher",
goal="Gather and condense sources",
backstory="Methodical, cites everything.",
llm=llm,
)How a crew spends tokens.
CrewAI (crewAIInc on GitHub, roughly 56K stars) organizes work as a crew of role-specialized agents executing tasks, sequentially or under a manager in hierarchical mode. Each agent turn is a chat-completions call carrying the agent's role prompt, task description, accumulated context from earlier tasks, and tool schemas. A five-task crew with tool use does not make five calls; it makes dozens, because every tool invocation and every iteration on a task is another round trip. That fan-out is why per-agent model assignment is the feature that matters here. The llm parameter is set per Agent, so a crew can hold a frontier model where judgment concentrates and fast models where volume concentrates, all through one endpoint because every LLM instance shares the same base_url and key. In hierarchical mode the same applies to the manager slot via manager_llm, which is the single most leveraged model choice in the crew: it decomposes, delegates, and evaluates everything. CrewAI forwards the post-prefix model string untouched, so vendor mixing inside one crew is just different ids on different agents: Claude planning, DeepSeek summarizing, Qwen translating, one billing surface underneath.
Full setup: a mixed-model crew.
The pattern below is the shape most production crews converge on: a strong model in the seat where decisions concentrate, fast models in the seats where volume concentrates, and one shared endpoint definition so the routing lives in exactly one place. Generation parameters (temperature, max_tokens) sit on the LLM instance, so a deterministic extractor and a more exploratory writer can share an id but differ in behavior. CrewAI is Python-only; there is no JS variant to configure.
import os
from crewai import Agent, Crew, Task, LLM
def gateway_llm(model_id: str, **kwargs) -> LLM:
return LLM(
model=f"openai/{model_id}",
base_url="https://api.apisrouter.com/v1",
api_key=os.environ["APISROUTER_API_KEY"],
**kwargs,
)
analyst = Agent(
role="Analyst",
goal="Reason about the evidence and draw conclusions",
backstory="Careful, skeptical, shows work.",
llm=gateway_llm("claude-opus-4-7", temperature=0.2),
)
summarizer = Agent(
role="Summarizer",
goal="Condense inputs fast and faithfully",
backstory="Terse and precise.",
llm=gateway_llm("deepseek-v4-flash"),
)
crew = Crew(
agents=[analyst, summarizer],
tasks=[
Task(description="Analyze the quarterly numbers.", agent=analyst,
expected_output="Key findings with reasoning."),
Task(description="Write a one-page brief of the findings.", agent=summarizer,
expected_output="One-page brief."),
],
)
print(crew.kickoff())Choosing models per seat.
Because every seat shares one endpoint and key, rebalancing the crew is a string edit per agent. The honest evaluation loop is to fix the tasks, swap one seat's id, and compare output quality against the per-key usage delta; crews are call-heavy enough that seat-level choices show up clearly in the bill.
- The manager (hierarchical mode) and any analyst-style agent carry the crew's judgment; claude-opus-4-7 or gpt-5.5 belong here, because a bad decomposition or a wrong verdict multiplies across every downstream task.
- Worker agents doing retrieval digestion, summarization, formatting, or translation are volume seats; deepseek-v4-flash and qwen3.7-max keep the per-task cost flat.
- Tool-heavy agents need ids with dependable function calling; claude-sonnet-4-6 is the reliable middle where tool schemas are complex.
- Set temperature low (0.1-0.3) on extractor and judge seats and leave creativity for writer seats; the parameter lives per LLM instance, so this is free to express.
Pay-as-you-go · transparent per-model pricing
Selected models are priced below official list prices. Exact input, output, cache, and per-request prices are shown for each model.
| Model | Official Price | Our Price |
|---|---|---|
| Claude Opus 4.7 | $5.00 / $25.00 per M | $4.00 / $20.00 per M |
| Claude Sonnet 4.6 | $3.00 / $15.00 per M | $2.40 / $12.00 per M |
| GPT-5.5 | $5.00 / $30.00 per M | $4.00 / $24.00 per M |
| DeepSeek V4 Flash | $0.14 / $0.28 per M | $0.13 / $0.25 per M |
| Qwen 3.7 Max | $2.50 / $7.50 per M | $2.25 / $6.75 per M |
Failure modes and version pinning.
The missing prefix routes around your endpoint. With model="claude-sonnet-4-6" bare, LiteLLM resolves the Anthropic client from the name and your base_url stops applying; the request heads for a vendor host with the wrong key and fails auth. Errors that mention a vendor SDK instead of your gateway are this bug. The openai/ prefix is not cosmetic; it is the routing instruction. Partial environment overrides. OPENAI_API_BASE set in the environment while an explicit LLM instance passes a different base_url is a working setup; the confusion arrives when some agents get explicit instances and others fall through to environment defaults. Pick one convention per codebase, the factory-function pattern above being the explicit one. Memory embeddings are a separate concern. Crew memory features embed text for retrieval, configured through their own embedder settings, not through the agent LLM. Moving agent traffic to the gateway does not move embeddings; leave the embedder configuration on its existing provider unless you decide otherwise deliberately. LiteLLM arrives pinned by CrewAI. CrewAI bundles and pins its own LiteLLM dependency; installing a different litellm version alongside can produce resolution behavior that differs between machines. Pin crewai itself in requirements and let it own its LiteLLM, upgrading the pair together. Context accumulation across tasks. Later tasks receive earlier outputs as context, so a crew whose early tasks emit long outputs pushes ever-larger prompts through later seats. If late-crew calls are unexpectedly expensive in the usage view, cap expected_output length upstream rather than reaching for a bigger model downstream.
Who routes CrewAI through a gateway.
- Teams running mixed-vendor crews, frontier judgment seats and fast volume seats, without maintaining one SDK and credential set per vendor.
- Builders of scheduled or productionized crews, where an env-only configuration and a single secret fit CI and container deployment cleanly.
- Engineers evaluating crew compositions, since each candidate seat assignment is a string edit priced automatically by per-key usage.
- Cost-conscious operators of call-heavy hierarchical crews, where manager plus workers multiply request counts and a single billing surface keeps spend legible.
- Developers without access to a given vendor's billing. Top-up based access with no card requirement removes the sign-up dependency per provider.
Verify the endpoint and debug the first kickoff.
List the gateway's models before wiring agents; the id after openai/ must match a served id exactly, version suffix included. First-kickoff failures follow a pattern. Errors naming a vendor SDK mean a bare, unprefixed model string routed by name. A 401 against your gateway means the key in that LLM instance (or the environment fallback) is wrong for the endpoint; print both effective values from the failing instance. A model-not-found error is an id typo. Crews that stall mid-task on tool calls usually hold a model with weak function calling in a tool-heavy seat; move that seat to a stronger id before debugging the tools themselves. Once the crew runs, the APIsRouter console shows per-request model, token counts, and spend. For hierarchical crews this is the fastest way to see the manager-to-worker call ratio and which seat actually carries the bill.
curl -s https://api.apisrouter.com/v1/models \
-H "Authorization: Bearer $APISROUTER_API_KEY" | head -50FAQ
Can different CrewAI agents use different vendors in one crew?
Yes. The llm parameter is per Agent, and each LLM instance carries its own model string. Through one gateway endpoint, a Claude analyst, a DeepSeek summarizer, and a Qwen translator coexist in the same crew under the same key.
Why does the model string need the openai/ prefix?
CrewAI's model layer uses LiteLLM, which resolves the provider client from the model string. The openai/ prefix forces the chat-completions client aimed at your base_url. A bare claude-* id resolves to the Anthropic client instead and ignores your endpoint.
Which environment variables configure CrewAI without touching code?
OPENAI_API_BASE for the endpoint, OPENAI_API_KEY for the key, and OPENAI_MODEL_NAME for the default model (prefix included). Agents constructed without an explicit llm fall through to these, which suits containerized deployments.
What should manager_llm be in hierarchical mode?
The strongest reasoning model you run, typically claude-opus-4-7 or gpt-5.5. The manager decomposes work, delegates, and evaluates results, so its errors multiply across the crew while worker-seat errors stay local.
Does crew memory route through the gateway too?
Memory's embedding calls are configured separately through the embedder settings, not the agent llm. Agent chat traffic moves to the gateway; leave the embedder on its existing provider unless you migrate it deliberately.
How should I pin versions?
Pin crewai and let it own its bundled LiteLLM pin; upgrading them together avoids provider-resolution differences between environments. Record the crew's model ids alongside results the way you would record seeds, since dated catalog ids keep runs reproducible.