Practical guide

Mailgun “401 Forbidden”: wrong key, wrong region or wrong user

Mailgun's 401 body is the plain text “Forbidden”, not JSON, so code that calls response.json() gets a second, confusing error. We tested both regions, the official SDK and SMTP with made-up keys.

The short answer. Mailgun returns 401 Forbidden when it does not accept the API key for that request. Check that the user is api, that the key is current, that you use the EU host api.eu.mailgun.net for a domain created in the EU region, and that a domain sending key is used only for that domain. The body is plain text, so do not parse it as JSON.

Formgong

Mailgun 401 Forbidden: why the API rejects your key
Mailgun “401 Forbidden”: wrong key, wrong region or wrong user

What Mailgun answered

On 16.10.2026 we posted a message to /v3/<domain>/messages with a made-up key on both of Mailgun's API hosts. Each answered HTTP 401, content-type: text/plain, and a body of one word: Forbidden. A request with no key at all got the same answer.

Three ways it showed up in code:

  • fetch plus response.json() in Node: SyntaxError: Unexpected token 'F', "Forbidden" is not valid JSON. The real problem, the key, is hidden behind a parse error. See why JSON parse errors look like this.
  • mailgun.js 14.1.0: an error with message: "Unauthorized", status: 401 and details: "Forbidden". With the key variable unset it threw Parameter "key" is required before sending anything.
  • SMTP through smtp.mailgun.org with a made-up password: Invalid login: 535 Authentication failed.

What to check

A made-up key cannot tell these apart, because Mailgun gives the same one-word answer for all of them. These are the causes Mailgun's docs describe, in the order worth checking:

  1. Region. A domain created in the EU region lives on api.eu.mailgun.net. Calling api.mailgun.net for it fails. In mailgun.js, pass url: "https://api.eu.mailgun.net".
  2. User name. Basic auth uses the user api and the key as the password.
  3. Key type. Mailgun's docs say a domain sending key only allows POST to /messages and /messages.mime, for the domain it was created for. Other endpoints, or another domain, need the account's API key.
  4. A replaced key. After you create a new key and delete the old one, every place that still has the old one gets 401.

Do not call Mailgun from the page

Unlike SendGrid, Mailgun's API answered the browser preflight with Access-Control-Allow-Origin: * and allowed the Authorization header. So a call from front-end code works, and that is the danger: the key is then in your page for anyone to copy, and it can send mail as your domain. Keep the call on a server or in a function.

Do you need a Mailgun key for a contact form?

If the API key is in your project only so a contact form can email you, a form that posts to Formgong at https://formgong.com/submit needs no key from a mail provider, and Formgong sends the notification. See the HTML guide.

Frequently asked questions

Why does Mailgun return 401 Forbidden?

It does not accept the key for that request: the key is wrong or deleted, the user is not api, the domain is in the other region, or a domain sending key is used for something else.

Why do I get Unexpected token F is not valid JSON from Mailgun?

Mailgun's 401 body is the plain text Forbidden. Calling response.json() on it fails. Check response.ok first and read the error body as text.

Which Mailgun API URL should I use in the EU?

Domains created in the EU region use api.eu.mailgun.net. Others use api.mailgun.net.

Sources and documentation

Official references for this guide: Mailgun: API overview (US and EU base URLs), Mailgun: Authentication (user api, domain sending keys), mailgun.js on npm. HTML contact form, where data is stored.

Rate this guide

Your rating

We read every rating. A comment appears here only after we approve it.

← Back to the blog