Run gpt-researcher on a custom OpenAI-compatible endpoint.

Updated 2026-07-16

gpt-researcher reads OPENAI_BASE_URL from the environment and splits its work across three model slots. Set the base URL to https://api.apisrouter.com/v1, keep the openai: prefix, and FAST_LLM, SMART_LLM, and STRATEGIC_LLM can each be a different catalog model behind one key.

Quick answer: a five-line .env block.

gpt-researcher's documented custom-endpoint path is environment variables. Set OPENAI_BASE_URL to https://api.apisrouter.com/v1, set OPENAI_API_KEY to your gateway key, and assign the three model slots with the openai: provider prefix. The prefix tells gpt-researcher which client to use; the string after the colon is passed through to the endpoint, so any id the gateway serves is valid, Claude and Gemini ids included. This is the configuration documented at docs.gptr.dev for custom OpenAI-compatible endpoints, and it works identically for the pip package, the web app, and the multi-agent flows, because all of them resolve the same config.

OPENAI_BASE_URL=https://api.apisrouter.com/v1
OPENAI_API_KEY=sk-APIsRouter-...
FAST_LLM=openai:claude-haiku-4-5-20251001
SMART_LLM=openai:claude-sonnet-4-6
STRATEGIC_LLM=openai:gpt-5.5

How gpt-researcher spends tokens across three slots.

gpt-researcher (assafelovic on GitHub, roughly 28K stars) turns a query into a researched, cited report: it plans research questions, fans out web searches through a retriever, scrapes and summarizes sources, and then writes a long-form report. The framework splits that pipeline across three configurable model slots rather than one. FAST_LLM handles the high-volume, low-stakes work, chiefly summarizing scraped pages. SMART_LLM does the heavy writing, including the final report. STRATEGIC_LLM handles planning: generating the research questions and deciding the approach. Out of the box these default to OpenAI models (gpt-4o-mini, gpt-4.1, and o4-mini respectively at time of writing), which is exactly why the single OPENAI_BASE_URL override is so effective: all three slots use the OpenAI-shaped client, so one base URL moves the whole pipeline. Because each slot takes its own provider:model string, the slots do not need to share a vendor. A run can summarize with a fast Claude model, write with a stronger Claude or GPT model, and plan with a reasoning-tier model, all through the same endpoint and key. On a single-vendor key that mix would require three accounts; behind a gateway it is three lines in .env.

Full setup: .env plus the Python API.

Create a .env file in your working directory (or export the variables in the shell) and run gpt-researcher as usual; the pip package and the web app both read the same environment. The Python API needs no endpoint-specific code at all, which is the point: routing is configuration, and the research code stays identical whether the endpoint is OpenAI's or a gateway. Two adjacent settings matter. Web retrieval runs through a retriever, Tavily by default, with its own key (TAVILY_API_KEY); that credential is independent of the LLM endpoint and still required for live web research. And embeddings default to openai:text-embedding-3-small, which means the embedding calls follow the same OpenAI-shaped client configuration; if the endpoint behind OPENAI_BASE_URL does not serve that embedding model, configure EMBEDDING to a provider that does (the docs use the custom: prefix for OpenAI-compatible embedding endpoints, and local options like Ollama are also supported).

import asyncio
from gpt_researcher import GPTResearcher

async def main():
    researcher = GPTResearcher(
        query="State of small modular reactors in 2026",
        report_type="research_report",
    )
    await researcher.conduct_research()
    report = await researcher.write_report()
    print(report)

asyncio.run(main())  # routing comes entirely from .env

Choosing models per slot.

The upstream defaults encode the right shape, small model for volume, strong model for writing, reasoning model for planning, so keep that shape and upgrade the slots rather than flattening them to one model. Behind one endpoint, an A/B between two writers is a one-line .env change per run, and the per-key usage log tells you what each report configuration actually cost.

  • FAST_LLM fires the most: every scraped source gets summarized. A fast id (claude-haiku-4-5-20251001, deepseek-v4-flash) keeps a many-source report from being dominated by summarization cost, and quality loss here is bounded because summaries feed the writer rather than the reader.
  • SMART_LLM writes the report the user actually reads. Long output, sustained structure, citation discipline: this is where claude-sonnet-4-6 or gpt-5.5 earns the spend, and where cutting quality shows up immediately.
  • STRATEGIC_LLM shapes the run before it starts. Bad research questions produce a bad report no matter how good the writer is; a reasoning-strong model here is few calls but high leverage.
  • Long-context ids like gemini-3.1-pro-preview are worth testing in the SMART slot for detailed_report runs, where the writer works across a large accumulated context of summaries.

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.

