"Send email API JavaScript" and "Python email API" are searches from developers who want working code, not a feature matrix. The practical question is how to get a message from an application to an inbox in Node.js, Python or PHP, with proper authentication, errors handled and secrets kept out of the repository. This guide gives you minimal, correct SMTP examples for each language that connect to smtp.migosmtp.com on port 587 with STARTTLS, explains the pieces around them (credentials, from addresses, HTML and plain text, attachments, retries and logging), and points to our API reference for the HTTPS endpoints so that you use the exact request format from the documentation rather than from an article. We use SMTP in the examples because it works with the standard libraries each ecosystem already has and keeps your code portable between providers. The credentials shown are placeholders; use the values from your dashboard.
Before you write code
- Verify your sending domain. Add the SPF and DKIM records from your dashboard domain settings. Copy them from the dashboard rather than from an article, and check them with the SPF, DKIM and DMARC checker. See authenticating your sending domain.
- Create credentials in the dashboard and store them as environment variables, for example
SMTP_USERandSMTP_PASS. - Choose a From address on your own domain, such as
[email protected], with a monitored reply path. - Confirm outbound port 587 is open from your server. Some hosts block it. See which SMTP port to use.
Node.js with Nodemailer
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: "smtp.migosmtp.com",
port: 587,
secure: false, // STARTTLS upgrades the connection
requireTLS: true,
auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
});
export async function sendEmail({ to, subject, text, html }) {
const info = await transporter.sendMail({
from: "Example <[email protected]>",
to, subject, text, html,
});
return info.messageId;
}
Use secure: false with requireTLS: true for port 587; that means the connection starts in plain text and is upgraded with STARTTLS before credentials are sent. Create the transporter once and reuse it. The full walk-through, including pooling and errors, is in the Nodemailer guide.
Python with smtplib
import os, smtplib, ssl
from email.message import EmailMessage
def send_email(to, subject, text, html=None):
msg = EmailMessage()
msg["From"] = "Example <[email protected]>"
msg["To"] = to
msg["Subject"] = subject
msg.set_content(text)
if html:
msg.add_alternative(html, subtype="html")
context = ssl.create_default_context()
with smtplib.SMTP("smtp.migosmtp.com", 587, timeout=20) as server:
server.starttls(context=context)
server.login(os.environ["SMTP_USER"], os.environ["SMTP_PASS"])
server.send_message(msg)
return msg["Message-ID"]
ssl.create_default_context() validates the server certificate, which you should never switch off to "make it work". The timeout prevents a stuck connection from hanging your worker. Python's EmailMessage builds correct multipart messages, so you do not need to assemble MIME by hand.
PHP with PHPMailer
use PHPMailerPHPMailerPHPMailer;
use PHPMailerPHPMailerException;
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'smtp.migosmtp.com';
$mail->SMTPAuth = true;
$mail->Username = getenv('SMTP_USER');
$mail->Password = getenv('SMTP_PASS');
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('[email protected]', 'Example');
$mail->addAddress($to);
$mail->Subject = $subject;
$mail->isHTML(true);
$mail->Body = $html;
$mail->AltBody = $text;
$mail->send();
} catch (Exception $e) {
error_log('Mail error: ' . $mail->ErrorInfo);
}
If your site runs on WordPress or another CMS, a plugin or built-in setting may be easier than code. See WordPress SMTP setup.
Sending through the HTTPS API
If you prefer HTTP requests to SMTP, use our email API. The exact endpoint, authentication header and JSON fields are documented in the API reference; follow that page, not examples from other services, because request formats differ between providers. For the pros and cons of each route, see email API vs SMTP relay.
Details that matter in every language
Always send a plain-text part
A multipart message with text and HTML is more compatible and tends to be treated more favourably than HTML only. Keep the two versions equivalent.
Use a real From and a Reply-To you monitor
Send from a domain you have authenticated. If replies should go elsewhere, set Reply-To explicitly. See no-reply addresses.
Prevent header injection
Never put raw user input into headers such as To, Subject or From. Validate addresses and strip line breaks. Most libraries reject newlines in headers, but check yours. Use email validation at signup, and see the free email validator.
Attachments
Keep attachments small, set the right MIME type and consider linking to a file instead for anything large. Providers and recipients impose size limits that differ; check them for your use case.
Timeouts, retries and idempotency
| Failure | What to do |
|---|---|
| Connection timeout or temporary error (4xx) | Retry with exponential backoff and a limit |
| Permanent error (5xx) such as an invalid recipient | Do not retry; record the failure and suppress the address if appropriate |
| Authentication failure | Alert the team; do not retry |
| Unknown outcome after sending | Check logs before retrying to avoid duplicates |
Do the sending in a background job or queue, not inside a web request, so that a slow connection never blocks your user. See SMTP bounce codes explained for interpreting errors.
Logging
Log the message identifier, recipient domain and outcome, but not full bodies or secrets. Store the identifier in your database so you can match later events from webhooks. See webhooks.
Structure your sending code
Whichever language you use, resist calling the mail library from many places. Create one module with a function such as sendEmail that takes a small, provider-neutral object: recipient, subject, text, HTML and optional metadata such as a template name or user identifier. Everything else, including the host, port, credentials and library calls, lives behind that function. This gives you a single place to add logging, rate limiting, retries and suppression checks, and it means that a later change of provider, library or transport is a change to one file. It also makes testing simple, because your tests can replace the module with a fake that records messages instead of sending them.
Keep message construction separate from sending. Build subject and bodies in template functions that return text and HTML, then pass them to the sender. Store templates in version control, escape any user-supplied values inserted into HTML and keep a plain-text version that reads well on its own. When a message is triggered by a user action, such as a password reset, put it on a queue and return to the user straight away. A background worker then sends it, retries temporary failures and records the result. This pattern keeps your application responsive when the mail connection is slow and prevents duplicate sends when a user refreshes a page.
Rate limits and bursts
Sending thousands of messages in a tight loop invites throttling and can look abusive to receiving servers. Use a queue with a worker that sends at a steady pace, and spread large batches over time. If you are new to a sending domain, increase volume gradually; see the warm-up guide. For marketing sends, keep them away from your transactional queue so that a large campaign never delays a password reset.
Test safely
- In development, use a sandbox or a mail-catching tool so that you never email real users. See testing email in staging.
- Before launch, send to your own mailboxes at several providers and read the authentication results in the headers. See reading email headers.
- Start with low volume and increase gradually. See warm-up guide.
Before you go live
- Send a test message from the production environment to mailboxes at several providers and confirm SPF, DKIM and DMARC all pass in the headers.
- Confirm that bounces and complaints reach your system, either through webhooks or by reviewing the dashboard, and that hard-bounced addresses are not retried.
- Check that the reply address is monitored and that unsubscribe links work for any marketing mail.
- Set an alert for authentication failures and for a sudden drop in successful sends, so that a rotated password or expired key is noticed in minutes, not days.
- Write down where the credentials are stored and who can rotate them.
Common mistakes
- Hard-coding credentials in the repository.
- Disabling certificate checks to get past an error.
- Using port 465 or 25 with settings meant for 587. Use the relay on 587 with STARTTLS as shown.
- Creating a new connection for every email in a loop; reuse a connection or queue the work.
- Sending HTML only.
- Sending from an unauthenticated domain or a free mailbox address.
- Ignoring errors, so failed sends disappear silently.
Send your first message Create an account, verify your domain and use the SMTP settings above, or follow the API reference for HTTPS requests.
References and further reading
Frequently asked questions
Which port should I use to send email from my app?
Use port 587 with STARTTLS on smtp.migosmtp.com. Do not switch off certificate validation to work around errors.
Should I use the SMTP relay or the HTTPS API?
SMTP works with the standard library of nearly every language and keeps code portable. The HTTPS API offers richer per-message options. Follow the API reference for exact request formats.
Why should I send both text and HTML?
A multipart message is more compatible with email clients and filters than HTML only. Keep both versions equivalent.
How should I handle sending errors?
Retry temporary errors with exponential backoff, do not retry permanent errors, alert on authentication failures and check logs before retrying unknown outcomes to avoid duplicates.
How do I keep credentials safe?
Store them in environment variables or a secret manager, never in source control or client-side code, and use separate credentials for each environment.