You’re encountering a 401 Unauthorized error when trying to send emails via the Mailgun API using PHP and cURL. This usually means there’s a problem with how you’re authenticating with the API or how you’re structuring your request. The error prevents your application from sending emails, hindering your automated email functionality.
TL;DR: The Quick Fix:
The main issue is likely a combination of incorrect authentication and using the wrong data format for the request. Correct your CURLOPT_USERPWD setting to only include 'api:' before your API key, and send the payload as form data, not JSON. Remove the Content-Type header.
Example Code (Corrected):
<?php
$subscriber_email = '[email protected]';
$my_api_key = 'actual_key_value_here'; // Replace with your actual API key
$full_auth = 'api:' . $my_api_key;
$endpoint = 'https://api.mailgun.net/v3/your_domain.com/messages'; // Remember to replace 'your_domain.com'
$payload = [
'from' => 'your_email@your_domain.com', // Your verified sender email
'to' => $subscriber_email,
'subject' => 'Test Email',
'text' => 'This is a test email sent via Mailgun API and PHP cURL.'
];
$curl = curl_init($endpoint);
curl_setopt($curl, CURLOPT_USERPWD, $full_auth);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // Consider removing for production
curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($response === false) {
echo 'cURL Error: ' . curl_error($curl);
} else {
echo 'Response Code: ' . $http_code . "\n";
echo 'Response Body: ' . $response . "\n";
}
curl_close($curl);
?>
Understanding the “Why” (The Root Cause):
The 401 Unauthorized error indicates the Mailgun API rejected your authentication credentials or request format.
Authentication: The CURLOPT_USERPWD option expects the format 'api:YOUR_API_KEY'. Adding a 'user:' prefix is incorrect and causes authentication failure.
Data Format: The Mailgun API for sending messages expects application/x-www-form-urlencoded data, not JSON. Using json_encode and setting a Content-Type: application/json header will lead to rejection. PHP arrays are automatically encoded to the correct format using CURLOPT_POSTFIELDS.
Step-by-Step Guide:
Correct Authentication: Ensure your $full_auth variable contains only 'api:' . $my_api_key and that $my_api_key holds your actual Mailgun API key. Double-check that this key is active and associated with the correct domain.
Use Form Data: Remove any Content-Type header and replace json_encode with a PHP array for CURLOPT_POSTFIELDS. The example code above demonstrates this correctly.
Verify Domain: In the $endpoint URL, make sure you’re using the correct Mailgun domain. It should be in the format https://api.mailgun.net/v3/your_domain.com/messages, replacing your_domain.com with your actual Mailgun domain.
Handle Errors: The improved example includes error handling using curl_error(). Always check for cURL errors and examine the HTTP response code and body for more detailed error messages from the Mailgun API.
Test Thoroughly: Send a test email. Check the Mailgun dashboard and logs to see if the email was successfully sent or if you receive any additional error messages.
Common Pitfalls & What to Check Next:
API Key Permissions: Verify that your API key has the necessary permissions to send emails.
Network Connectivity: Ensure your server can reach the Mailgun API.
Mailgun Server Status: Check the Mailgun status page to rule out API outages.
Rate Limits: If sending many emails, monitor for rate limiting. Implement delays if needed.
Sandbox Mode: If testing, use your Mailgun sandbox domain. Avoid sending emails to real recipients during testing.
Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!
You are receiving a 401 Unauthorized error when attempting to create a Mailgun mailing list using PHP and cURL. This indicates a problem with your API key authentication. Your provided code uses incorrect authentication parameters and sends a JSON payload when Mailgun expects form data.
TL;DR: The Quick Fix:
Replace your PHP curl_setopt for CURLOPT_USERPWD and CURLOPT_POSTFIELDS, and remove the Content-Type header. The corrected code should look like this:
The 401 Unauthorized error arises from two key mistakes in your original PHP code:
Incorrect Authentication: You were adding an extra 'user:' prefix to your API key within CURLOPT_USERPWD. Mailgun’s API expects the authentication string to be in the format api:YOUR_API_KEY. Your code was sending user:api:YOUR_API_KEY, which is incorrect.
Content-Type Mismatch: The working cURL command uses -F flags, which sends form data. Your PHP code, however, used json_encode and a Content-Type: application/json header. The Mailgun lists endpoint requires multipart/form-data for creating lists; it does not accept JSON in this context.
Step-by-Step Guide:
Correct the Authentication: Modify the CURLOPT_USERPWD setting in your PHP code. Ensure you are only providing 'api:' concatenated with your API key. The corrected line should be curl_setopt($curl, CURLOPT_USERPWD, $full_auth); where $full_auth is defined as 'api:' . $my_api_key;.
Switch to Form Data: Change how you send your data. Instead of using json_encode, use a standard PHP array for CURLOPT_POSTFIELDS. Remove the CURLOPT_HTTPHEADER setting entirely, as cURL will automatically handle the multipart/form-data encoding.
Verify API Key: Double-check that you are using the correct API key for your Mailgun domain. Make sure it hasn’t expired and that it’s associated with the correct Mailgun account. A simple echo $full_auth; before your curl_init can confirm the correct string is being sent.
Test and Iterate: After implementing these changes, test your code again. If you still encounter issues, carefully review the response from the server and the HTTP status code for more specific clues.
Common Pitfalls & What to Check Next:
API Key Scope: Verify your API key has the necessary permissions to create mailing lists. Some keys might be restricted to specific actions.
Network Connectivity: Confirm that your server has proper network access to the Mailgun API.
Mailgun Server Status: Check the Mailgun status page to ensure that there are no current outages or issues affecting the API.
Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!
Yeah, everyone spotted the auth issue, but there’s another problem lurking in your code. Your payload looks good now, but debugging API auth is always a pain.
I work with Mailgun constantly and stopped writing custom cURL months ago. Auth, error handling, retry logic - it’s way more complex than it needs to be.
Skip the PHP cURL headaches and just use Latenode instead. Build your mailing workflow visually, test each step, and actually see where things break.
Auth gets handled automatically once you drop in your API key. No more guessing if your credentials are formatted right or if Mailgun changed something.
When you need to add subscribers or send campaigns later, everything’s already connected. No more auth debugging sessions.
check if your api key expired or using it for the wrong domain. mailgun keys are domain-specific - if created for sandbox.mailgun.org but hitting a different endpoint, 401 even with the right auth format.
Your auth string is the problem. When you use curl_setopt($curl, CURLOPT_USERPWD, 'user:' . $full_auth); you’re sending ‘user:api:your_actual_key’ as credentials - Mailgun rejects that. I hit this exact issue six months ago migrating from command line to PHP. Just use curl_setopt($curl, CURLOPT_USERPWD, $full_auth); instead. No extra prefixes needed. For the payload, Mailgun wants form-encoded data, not JSON. Ditch the json_encode and use a plain array. Remove that content-type header too - let curl handle the multipart encoding. Pro tip: echo your auth string during testing to make sure it’s actually what you expect.
That 401 error means your auth header is messed up. You’re concatenating the credentials wrong - CURLOPT_USERPWD just wants ‘username:password’ format. For Mailgun, that’s ‘api:your_key’. Right now you’re sending ‘user:api:your_key’ and Mailgun doesn’t know what to do with that. Also, you’re sending JSON but the working curl example uses form fields. Mailgun’s list creation endpoint wants form-encoded data, not JSON. Drop the JSON content-type header and send your data as a plain array instead of json_encode. I hit this exact same issue migrating some old scripts - the auth format difference between curl and PHP gets everyone at first.
Your CURLOPT_USERPWD line has ‘user:’ prefix, but you’ve already got ‘api:’ in your $full_auth variable. You’re sending ‘user:api:your_key’ instead of ‘api:your_key’. Drop the ‘user:’ part.
Second issue: content type mismatch. Your curl command uses -F flags (form data), but your PHP sends JSON with application/json header. Mailgun wants multipart/form-data for this endpoint. Switch to a regular array and ditch the JSON content-type header. Send it as key-value pairs, not encoded JSON.
I’ve hit similar auth issues with APIs before. Double-check your API key format too - sandbox and live keys sometimes have subtle differences that aren’t obvious.
you’re mixing up your auth - just use curl_setopt($curl, CURLOPT_USERPWD, 'api:' . $my_api_key); without the ‘user:’ part. mailgun expects form data, so ditch the json_encode and remove that content-type header.