ModelOfficial PriceOur Price
Claude Haiku 4.5 20251001$1.00 / $5.00 per M$0.80 / $4.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
Gemini 3.1 Pro Preview$2.00 / $12.00 per M$1.60 / $9.60 per M
DeepSeek V4 Pro$0.43 / $0.87 per M$0.39 / $0.78 per M

The failure modes specific to gpt-researcher.

Dropping the provider prefix. The slot format is provider:model, and the prefix selects the client. Setting SMART_LLM=claude-sonnet-4-6 without openai: does not route a Claude id through your base URL; it makes gpt-researcher try to interpret the string as a different provider. Every custom-endpoint model must keep the openai: prefix, because "openai" here names the protocol, not the vendor. Embeddings quietly following the override. The default EMBEDDING is an OpenAI-shaped model, so once OPENAI_BASE_URL points at a gateway, embedding requests go there too. If the gateway does not serve that embedding id, research runs fail during source processing rather than at the first chat call, which misleads people into debugging the wrong slot. Set EMBEDDING explicitly and the symptom disappears. Blaming the endpoint for retriever failures. A missing or exhausted TAVILY_API_KEY breaks the search phase, and the resulting empty-source errors look superficially like LLM failures. The retriever is a separate service with a separate key; check it separately. Stale environment between runs. The .env file is read from the working directory. Running the web app from one directory and the Python API from another means two different configs, and "it works in the app but not my script" is almost always this. Token-limit settings are separate from model capability. gpt-researcher carries its own per-slot token limits (FAST_TOKEN_LIMIT, SMART_TOKEN_LIMIT, and related settings) with conservative defaults. Pointing SMART_LLM at a long-context model does not by itself raise those limits; tune them deliberately if you want longer generations.

Who routes gpt-researcher through a gateway.

  • Teams generating recurring reports (market scans, literature reviews, competitive briefs) where per-run cost visibility across three model slots matters more than a single vendor relationship.
  • Researchers comparing writer models. Holding FAST and STRATEGIC fixed while swapping SMART between Claude, GPT, and DeepSeek ids is three .env edits, not three vendor accounts.
  • Builders embedding gpt-researcher in products, where one gateway key per environment replaces a bundle of vendor secrets in the deploy pipeline.
  • Users who want Claude or Gemini doing the report writing while keeping gpt-researcher's stock OpenAI-shaped configuration untouched.
  • Developers without access to a given vendor's billing. Top-up based access with no card requirement removes the per-provider sign-up dependency.

Verify the endpoint and debug the first report.

List the gateway's models first; the string after openai: in each slot must match a served id exactly, version suffixes included. First-run failures sort cleanly. A 401 means OPENAI_API_KEY is absent from the environment the process actually sees; .env files load from the working directory, so run from where the file lives or export the variables globally. A model-not-found error names the slot with the typo. A failure during source processing rather than at planning time points at embeddings or the retriever, not the chat slots: check EMBEDDING and TAVILY_API_KEY before touching the LLM config. A full research run is a burst of dozens of requests across all three slots, so once it completes, the APIsRouter console's per-request view is the fastest way to see the FAST/SMART/STRATEGIC split in real tokens and real spend, and to catch a slot that is consuming more than its role deserves.

curl -s https://api.apisrouter.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY" | head -50

FAQ

Can gpt-researcher use Claude or Gemini models through OPENAI_BASE_URL?

Yes. The openai: prefix selects the OpenAI-shaped client, and the model string after the colon is passed through to the endpoint. Any id the gateway serves is valid in any of the three slots, including Claude, Gemini, and DeepSeek ids.

Do FAST_LLM, SMART_LLM, and STRATEGIC_LLM have to be the same vendor?

No. Each slot is an independent provider:model string. Behind a multi-vendor endpoint, a common setup is a fast Claude id for summaries, a stronger Claude or GPT id for report writing, and a reasoning-tier id for planning, all on one key.

Do I still need a Tavily key after changing the LLM endpoint?

Yes, if you want live web research. The retriever (Tavily by default, set via RETRIEVER) fetches search results and has its own key. It is a separate service from the LLM endpoint and is unaffected by OPENAI_BASE_URL.

What happens to embeddings when I set OPENAI_BASE_URL?

The default embedding is an OpenAI-shaped model, so embedding calls follow the same client configuration and hit your gateway. If the gateway does not serve that embedding id, set EMBEDDING explicitly to a provider that does, or to a local option; otherwise runs fail during source processing.

Does this configuration work for the web app and the multi-agent mode too?

Yes. The pip package, the web application, and the multi-agent flows all resolve the same environment configuration, so one .env file routes them identically.

How much does one research run cost through the gateway?

It depends on report type and how many sources the retriever returns: FAST_LLM summarizes each source, SMART_LLM writes the report, STRATEGIC_LLM plans. Most runs land in the tens to hundreds of thousands of tokens. The per-key usage view shows the exact per-slot split, which beats estimating.