API keys & environments
Every request carries a key in the x-api-key header. The key decides who you are, what you may
do, and whether anything real happens.
x-api-key: msg_live_a6bd5aea...Test and live
The environment is in the prefix, so it is obvious in a log or a config file. A msg_test_ key
runs the same code as a live one but reaches nobody, so you can build the whole integration before
anything is approved or anyone is messaged.
What each operation does under a test key — and the one that behaves the same either way — is on Test keys and the sandbox.
An environment cannot be changed after a key is created. Create a new key instead.
Scopes
A key gets one or more. Give each key the least it needs.
| Scope | Lets the key |
|---|---|
otp:send | Use Verify — send and check codes |
messages:send | Send messages and read their status |
templates:manage | Create, validate and update templates |
read | Read-only access |
A call outside a key's scopes fails with insufficient_scope.
Per-key limits
Each key can carry its own ceilings, set when you create it — see Set up your API access.
- Requests per minute — burst control. Breaching it returns
rate_limitedwith aRetry-After, and the SDK retries for you. - Monthly messages — messages per billing cycle. Breaching it returns
key_limit_reached, which does not clear by waiting — raise the key's limit. - Spend cap — a ceiling on what this key can run up on your Meta bill. Breaching it returns
spend_cap_reached.
These sit inside your plan's allowance, not on top of it: ten keys at 5,000 messages each on a plan allowing 15,000 means the plan runs out first. See Rate limits & quotas.
One key per customer
If you build software that many businesses use — clinic software, a booking platform, a CRM — issue each of them their own key rather than sharing one.
| One shared key | A key per customer |
|---|---|
| One usage number for everyone | Usage per customer, for billing or support |
| One rate limit, shared | A noisy customer cannot starve the rest |
| A leak means rotating for everyone | Revoke one, nobody else notices |
| "Which customer sent this?" is unanswerable | Answered by the key on the request |
Give each key that customer's name and only the scopes they need, and store the secret against your customer record. Start them on a test key, then swap in a live one — nothing in their code changes.
Two things to watch. Per-key limits come out of the same plan allowance, so the console warns you when they sum past it; over-allocating on purpose is fine, as long as it is on purpose. And everyone shares your one sending number, so recipients see your business, not your customer's.
A webhook fires before a key reaches its monthly limit, so you can warn the customer or raise the limit before anything breaks.
Losing a key
The secret is shown once, at creation, and cannot be retrieved afterwards. If it is lost or leaked, rotate it — you get a new secret and the old one keeps working for a grace period so nothing breaks mid-deploy.
See Rotate a key safely.