API key injection: what it is and how it works
"Injection" is just substituting the real key into an outgoing request. The question isn't whether it happens — it always does, for anyone calling a third-party API. The question is where, and who does it.
Injection isn't a technology, it's a moment
Every request to OpenAI, Stripe, or the Telegram Bot API has a moment when the real key lands in a header or URL of the outgoing HTTP request. That's injection. By default it happens in your application code — you literally write headers={"Authorization": f"Bearer {api_key}"}. The key then lives in the process's memory, in environment variables, sometimes in command-line arguments visible via ps aux to any user on the same machine.
The idea behind a credential proxy is to move that moment out of the application and onto a separate server whose sole job you trust: substituting the key. The application never sees the real value at all — it works with a revocable token, and injection happens on the proxy, at the last moment before the request goes out to the provider.
Where the substitution actually happens
| Approach | Where the real key lives | What the consumer sees |
|---|---|---|
| In application code (the default) | Process memory, .env, config | The full real key |
| An nginx/Lua rule | Server config or the container's environment | Nothing — but the same key for everyone who reaches the server |
| A homegrown middleware | A database or secrets store you maintain yourself | Depends on the implementation — usually no per-consumer revocation or limits |
| Credential proxy (proxykey) | Encrypted (AES-256-GCM), decrypted only in memory for one request | A revocable token (vlt_…), one per consumer |
Formats you need to be able to inject into
Providers put the key in different places in the request, and a proxy that only knows how to inject into Authorization: Bearer won't cover half of real integrations:
- Bearer header — the most common case: OpenAI, Anthropic, most REST APIs.
Authorization: Bearer sk-… - A custom header — e.g.
X-Api-Keyfor providers that don't use Bearer. - Query parameter — the key in the query string,
?api_key=…. Common with older or simpler APIs. - URL path — the edge case: the Telegram Bot API puts the bot token right in the path (
/bot<token>/sendMessage) — there's no header at all.
proxykey supports all four formats at the provider-configuration level (bearer / header / query / path) — including special path parsing for Telegram, where the token is extracted straight from the URL instead of a header.
Why homegrown injection is usually worse than a purpose-built proxy
Technically you can build header substitution in nginx or a simple middleware in an evening. The gap shows up not at launch, but at the moment of an incident:
| Homegrown injection | Credential proxy | |
|---|---|---|
| A separate token per consumer | Usually not — one key for everyone | Yes, its own revocable token |
| Revoking one consumer's access | Requires changing config and reloading the service | One click, nothing else affected |
| Per-consumer request limits | Usually not implemented | rpm/rpd on every token |
| A log of who called when | Needs to be set up separately | Built in |
| Key in git/config history | Often — server config gets versioned too | The key never leaves encrypted storage |
Homegrown injection solves exactly one problem — "the key isn't in client code." It doesn't solve the fact that the same key is still shared across every consumer, with no way to revoke just one of them.
FAQ
How is injection different from a credential proxy?
Injection is the substitution itself. A credential proxy is a service that does that substitution properly: it stores the key encrypted, hands consumers revocable tokens, and logs every substitution.
Can I do injection in an existing nginx setup?
Technically yes, via proxy_set_header with a secret from a protected store. But that's a static substitution of the same key for everyone who reaches the server — no separate token per consumer, no revocation without a config reload, no per-consumer limits or logs.
Does injection work with streaming?
Yes, as long as the proxy doesn't buffer the body. The substitution happens in headers or the path before the request leaves for the provider — a streaming response (SSE, chunks) passes through unchanged.
Try it on one key
Add a key, get a pass, and switch one service — it takes a minute. Free, no card required.
Get started →