A VerifyAnyEmail key (they start with vae_) is a bearer credential. “Bearer” means possession is the whole authorisation: the server trusts whoever presents the key, with no second factor. That makes keys convenient and dangerous in equal measure. The good news is that keeping them safe is mostly about a few disciplined habits, not exotic security engineering.
1. Keep keys server-side — always
The most common and most damaging leak is putting a key somewhere the public can read it. If your key is in front-end code, it is compromised the moment the page loads — “minified” or “obfuscated” changes nothing, because anyone can open dev tools and read the network request. The same goes for mobile apps: a shipped binary can be decompiled, and any string inside it, including your key, can be extracted. Treat all of these as public:
- Browser JavaScript, React/Vue/Svelte bundles, and anything under a
NEXT_PUBLIC_/VITE_/REACT_APP_prefix (those are deliberately exposed to the client — never put a secret there). - Mobile app binaries (iOS/Android), desktop apps, and browser extensions.
- Single-page apps calling the API directly from the user’s browser.
The fix is always the same: put a small server between the client and the API. The browser calls your endpoint; your server, holding the key, calls VerifyAnyEmail and returns only the result. In Next.js that’s a Route Handler; in Node it’s an Express route. This is exactly the pattern in verifying email in Node.js & Express — the client never sees the key.
// server-side only — e.g. a Next.js Route Handler or Express route
export async function POST(req) {
const { email } = await req.json();
const res = await fetch("https://api.verifyany.email/v1/verify", {
method: "POST",
headers: {
// read from the environment, never hard-code
Authorization: "Bearer " + process.env.VAE_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
});
const { result } = await res.json();
// return only what the client needs — not the raw upstream response
return Response.json({ status: result.status, score: result.score });
}2. Load keys from environment variables or a secret store
Never hard-code a key in source. Read it from an environment variable (process.env.VAE_API_KEY) so the secret lives in configuration, not code. Keep it out of your repository with a .env file that is listed in .gitignore, and commit a .env.example with the variable name but a placeholder value so teammates know what to set.
# .env (git-ignored — never commit this)
VAE_API_KEY=vae_live_xxxxxxxxxxxxxxxxxxxx
# .env.example (safe to commit)
VAE_API_KEY=vae_your_api_key_here# .gitignore
.env
.env.local
.env.*.localFor anything beyond a hobby project, graduate from plain .env files to a managed secret store: your hosting platform’s environment settings, or a dedicated vault such as AWS Secrets Manager, Google Secret Manager, HashiCorp Vault, or Doppler. These inject the secret at runtime, control who can read it, and give you an audit trail — so the key never sits in a file on disk or in a developer’s laptop.
3. Never commit a key to git
Git remembers everything. Committing a key and then deleting it in a later commit does not remove it — it stays in the history, readable by anyone who clones the repo, and automated scanners crawl public repositories for exactly this. If you catch it before pushing, amend or reset the commit. If it has already been pushed anywhere others can see it, treat the key as burned and rotate it (see below) — scrubbing history is worth doing, but rotation is the part that actually stops the bleeding. Add a pre-commit secret scanner (gitleaks, trufflehog, or GitHub’s push protection) so a key never reaches a commit in the first place.
4. Scope keys and restrict by IP
Give each use its own key rather than sharing one everywhere. Separate keys for production, staging and local development mean a leak in one place doesn’t compromise the others, and you can revoke one without disrupting the rest. Where the API supports scoped or restricted keys, grant each key only the permissions it needs — a key that only verifies addresses shouldn’t also be able to change account settings.
If your traffic comes from stable, known servers, add an IP allowlist so a key only works from those addresses. This is one of the strongest mitigations available: even if the key leaks, it’s useless from an attacker’s machine because requests from other IPs are rejected. Because your calls originate from your server (rule 1), the allowlist is just your server’s egress IPs — easy to maintain.
5. Rotate on a schedule
Keys should not live forever. Rotate them periodically — quarterly is a reasonable default — and immediately whenever someone with access leaves the team or you suspect exposure. To rotate without downtime: create a new key, deploy it to your environment, confirm traffic is flowing on the new key, then revoke the old one. Supporting two valid keys briefly makes rotation a non-event instead of an outage.
6. Rotate a leaked key immediately
If a key is exposed — pushed to a public repo, pasted into a ticket or chat, logged somewhere shared, or just suspected — act fast, in this order:
- Generate a new key in your dashboard and deploy it to your environment / secret store.
- Revoke the leaked key so it stops working everywhere at once.
- Check usage for calls you don’t recognise, and review your credit balance and recent verifications for anything unexpected.
- Scrub the source if it landed in git history, and add scanning so it can’t recur.
Rotation is cheap and instantly effective; hesitating is what turns a leak into abuse. A separate but related credential — your webhook secret — deserves the same treatment: rotate it if exposed, and verify every webhook with it as shown in how to verify webhook signatures correctly.
7. Keep keys out of logs and URLs
The key belongs in the Authorization header, never in a URL or query string — URLs get logged by servers, proxies and browser history, quietly leaking the secret. Scrub keys from your application logs and error trackers too; it’s easy to accidentally log a whole request object, secret header and all. And never send a key to any endpoint other than the official API host.
Practical checklist
- Key lives only on the server — never in browser, mobile, or JS bundle code.
- Loaded from an environment variable or secret store, never hard-coded.
.envis in.gitignore; only.env.examplewith placeholders is committed.- A secret scanner runs pre-commit / on push.
- Separate keys per environment (prod / staging / dev).
- IP allowlist enabled for stable server IPs where supported.
- Rotation scheduled (e.g. quarterly) and a documented rotate-on-leak runbook.
- Keys never appear in URLs, query strings, or logs.
Where to go next
The server-side call patterns that keep a key safe are shown per-language in the API docs and in verifying email in Node.js & Express. If you’re processing lists in bulk, the same key hygiene applies throughout how to process large verification batches. To try verification without touching a key at all, use the Email Verifier.
Sources
The bearer-token scheme — why possession of the token is the authorisation and why it must be protected in transit and at rest — is defined in RFC 6750. OWASP’s Secrets Management Cheat Sheet covers storage, rotation and leak response in more depth.