API key security best practices: keep keys on the server, one key per project
Whoever holds your API key can spend your balance. Eight practices — server-side use only, environment variables or a secrets manager, one key per project and environment, spending limits and expiry, IP and model allowlists, rotation, watching usage — plus what to do after a leak.
Whoever holds your API key can call models on your balance. Hold three lines: keys live only on servers; keys never go into front-end code, app packages or git repositories; and every key has a spending limit. Add expiry dates, an IP allowlist and a model allowlist, and even if one key leaks, the damage is capped at that key's limit.
How keys leak
| Common case | Why it is dangerous | What to do instead |
|---|---|---|
| In web or mini-program front-end code | Anyone can read it in the browser's developer tools | Have the front end call your back end, and the back end call the API |
| Bundled into a mobile app | App packages can be unpacked and searched for strings | Route through your back end as well |
| Committed to a git repository | It leaks as soon as the repo goes public or is cloned; deleting the file leaves it in history | Use environment variables; treat any committed key as leaked and replace it now |
| Pasted into chats, tickets or screenshots | After a few forwards nobody knows who has it | Mask it before sending |
| Full request headers in logs | Plain text in log systems is visible to many people | Redact before logging, keeping only the first and last few characters |
Eight practices
1. Call the API only from your servers
Browsers and apps call your own back end, which checks the login and applies rate limits, then calls Tutujin API with the key:
import express from 'express'
import OpenAI from 'openai'
const app = express()
app.use(express.json())
const client = new OpenAI({ baseURL: 'https://open.tutujin.com/v1', apiKey: process.env.TUTUJIN_API_KEY })
app.post('/ask', async (req, res) => {
// check your own session and rate-limit per user first
const resp = await client.chat.completions.create({
model: 'gpt-5.4-mini',
messages: [{ role: 'user', content: String(req.body.question ?? '').slice(0, 2000) }],
max_tokens: 500,
})
res.json({ answer: resp.choices[0].message.content })
})
app.listen(3000)The key exists only in the server's environment; the browser only ever sees the answer.
2. Store keys in environment variables or a secrets manager
# set it on the server, or in a .env file that exists only on the server
export TUTUJIN_API_KEY="sk-your-key"
# make sure .env never gets committed
echo ".env" >> .gitignoreLarger teams can keep keys in a cloud provider's secrets manager or Vault and inject them at deploy time; code only reads them and never hard-codes them.
3. One key per project and per environment
For example, separate keys for "support-bot-prod" and "support-bot-test". When something goes wrong you disable one key without touching anything else, and the Usage page shows what each project spends.
4. Set a spending limit and an expiry date
The spending limit is the most a key can ever spend; it stops working once the limit is reached. Expiry dates suit temporary projects, contractors and demo environments: the key dies on schedule and nobody has to remember to delete it.
5. Turn on the IP allowlist
Only your servers' outbound IPs may call; everything else is rejected with a 403. A stolen key is then useless on someone else's machine. If your servers' outbound IP changes, update the allowlist too.
6. Turn on the model allowlist
Allow only the models your product needs; calls to anything else return a 403. A leaked key then cannot be used to call expensive models.
7. Rotate keys
Replace keys on a schedule, say every quarter: create the new key, update the server, confirm calls work, then delete the old one. Rotate as well when someone leaves the team or a contractor project ends.
8. Watch the usage
If the Usage page shows models you never use, calls in the middle of the night or a sudden spike, disable the key first and investigate second.
If you suspect a leak
- Disable or delete the key in the console.
- Create a new key, update the server configuration and redeploy.
- Check the calls and charges made during the leak on the Usage page.
- Find how it leaked: git history, logs, front-end code, chat records.
- If there are unexpected charges, contact WeChat support at
JsnonoChatwith the time range and the request id from the error message.
Keys in the console
The key list in the console shows masked keys and reveals the full key only on request. Put a new key straight into your server's environment or secrets manager; do not keep it in a text file on your laptop or in a chat.
Header formats are in Authentication and base URL, and what 401 and 403 mean is in Errors and retries. Spending limits and allowlists also help control costs — see How to control API costs.
FAQ
What should I do if a key leaks?
Disable or delete the key in the console right away, create a new one and update your server configuration, then check the calls made during the leak on the Usage page. If you see unexpected charges, contact WeChat support at JsnonoChat with the time range and the request id from the error message.
Can a web page or mobile app call the API directly?
We advise against it. Front-end code and app packages are visible to users, so a key inside them will leak sooner or later. Have the front end call your own back end, and let the back end call Tutujin API with the key.
What goes in the IP allowlist?
The public outbound IP addresses of the servers that call the API; you can add several IPs or ranges. Requests from anywhere else are rejected with a 403.
Why does the console show only part of each key?
The key list shows masked keys and reveals the full key only when you ask for it, which makes accidental leaks through screen sharing or screenshots less likely.