Python can send email with nothing installed. The standard library ships `smtplib` for the SMTP connection and `email.message.EmailMessage` for building the message, so a working script is about fifteen lines. Below is that script with TLS done correctly, then the case for moving to an HTTP API once real traffic shows up.

## How do you send email with the standard library?

`EmailMessage` builds the message and sets headers for you. `smtplib.SMTP_SSL` opens an encrypted connection on port 465:

```python
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "you@yourdomain.com"
msg["To"] = "recipient@example.org"
msg["Subject"] = "Your receipt"
msg.set_content("Thanks for your order. This is the plain-text version.")
msg.add_alternative("<p>Thanks for your order.</p>", subtype="html")

with smtplib.SMTP_SSL("smtp.example.com", 465) as server:
    server.login("smtp-user", "smtp-password")
    server.send_message(msg)
```

`set_content` writes the plain-text body and `add_alternative` attaches the HTML version, so clients pick what they can render. Sending both parts is good practice and helps with [deliverability](/products/email/deliverability).

## SSL or STARTTLS?

Two encryption paths exist and they use different ports. `SMTP_SSL` on 465 negotiates TLS from the first byte. The alternative is plain `SMTP` on 587 followed by `starttls()`, which upgrades a cleartext connection:

```python
with smtplib.SMTP("smtp.example.com", 587) as server:
    server.starttls()
    server.login("smtp-user", "smtp-password")
    server.send_message(msg)
```

Pick one based on what your mail server supports. Do not send credentials over an unencrypted connection. Keep the username and password in environment variables, read with `os.environ`, not hard-coded.

## How do you add an attachment?

`add_attachment` takes the bytes plus a MIME type. Read the file in binary mode:

```python
with open("invoice.pdf", "rb") as f:
    msg.add_attachment(
        f.read(),
        maintype="application",
        subtype="pdf",
        filename="invoice-1042.pdf",
    )
```

## Why move off raw SMTP for production?

The script works, but production adds problems the standard library does not solve. You need a mail server with a clean IP reputation, correct [SPF, DKIM, and DMARC](/docs/guides/email/dkim-spf-dmarc) records, retry logic for transient failures, and a way to see [bounces](/blog/what-are-bounced-emails) and complaints. An HTTP API folds all of that in. You authenticate with a key, send JSON, and read structured responses instead of parsing SMTP status codes.

## Send it with Bird

The [Bird Python SDK](/docs/sdks/python) sends the same message without the SMTP plumbing:

```python
import bird

client = bird.Bird(api_key=os.environ["BIRD_API_KEY"])

data, error = client.emails.send(
    from_="you@yourdomain.com",
    to="delivered@bird.dev",
    subject="Hello from Python",
    html="<p>It works.</p>",
)
```

The `delivered@bird.dev` sandbox address always accepts mail, so the first run confirms your key and payload before you point at real recipients. The SDK returns a `(data, error)` pair, which keeps error handling explicit rather than catching SMTP exceptions. If PHP is more your language, the [PHPMailer walkthrough](/blog/sending-email-with-phpmailer) covers the equivalent in that ecosystem.

## FAQ

### Do I need to install anything to send email in Python?

No. `smtplib` and `email.message` are in the standard library, so a basic SMTP send needs no packages. You only add a dependency when you want a higher-level client or an HTTP API.

### What is the difference between port 465 and 587?

Port 465 expects TLS from the start, which `smtplib.SMTP_SSL` handles. Port 587 starts in cleartext and upgrades with `starttls()`. Both end up encrypted; they just negotiate it at different points.

### When should I switch from smtplib to an API?

When you care about deliverability, retries, and event tracking. The standard library sends a message; it does not manage reputation or report what happened after the handoff. An API like [Bird email](/products/email) covers that layer.

For the full request and response shape, the [sending email guide](/docs/guides/email/sending-email) walks through the API end to end.