> ## Documentation Index
> Fetch the complete documentation index at: https://ahasend.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> AhaSend stores all message data in the EU. See /facts.
> The API base URL is https://api.ahasend.com. Send the API key in the Authorization: Bearer header. See /api-reference/authentication.
> Use send.ahasend.com for SMTP. Ports 25, 587 and 2525 use STARTTLS. Port 465 is not supported. See /smtp.
> For send-only integrations, create an API Key v2 under Credentials → Add and scope it to messages:send:{your-domain}. See /send-api/credentials and /api-reference/scopes.

# Send Email in Python via SMTP (smtplib)

> Send email from Python with smtplib and AhaSend's SMTP relay: STARTTLS on port 587, HTML and plain-text messages, attachments, custom headers and sandbox tests.

Send emails through AhaSend's SMTP servers using Python's built-in [smtplib](https://docs.python.org/3/library/smtplib.html) and [email](https://docs.python.org/3/library/email.html) modules. This guide covers everything from basic setup to advanced features like HTML content, attachments, and custom headers.

## Prerequisites

Before sending, [verify your domain](https://ahasend.com/docs/domains.md) and [create an SMTP credential](https://ahasend.com/docs/smtp/credentials.md). Use a [sandbox credential](https://ahasend.com/docs/send-api/sandbox.md) for these examples so no email is delivered. Keep the username and password in your server's secret store.

Before you begin, ensure you have:

> [accordion] System Requirements
>
> * **Python installed** on your system ([Download Python](https://python.org/downloads/))
> * a supported Python 3 release (recommended)
> * Basic knowledge of Python programming

## Built-in Modules

Python's standard library includes everything you need for SMTP:

> [card] smtplib Module
>
> **Purpose:** SMTP client session object\\
>
> **Features:** Connection, authentication, sending\\
>
> **Security:** STARTTLS support\\
>
> **Import:** `import smtplib`

> [card] email Module
>
> **Purpose:** Email message construction\\
>
> **Features:** MIME, attachments, HTML\\
>
> **Classes:** EmailMessage, MIMEMultipart\\
>
> **Import:** `from email.message import EmailMessage`

## Connection Settings

| Setting | Value |
| - | - |
| Host | `send.ahasend.com` |
| Ports with STARTTLS | 25, 587 and 2525 |
| Port with implicit TLS | 465 (TLS from the first byte, also called SSL/TLS or SMTPS) |
| Encryption | Required on every port: STARTTLS on 25, 587 and 2525, implicit TLS on 465 |
| Authentication | AUTH PLAIN after STARTTLS on 25, 587 and 2525; AUTH PLAIN or AUTH LOGIN on 465 |
| Username and password | Your SMTP credential, not an API key |

All message data is stored in the EU. See [SMTP limits](https://ahasend.com/docs/smtp.md#what-are-the-smtp-limits).

## Basic Email Examples

```python Plain Text Email theme={null}
import smtplib
from email.message import EmailMessage

def send_plain_text_email():
    # SMTP server configuration
    smtp_server = "send.ahasend.com"
    smtp_port = 587
    smtp_username = "YOUR_SMTP_USERNAME"
    smtp_password = "YOUR_SMTP_PASSWORD"

    # Email details
    from_addr = "hello@yourdomain.com"
    to_addr = "user@example.com"
    subject = "Welcome to our platform!"
    body = "Thanks for signing up. We're excited to have you!"

    # Create the email message
    msg = EmailMessage()
    msg.set_content(body)
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr

    # Connect to the SMTP server and send
    try:
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()  # Secure the connection
            server.login(smtp_username, smtp_password)
            server.send_message(msg)

        print("Email sent successfully!")
    except Exception as e:
        print(f"Failed to send email: {e}")

# Send the email
send_plain_text_email()
```

```python HTML Email theme={null}
import smtplib
from email.message import EmailMessage

def send_html_email():
    # SMTP server configuration
    smtp_server = "send.ahasend.com"
    smtp_port = 587
    smtp_username = "YOUR_SMTP_USERNAME"
    smtp_password = "YOUR_SMTP_PASSWORD"

    # Email details
    from_addr = "hello@yourdomain.com"
    to_addr = "user@example.com"
    subject = "Welcome to our platform!"

    # Create the email message
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr

    # Set plain text content (fallback)
    msg.set_content("Welcome! Thanks for signing up. Visit https://yourdomain.com/get-started to get started.")

    # Add HTML alternative
    html_content = """
    <html>
    <body>
        <h1>Welcome!</h1>
        <p>Thanks for signing up. We're <strong>excited</strong> to have you!</p>
        <p><a href="https://yourdomain.com/get-started">Get Started</a></p>
    </body>
    </html>
    """
    msg.add_alternative(html_content, subtype='html')

    # Connect and send
    try:
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()
            server.login(smtp_username, smtp_password)
            server.send_message(msg)

        print("HTML email sent successfully!")
    except Exception as e:
        print(f"Failed to send email: {e}")

send_html_email()
```

```python With Attachments theme={null}
import smtplib
from email.message import EmailMessage

def send_email_with_attachment():
    # SMTP server configuration
    smtp_server = "send.ahasend.com"
    smtp_port = 587
    smtp_username = "YOUR_SMTP_USERNAME"
    smtp_password = "YOUR_SMTP_PASSWORD"

    # Email details
    from_addr = "hello@yourdomain.com"
    to_addr = "user@example.com"
    subject = "Your Invoice is Ready"

    # Create the email message
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr

    # Set content
    msg.set_content("Please find your invoice attached to this email.")

    # Add HTML alternative
    html_content = """
    <html>
    <body>
        <h1>Invoice Attached</h1>
        <p>Please find your invoice attached to this email.</p>
    </body>
    </html>
    """
    msg.add_alternative(html_content, subtype='html')

    # Add attachment
    attachment_path = "/path/to/invoice.pdf"
    try:
        with open(attachment_path, 'rb') as f:
            file_data = f.read()
            file_name = f.name.split('/')[-1]  # Get filename
            msg.add_attachment(file_data,
                             maintype='application',
                             subtype='pdf',
                             filename=file_name)
    except FileNotFoundError:
        print(f"Attachment file not found: {attachment_path}")
        return

    # Connect and send
    try:
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()
            server.login(smtp_username, smtp_password)
            server.send_message(msg)

        print("Email with attachment sent successfully!")
    except Exception as e:
        print(f"Failed to send email: {e}")

send_email_with_attachment()
```

> [!TIP]
>
> **Pro Tip:** Python's built-in modules are powerful and reliable. Use context managers for connection handling, always provide both HTML and text versions, and implement proper error handling with retry logic. Start with sandbox mode during development and use environment variables for credentials.

## Testing with Sandbox Mode

Use sandbox mode to safely test your Python email integration:

```python Sandbox Test theme={null}
import smtplib
from email.message import EmailMessage

def send_sandbox_email():
    # SMTP server configuration
    smtp_server = "send.ahasend.com"
    smtp_port = 587
    smtp_username = "YOUR_SMTP_USERNAME"
    smtp_password = "YOUR_SMTP_PASSWORD"

    # Email details
    from_addr = "test@yourdomain.com"
    to_addr = "test@example.com"
    subject = "Sandbox Test Email"

    # Create the email message
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr

    # Enable sandbox mode
    msg['AhaSend-Sandbox'] = 'true'
    msg['AhaSend-Sandbox-Result'] = 'deliver'

    msg.set_content("This email is sent in sandbox mode for testing.")

    # Connect and send
    try:
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()
            server.login(smtp_username, smtp_password)
            server.send_message(msg)

        print("Sandbox email sent successfully!")
    except Exception as e:
        print(f"Failed to send sandbox email: {e}")

send_sandbox_email()
```

```python Sandbox Bounce Test theme={null}
import smtplib
from email.message import EmailMessage

def send_sandbox_bounce_test():
    # SMTP server configuration
    smtp_server = "send.ahasend.com"
    smtp_port = 587
    smtp_username = "YOUR_SMTP_USERNAME"
    smtp_password = "YOUR_SMTP_PASSWORD"

    # Email details
    from_addr = "test@yourdomain.com"
    to_addr = "test@example.com"
    subject = "Sandbox Bounce Test"

    # Create the email message
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr

    # Enable sandbox mode with bounce simulation
    msg['AhaSend-Sandbox'] = 'true'
    msg['AhaSend-Sandbox-Result'] = 'bounce'

    # Set content
    msg.set_content("This email simulates a bounce for webhook testing.")

    # Add HTML content
    html_content = """
    <html>
    <body>
        <h1>Bounce Test</h1>
        <p>This email simulates a bounce for webhook testing.</p>
    </body>
    </html>
    """
    msg.add_alternative(html_content, subtype='html')

    # Connect and send
    try:
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()
            server.login(smtp_username, smtp_password)
            server.send_message(msg)

        print("Sandbox bounce test sent - check webhooks!")
    except Exception as e:
        print(f"Failed to send sandbox email: {e}")

send_sandbox_bounce_test()
```

```python With Special Headers theme={null}
import smtplib
from email.message import EmailMessage

def send_email_with_special_headers():
    # SMTP server configuration
    smtp_server = "send.ahasend.com"
    smtp_port = 587
    smtp_username = "YOUR_SMTP_USERNAME"
    smtp_password = "YOUR_SMTP_PASSWORD"

    # Email details
    from_addr = "updates@yourdomain.com"
    to_addrs = ["user1@example.com", "user2@example.com"]
    subject = "Product Update with Tracking"

    # Create the email message
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = ", ".join(to_addrs)

    # Add AhaSend special headers
    msg['ahasend-track-opens'] = 'true'
    msg['ahasend-track-clicks'] = 'true'
    msg['ahasend-tags'] = 'product-update,newsletter,python'
    msg['AhaSend-Metadata-Retention'] = '30'
    msg['AhaSend-Data-Retention'] = '7'

    # Set content
    msg.set_content("Check out our latest features at https://yourdomain.com/features")

    # Add HTML content
    html_content = """
    <html>
    <body>
        <h1>Product Updates</h1>
        <p>Check out our <a href="https://yourdomain.com/features">latest features</a>!</p>
        <p>This email has tracking enabled and custom tags.</p>
    </body>
    </html>
    """
    msg.add_alternative(html_content, subtype='html')

    # Connect and send
    try:
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()
            server.login(smtp_username, smtp_password)
            server.send_message(msg)

        print("Email sent with special headers!")
    except Exception as e:
        print(f"Failed to send email: {e}")

send_email_with_special_headers()
```

> [!NOTE]
>
> **Sandbox Benefits:** Emails sent in sandbox mode are free, trigger webhooks normally, and never actually deliver to recipients - perfect for development and testing.

## Environment Variables & Best Practices

### Using Environment Variables

```python smtplib with environment variables theme={null}
import smtplib
import os
from email.message import EmailMessage

def send_email_with_env_vars():
    # Load configuration from environment variables
    smtp_server = os.getenv('SMTP_HOST', 'send.ahasend.com')
    smtp_port = int(os.getenv('SMTP_PORT', '587'))
    smtp_username = os.getenv('SMTP_USERNAME')
    smtp_password = os.getenv('SMTP_PASSWORD')
    from_email = os.getenv('FROM_EMAIL')
    from_name = os.getenv('FROM_NAME', 'Your Company')

    # Validate required environment variables
    required_vars = [smtp_username, smtp_password, from_email]
    if not all(required_vars):
        print("Missing required environment variables:")
        print("Set: SMTP_USERNAME, SMTP_PASSWORD, FROM_EMAIL")
        return False

    # Create email message
    msg = EmailMessage()
    msg['Subject'] = "Environment Variables Test"
    msg['From'] = f'"{from_name}" <{from_email}>'
    msg['To'] = "user@example.com"
    msg.set_content("This email uses environment variables for configuration.")

    # Send email
    try:
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()
            server.login(smtp_username, smtp_password)
            server.send_message(msg)

        print("Email sent using environment variables!")
        return True
    except Exception as e:
        print(f"Failed to send email: {e}")
        return False

# Usage
if __name__ == "__main__":
    send_email_with_env_vars()
```

## Resources

> [card] [Python smtplib Documentation](https://docs.python.org/3/library/smtplib.html)
>
> Official Python smtplib module documentation

> [card] [Python email Documentation](https://docs.python.org/3/library/email.html)
>
> Official Python email module documentation

> [card] [Python.org](https://python.org/docs/)
>
> Official Python programming language documentation

> [card] [AhaSend Support](mailto:support@ahasend.com)
>
> Get help from our engineering team

For framework setup, use [Django](https://ahasend.com/docs/guides/django.md) or [Flask](https://ahasend.com/docs/guides/flask.md). Python API guides cover [AWS Lambda](https://ahasend.com/docs/guides/aws-lambda-python.md), [Azure Functions](https://ahasend.com/docs/guides/azure-functions-python.md) and [Cloud Run functions](https://ahasend.com/docs/guides/google-cloud-functions-python.md).

## Related Guides

* Before sending: [verify your domain](https://ahasend.com/docs/domains.md) and [create an SMTP credential](https://ahasend.com/docs/smtp/credentials.md).
* Connection and message rules: [SMTP hosts, ports and limits](https://ahasend.com/docs/smtp.md), [special headers](https://ahasend.com/docs/smtp/special-headers.md), [sandbox mode](https://ahasend.com/docs/send-api/sandbox.md), [retention](https://ahasend.com/docs/retention.md) and [plan features](https://ahasend.com/docs/facts.md#plans-and-features).
* Other ways to send: [REST API](https://ahasend.com/docs/send-api/send-email.md), [AhaSend CLI](https://ahasend.com/docs/cli/quickstart.md), [Node.js SDK](https://ahasend.com/docs/guides/nodejs-sdk.md) and [Go SDK](https://ahasend.com/docs/guides/go-sdk.md).
* Delivery events: [webhook setup](https://ahasend.com/docs/integrations/webhooks.md) and [local webhook testing](https://ahasend.com/docs/cli/webhook-testing.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.