# Providers and keys

> Every AI provider key the CLI recognises, and what it writes instead.

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.

```bash
npx extraorbital add openai
```

```txt
✓ 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

The recognised provider keys are published as the `providers` array of `GET /api/v1/services`.

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](/docs/resources/ai-gateway/sdks#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`:

```txt
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:

```dotenv title=".env"
# >>> 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=openrouter
```

Both `_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.

<Callout title="Anthropic is two variables in two places">
`ANTHROPIC_BASE_URL` is written as `https://openrouter.ai/api`, without the
`/v1` — the Anthropic SDK appends `/v1/messages` itself. Claude Code needs the
same value but does not read `.env` at all; see [its
recipe](/docs/resources/ai-gateway/sdks#claude-code).
</Callout>

## Next

- [Models](/docs/resources/ai-gateway/models) — what each provider offers, with prices
- [SDK recipes](/docs/resources/ai-gateway/sdks) — the code for each

---

More for agents: [Docs index](https://extraorbital.dev/sitemap.md) · [llms.txt](https://extraorbital.dev/llms.txt) · [agents.md](https://extraorbital.dev/agents.md)
