Providers and keys
Every AI provider key the CLI recognises, and what it writes instead.
AI/LLM: this page is available in plain markdown at /docs/resources/ai-gateway/providers.md
A provider's API key can only be minted by that provider. What we can do is recognise that your project is asking for one and answer it with the gateway, which reaches the same models.
npx extraorbital add openai✓ Provisioned openrouter/default (small models · $5.00 allowance)
✓ Wrote 4 variables to .env
OPENROUTER_BASE_URL, OPENROUTER_API_KEY, OPENAI_BASE_URL, OPENAI_API_KEY
✗ openai — an OpenAI key cannot be provisioned directly
OpenAI models are served through the AI Gateway. OPENAI_BASE_URL and
OPENAI_API_KEY now point at it, so your OpenAI SDK works unchanged — prefix
model ids with the author, e.g. openai/<model>.Exit code 8. The credentials are on disk and your code runs; the non-zero code
is there because the thing you literally asked for is not what you got, and a
script should be able to notice that without parsing text.
Every recognised key
| Provider | Recognised keys | Model ids | Also written |
|---|---|---|---|
| OpenAI | OPENAI_API_KEY | openai/… | OPENAI_BASE_URLOPENAI_API_KEY |
| Anthropic | ANTHROPIC_API_KEYANTHROPIC_AUTH_TOKEN | anthropic/…, ~anthropic/… | ANTHROPIC_BASE_URLANTHROPIC_API_KEY |
| Google Gemini | GEMINI_API_KEYGOOGLE_GENERATIVE_AI_API_KEYGOOGLE_API_KEY | google/… | Nothing — the gateway does not speak this API |
| xAI | XAI_API_KEYGROK_API_KEY | x-ai/… | XAI_BASE_URLXAI_API_KEY |
| Mistral | MISTRAL_API_KEY | mistralai/… | MISTRAL_BASE_URLMISTRAL_API_KEY |
| DeepSeek | DEEPSEEK_API_KEY | deepseek/… | DEEPSEEK_BASE_URLDEEPSEEK_API_KEY |
| Meta Llama | LLAMA_API_KEY | meta-llama/…, meta/… | Nothing — the gateway does not speak this API |
| Qwen | DASHSCOPE_API_KEYQWEN_API_KEY | qwen/… | Nothing — the gateway does not speak this API |
| Moonshot | MOONSHOT_API_KEY | moonshotai/… | MOONSHOT_BASE_URLMOONSHOT_API_KEY |
| Z.ai | ZAI_API_KEYZHIPUAI_API_KEY | z-ai/… | Nothing — the gateway does not speak this API |
| MiniMax | MINIMAX_API_KEY | minimax/… | Nothing — the gateway does not speak this API |
| Nous Research | NOUS_API_KEY | nousresearch/… | Nothing — the gateway does not speak this API |
| Cohere | COHERE_API_KEYCO_API_KEY | cohere/… | Nothing — the gateway does not speak this API |
| Perplexity | PERPLEXITY_API_KEYPPLX_API_KEY | perplexity/… | PERPLEXITY_BASE_URLPERPLEXITY_API_KEY |
| Groq | GROQ_API_KEY | — | GROQ_BASE_URLGROQ_API_KEY |
| Together AI | TOGETHER_API_KEY | — | TOGETHER_BASE_URLTOGETHER_API_KEY |
| Fireworks AI | FIREWORKS_API_KEY | — | FIREWORKS_BASE_URLFIREWORKS_API_KEY |
| Cerebras | CEREBRAS_API_KEY | — | CEREBRAS_BASE_URLCEREBRAS_API_KEY |
| Amazon Bedrock | AWS_BEARER_TOKEN_BEDROCK | amazon/… | Nothing — the gateway does not speak this API |
The Also written column is the difference that matters. OpenRouter speaks
the OpenAI and Anthropic wire protocols, so for those providers we can write
their own variable names pointed at the gateway and nothing in your code has to
change. For Gemini, Cohere and Bedrock it speaks neither — writing a gateway key
into GEMINI_API_KEY would produce a variable that looks right and fails on the
first call, so we write nothing and say so.
Those models are still reachable. Call them through the gateway with an OpenAI-compatible client or the Vercel AI SDK, which has no such limit.
What autopilot does
extraorbital provision reads the same list. A project declaring
GEMINI_API_KEY in its .env.example gets one gateway resource, an explanation,
and exit 8:
Provisioning 2 resources for .
openrouter GEMINI_API_KEY · declared in .env.example · via the AI Gateway
mongo MONGODB_URI · declared in .env.example
✓ Provisioned openrouter/default
✓ Provisioned mongo/default
✗ GEMINI_API_KEY — cannot be provisioned
Google Gemini models are served through the AI Gateway, which does not speak
Google Gemini's own API. Call them through the gateway with an
OpenAI-compatible client or the Vercel AI SDK.Several providers in one project collapse onto one gateway key rather than one each — the key is not per-vendor, so neither is the resource.
What gets written where
The fenced block records which providers it was written for, so
extraorbital check --fix rewrites the same set rather than dropping the compat
variables:
# >>> extraorbital resource=openrouter project=cosmic-otter slug=default compat=openai
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
OPENROUTER_API_KEY=sk-or-v1-9c4e17b2…
OPENAI_BASE_URL=https://openrouter.ai/api/v1
OPENAI_API_KEY=sk-or-v1-9c4e17b2…
# <<< extraorbital resource=openrouterBoth _API_KEY variables hold the same secret. That is not a mistake: there is
one key, and OPENAI_API_KEY is a second name for it that the OpenAI SDK knows
to read.
If your project already sets one of these to a placeholder, the CLI comments that
line out — dotenv keeps the first definition it sees, so an unfilled
OPENAI_API_KEY= above the block would otherwise win over the credential below
it.
Next
- Models — what each provider offers, with prices
- SDK recipes — the code for each