IT & Engineering
Enviar emails con Python 3 y la API de Mailgun
Si quieres optimizar tu estrategia para enviar emails mediante programación, estás en buenas manos. Nuestro equipo de desarrollo tiene la experiencia que necesitas para empezar. Sigue leyendo para descubrir los detalles sobre cómo integrar Python 3 con la potente API de email de Mailgun, con consejos, orientación y optimizaciones a lo largo del proceso.
Primeros pasos: qué necesitas para este tutorial
A la hora de enviar un email, tienes dos opciones: manual o mediante programación. Cuando hablamos de emails transaccionales en concreto, hacerlo mediante programación es la mejor opción. En lugar de enviar actualizaciones importantes por email o notificaciones de forma manual, puedes utilizar scripts de email automatizados para ahorrar tiempo y reducir errores.
Los scripts (pequeños programas que automatizan tareas) ofrecen una forma más eficiente de realizar envíos masivos de email, programar mensajes o ejecutarse directamente desde tu aplicación. Algunos casos de uso para enviar emails mediante scripts automatizados incluyen los siguientes:
- Enviar emails de bienvenida para el registro de usuarios
- Enviar notificaciones y alertas desde aplicaciones, como informar a los usuarios sobre el uso de su servicio o avisar al personal de administración cuando los recursos del servidor son escasos
- Enviar informes, como un informe semanal sobre nuevos clientes
- Enviar campañas de marketing (por ejemplo, para promocionar nuevos productos o servicios)
En este tutorial, aprenderás a enviar emails utilizando scripts de Python 3 y la API de Mailgun.
Requisitos previos
Para seguir este tutorial sobre Python 3 y la API de email, necesitarás lo siguiente:
- Python 3.9 o superior. Python suele describirse como la navaja suiza de los lenguajes de programación y ofrece una amplia gama de capacidades. Además de sus múltiples usos, Python es excelente para escribir diversos scripts.
- Una cuenta de Mailgun. Mailgun es una plataforma de envío de emails que te permite enviar y hacer el seguimiento de los emails. Si aún no eres usuario de Mailgun, puedes seguir este tutorial utilizando nuestra prueba gratuita.
Configurar un nuevo proyecto de Python
Empecemos.
Empieza creando un directorio adecuado para tu proyecto de Python y añade un archivo .env vacío en su interior. Antes de continuar, considera crear y activar un nuevo entorno virtual antes de instalar las bibliotecas de Python necesarias para este tutorial. Esto aísla tu proyecto, lo que evita conflictos de dependencias.
Para instalar las bibliotecas necesarias (requests y python-dotenv) mediante pip en tu entorno virtual, ejecuta el siguiente comando:
pip install requests python-dotenv
La biblioteca requests te permite hacer llamadas a la API, mientras que la biblioteca python-dotenv te permite almacenar la clave de API fuera de tu código.
Cómo obtener una clave de API con Mailgun
A continuación, obtendrás una clave de API de tu cuenta de Mailgun, que utilizarás para realizar llamadas a la API de Mailgun.
Inicia sesión en tu cuenta de Mailgun y crea una clave de API en la página «Seguridad de la API», a la que se puede acceder mediante un menú desplegable debajo de tu nombre en la esquina superior derecha.

Copia y pega esta clave de API en el archivo .env de tu proyecto.
MAILGUN_API_KEY=»Tu clave de API aquí»
Mientras estás en Mailgun, anota tus dominios de envío desde el panel de control.
Si tienes un plan gratuito, verás un dominio sandbox, que se utilizará en el punto de conexión de la API, así como en la dirección de email del remitente al enviar emails a través de la API de Mailgun. El dominio sandbox sigue el formato .mailgun.org, como se muestra a continuación.

Tendrás que pasar a un plan superior de pago si necesitas utilizar un dominio de email personalizado para enviar emails a través de Mailgun. El plan gratuito solo permite el dominio sandbox y puedes enviar emails a un máximo de cinco direcciones verificadas. Sin embargo, este plan gratuito es útil durante la fase de desarrollo al integrar la API de Mailgun en tu sistema.
Importaciones e inicialización
A continuación, crea el archivo main.py en el directorio de tu proyecto y copia y pega este código:
```python import jsonrnimport requestsrnimport loggingrnrnimport osrnfrom dotenv import load_dotenvrnrnlogging.basicConfig(level=logging.INFO) # set log levelrnload_dotenv() # for reading API key from `.env` file.rnrn# Sandbox API URL format: https://api.mailgun.net/v3/sandbox<ID>.mailgun.org/messagesrnMAILGUN_API_URL = "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages"rnFROM_EMAIL_ADDRESS = "Sender Name <SENDER_EMAIL_ID>"
Utiliza el registro estándar de Python y lo configura al nivel INFO con este código:
```pythonrnlogging.basicConfig(level=logging.INFO)rn```
La función load_dotenv() procede de la biblioteca python-dotenv y se utiliza para cargar las variables de entorno desde el archivo .env en tu código de Python. Es una de las mejores prácticas almacenar y leer información sensible como credenciales y claves de API del archivo .env.
MAILGUN_API_URL establece la URL de la API para tu script de Python e indica el ID del email del remitente. Ambos deben utilizar tu dominio de envío de emails o el dominio sandbox mencionado anteriormente.
Enviar emails individuales mediante la API de Mailgun
Veamos primero la forma más sencilla de utilizar la API de Mailgun: enviar un solo email. Puedes usarla para casos puntuales, como enviar un recordatorio o un email de seguimiento a tu cliente de gran valor. Esto también te permitirá hacer un seguimiento de la entrega del email.
Copia y pega el código siguiente en tu archivo main.py, después de las importaciones y la inicialización:
```pythonrndef send_single_email(to_address: str, subject: str, message: str):rn try:rn api_key = os.getenv("MAILGUN_API_KEY") # get API-Key from the `.env` filernrn resp = requests.post(MAILGUN_API_URL, auth=("api", api_key),rn data={"from": FROM_EMAIL_ADDRESS,rn "to": to_address, "subject": subject, "text": message})rn if resp.status_code == 200: # successrn logging.info(f"Successfully sent an email to '{to_address}' via Mailgun API.")rn else: # errorrn logging.error(f"Could not send the email, reason: {resp.text}")rnrn except Exception as ex:rn logging.exception(f"Mailgun error: {ex}")rnrnif __name__ == "__main__":rn send_single_email("Manish <manish@exanple.com>", "Single email test", "Testing Mailgun API for a single email")rn```
Esta función send_single_email(…) toma tres argumentos: to_address, subject y message. to_address corresponde a una única dirección de email y las otras dos corresponden al asunto y al contenido del email.
El código lee la clave de API de Mailgun desde el archivo .env y la utiliza para realizar una llamada a la API, lo que establece una conexión segura con la API_URL especificada. Esta clave de API actúa como un identificador único, lo que permite a Mailgun autenticar la llamada a la API y verificar quién está utilizando sus servicios.
Debes utilizar tu clave de API única para garantizar la correcta autenticación. La dirección de email del remitente utilizada en esta llamada a la API también debe estar asociada a tu dominio válido o al dominio sandbox de Mailgun. En caso contrario, la llamada fallará con un mensaje de error.
Los parámetros de datos se envían a través del método HTTP POST al punto de conexión de la API con la llamada requests.post(…).
Cuando la llamada a la API tiene éxito, tu email se añadirá a la cola de entrega y la API devolverá un estado HTTP 200 (OK). En caso de error, la API devolverá un mensaje de error con un código de estado HTTP adecuado. Este fragmento de código registra ambos casos adecuadamente.
if __name__ == «__main__» muestra cómo se puede llamar a esta función en tu script.
Al ejecutar este script en tu terminal se mostrará lo siguiente:

Envío masivo de email con la API de Mailgun
Aunque puedes utilizar la función send_single_email(…) en un bucle para enviar emails a múltiples destinatarios, no es el método más eficaz debido a los retrasos de E/S de la red. Este enfoque también puede verse afectado por la limitación de velocidad de la API.
En su lugar, utiliza Envío por lotes para enviar emails a múltiples destinatarios. El siguiente fragmento de código muestra cómo utilizarlo en tu script de Python.
Copia y pega la función send_batch_emails(…) en tu archivo main.py, después de la función send_single_email(…) y modifica la parte __main__ como se muestra a continuación:
```pythonrndef send_batch_emails(recipients: dict, subject: str, message: str):rn try:rn api_key = os.getenv("MAILGUN_API_KEY") # get API-Key from the `.env` filernrn to_address = list(recipients.keys()) # get only email addressesrn recipients_json = json.dumps(recipients) # for API callrnrn logging.info(f"Sending email to {len(to_address)} IDs...")rn resp = requests.post(MAILGUN_API_URL, auth=("api", api_key),rn data={"from": FROM_EMAIL_ADDRESS,rn "to": to_address, "subject": subject, "text": message,rn "recipient-variables": recipients_json})rn if resp.status_code == 200: # successrn logging.info(f"Successfully sent email to {len(recipients)} recipients via Mailgun API.")rn else: # errorrn logging.error(f"Could not send emails, reason: {resp.text}")rn except Exception as ex:rn logging.exception(f"Mailgun error: {ex}")rnrnif __name__ == "__main__":rn # send_single_email("Manish <manish@exanple.com>", "Single email test", "Testing Mailgun API for a single email")rn _recipients = {"manish@example.com": {"name": "Manish", "id": 1},rn "jakkie@example.com": {"name": "Jakkie", "id": 2},rn "elzet@example.com": {"name": "Elzet", "id": 3}}rnrn send_batch_emails(_recipients, "Hi, %recipient.name%!", "Testing Mailgun API. This email is sent via Mailgun API.")rn```
¿Cómo puedo enviar varios emails personalizados?
El envío por lotes utiliza un parámetro especial llamado Recipient Variables que te permite enviar emails personalizados a múltiples destinatarios en una sola llamada a la API. Al utilizar Recipient Variables con el envío por lotes, te aseguras de que Mailgun envía emails individuales a cada destinatario en el campo «Para». Sin él, cada destinatario verá todas las direcciones de email del resto en el campo «Para».
Aquí tienes una de estas variables de destinatario, con las direcciones de email como claves y los correspondientes «name» e «id» como valores:
{«manish@example.com»: {«name»: «Manish», «id»: 1},
«jakkie@example.com»: {«name»: «Jakkie», «id»: 2},
«elzet@example.com»: {«name»: «Elzet», «id»: 3}}
La función send_batch_emails(…) tiene tres parámetros: recipients, subject y message. Ten en cuenta que recipients es un objeto de diccionario que representa la variable de destinatario explicada anteriormente.
Primero, el código extrae las direcciones de email de este diccionario utilizando to_address = list(recipients.keys()) y, a continuación, convierte el diccionario a JSON para el uso de la API con la llamada recipients_json = json.dumps(recipients).
La variable recipients_json se pasa en la llamada a la API para el campo recipient-variables, que se utiliza para personalizar el asunto y el contenido del email. El asunto se especifica de la siguiente manera:
«¡Hola, %recipient.name%!»
La API de Mailgun sustituye correctamente el nombre de cada destinatario desde el JSON al enviar los emails con %recipient.name%. Así, en el ejemplo anterior, Manish, Jakkie y Elzet recibirán líneas de asunto personalizadas: «¡Hola, Manish!» , «¡Hola, Jakkie!» y «¡Hola, Elzet!». De manera similar, puedes personalizar el contenido del email utilizando cualquier valor %recipient.KEY-NAME%.
El resto del código send_batch_emails(…) es similar al de la función send_single_email(…), salvo que se envía a múltiples destinatarios en una sola llamada a la API.
Al ejecutar este script se mostrará lo siguiente:

Este es un ejemplo de cómo aparecerá este email en la bandeja de entrada del destinatario:

Funciones adicionales de la API de email de Mailgun
La API de Mailgun ofrece varias funciones adicionales para facilitarte la vida.
Enviar emails en HTML con archivos adjuntos
La API de Mailgun te permite enviar emails con archivos adjuntos, ya sea con contenido de texto o HTML. Puedes utilizar el mismo punto de conexión de la API (https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages) con un «attachment» adicional que se pasa al parámetro files, como se muestra a continuación:
```pythonrnfiles = {'attachment': open('weekly-report.csv', 'rb')} # file you want to attachrnresp = requests.post(MAILGUN_API_URL, auth=("api", api_key), files=files,rn data={"from": FROM_EMAIL_ADDRESS,rn "to": to_address, "subject": subject, "text": message})rn```
Entrega y seguimiento de emails
Mailgun proporciona un seguimiento detallado de los emails, lo que incluye saber cuándo se entregan o se abren los emails, si se hace clic en los enlaces, si hay un rebote en los emails, si se solicita la baja o si los emails se marcan como spam. Estos datos están disponibles mediante el panel de control y a través de la API.
Mailgun también guarda los emails de forma permanente si no se pueden entregar (rebote definitivo) o si un destinatario se da de baja o marca el email como spam. En estos casos, Mailgun no volverá a intentar enviar emails a esos destinatarios.
Plantillas de email
La API de Mailgun te permite crear plantillas en HTML para estandarizar el diseño de tus emails y hacerlos más atractivos con diseños predefinidos y contenido estándar.
Puedes encontrar las plantillas en la barra lateral izquierda, en el menú de envíos.
Pruebas de email
Mailgun también proporciona herramientas de pruebas de email para ayudarte a garantizar que tus mensajes se vean y funcionen como esperas. Con Inspeccionar, puedes comprobar automáticamente si hay problemas como enlaces rotos, imágenes que faltan o disparadores de spam. El Previsualización de emails te permite ver cómo se procesará tu email en diferentes clientes y dispositivos, mientras que Accesibilidad del email las pruebas ayudan a confirmar que tu contenido cumple las normas de accesibilidad. Puedes obtener más información sobre el software de pruebas de email de Mailgun e integrar estas comprobaciones en tu flujo de trabajo al crear emails con la API.
Bibliotecas de email alternativas de Python
Antes de recurrir a una API de email dedicada, merece la pena conocer las herramientas nativas que proporciona Python para enviar emails, junto con algunas opciones ligeras de terceros.
Python incluye dos módulos principales para el email basado en SMTP: smtplib y email.
smtplib
El módulo smtplib gestiona las sesiones SMTP y te permite conectarte a un servidor SMTP mediante SMTP_SSL() para TLS implícito (normalmente en el puerto 465) o SMTP() con starttls() para TLS oportunista (normalmente en el puerto 587). La autenticación se maneja con un nombre de usuario y una contraseña estándar.
El paquete email se encarga de la creación de los mensajes. EmailMessage ofrece una API moderna y de alto nivel, mientras que MIMEText y MIMEMultipart ofrecen asistencia para texto sin formato, HTML y cargas útiles multiparte.
Este código crea un email sencillo y lo envía de forma segura a través de un servidor SMTP usando Python:
from email.message import EmailMessage
import smtplib
msg = EmailMessage()
msg["From"] = "you@example.com"
msg["To"] = "friend@example.com"
msg["Subject"] = "Hello"
msg.set_content("Sent with Python!")
with smtplib.SMTP("smtp.example.com", 587) as smtp:
smtp.starttls()
smtp.login("username", "password")
smtp.send_message(msg)
Yagmail
Varias bibliotecas de terceros reducen aún más el código repetitivo. Yagmail proporciona una abstracción centrada en Gmail para enviar mensajes y adjuntos con una configuración mínima. python-emails añade funciones de nivel superior, como el procesamiento de plantillas, la composición estructurada de mensajes y la firma DKIM.
Por norma general, evita incrustar credenciales en el código. Utiliza el módulo getpass() de Python para introducir contraseñas de forma segura, almacena las claves de API en variables de entorno y siempre transmite los emails a través de conexiones cifradas con TLS.
Autenticación y seguridad
Es importante tener en cuenta algunas mejores prácticas y consejos al enviar emails mediante programación.
Gestión de errores
Cuando tu script utiliza una API de terceros como Mailgun para enviar emails, debes gestionar los posibles errores relacionados con fallos en la red o en la API y las respuestas de la API que indican errores para garantizar que tu script funciona sin problemas, puede detectar problemas como claves de API o URL no válidas y sabe cuándo la API de Mailgun no envía los emails. Sin una gestión de errores, tu script podría fallar de forma inadvertida, lo que daría lugar a emails no entregados.
Los fragmentos de código anteriores incluyen todo lo necesario para gestionar los errores que puedan producirse. En primer lugar, comprueban el código de estado de la respuesta (resp.status_code). Si no se realiza correctamente (HTTP 200), registran el mensaje de error para que puedas depurar el problema.
Tanto la función send_single_email(…) como send_batch_emails(…) utilizan también un bloque try-except para garantizar que cualquier excepción se capture y se registre correctamente.
Entregabilidad de emails
La entregabilidad del email significa garantizar que tus emails llegan a la bandeja de entrada del destinatario y no a la carpeta de spam. Necesitas una buena reputación como remitente para conseguir una entregabilidad alta. Cuando hablamos de desarrollar el envío mediante programación, hay un par de cosas específicas que podemos analizar para mejorar la entregabilidad de los emails, incluida la autenticación y la optimización de los emails para que sean responsive.
Autentica tu email
Utiliza SPF, DKIM y DMARC para validar tu identidad como remitente. Envía emails solo a suscriptores verificados que hayan aceptado recibirlos y elimina regularmente a los suscriptores inactivos y a quienes marcan tus emails como spam.
DMARC se está convirtiendo en un requisito del sector. Obtén más información sobre por qué y qué hace este estándar de autenticación en nuestra publicación sobre la perspectiva de DMARC.
Empieza a enviar con Python 3 y la API de email de Mailgun
En este artículo, has aprendido a enviar emails mediante un script de Python y la API de Mailgun. También has aprendido sobre funciones de Mailgun como el seguimiento de emails y las plantillas, y sobre la importancia de la gestión de errores, optimizar la entregabilidad y enviar emails responsive.
Las personas apasionadas por los emails ayudan a otras apasionadas por los emails. Puedes encontrar el código que hemos analizado en este tutorial en este repositorio de GitHub.
¿Te ha resultado útil? Si es así, asegúrate de suscribirte a nuestra newsletter para recibir más tutoriales, anuncios e información del sector.
Preguntas frecuentes
Utiliza TLS (STARTTLS) en el puerto 587 cuando quieras pasar una conexión simple a cifrada y SSL (TLS implícito) en el puerto 465 cuando la conexión se inicie cifrada.
- TLS (STARTTLS): smtplib.SMTP(host, 587) → starttls() → login() → send_message()
- SSL (TLS implícito): smtplib.SMTP_SSL(host, 465) → login() → send_message()
En ambos casos, redacta el mensaje (por ejemplo, con EmailMessage) y envíalo con smtp.send_message(msg). Autentica siempre la conexión utilizando las credenciales SMTP (nombre de usuario/contraseña) proporcionadas por tu proveedor.
- EmailMessage: API moderna y de alto nivel. La mejor opción predeterminada para la mayoría de los casos de uso. Facilita configurar encabezados, añadir texto sin formato, añadir alternativas HTML (add_alternative()) y adjuntar archivos con menos código repetitivo.
- MIMEText: bloque de creación de nivel inferior para una sola parte de texto (texto sin formato o HTML). Es útil cuando deseas un control explícito sobre una única parte del cuerpo del mensaje.
- MIMEMultipart: contenedor para combinar varias partes MIME (texto + HTML + adjuntos). Es común en ejemplos más antiguos; los archivos y las partes de MIMEText se adjuntan manualmente.
Si estás empezando desde cero, elige EmailMessage a menos que necesites compatibilidad con patrones antiguos de creación MIME.
Utiliza variables de entorno para las credenciales en aplicaciones desplegadas (CI/CD, contenedores, servidores) y getpass() para scripts locales interactivos.
Las variables de entorno mantienen los secretos fuera del control de fuentes y los registros. Almacena valores como nombres de usuario/contraseñas SMTP o claves de API como SMTP_USER, SMTP_PASS, MAILGUN_API_KEY, etc. y léelos en tiempo de ejecución.
getpass.getpass() solicita una contraseña de forma segura sin mostrarla en el terminal, algo útil para scripts rápidos o pruebas locales.
Un patrón común es: leer primero desde env y recurrir a getpass() solo si falta.
Yagmail está diseñado específicamente para flujos de trabajo de Gmail, por lo que reduce el código repetitivo de SMTP y facilita las tareas comunes en Gmail.
Principales ventajas para quienes usan Gmail:
- Menos código: configuración mínima para enviar mensajes, HTML y archivos adjuntos.
- Ergonomía orientada a Gmail: una API más amigable para redactar mensajes y gestionar archivos adjuntos.
- Iteración más rápida: es ideal para scripts y herramientas internas en las que deseas que el «envío de emails» sea sencillo y sin tener que gestionar manualmente las partes MIME.
Si solo envías estrictamente a través de Gmail y quieres menos piezas móviles que la combinación pura y dura de smtplib y creación de MIME, Yagmail suele ser la vía más rápida.
Con python-emails, normalmente tienes que hacer lo siguiente:
- Procesar una plantilla (a menudo HTML y texto) usando los asistentes para plantillas de la biblioteca (o pasar el contenido preprocesado desde tu propio motor de plantillas).
- Adjuntar la configuración DKIM mediante el selector y la clave privada DKIM de tu dominio antes de enviar.
En la práctica, harás lo siguiente:
- Crear un objeto de email (asunto/de/para)
- Proporcionar los cuerpos de texto/HTML procesados (y, de forma opcional, las variables de las plantillas)
- Configurar la firma DKIM con:
- un selector (por ejemplo, mailgun o default)
- tu dominio de firma
- la clave privada DKIM (cargada de forma segura desde variables de entorno o un gestor de secretos)
Para la producción, mantén la clave privada DKIM fuera del código fuente (variables de entorno o archivos secretos montados) y valida la firma comprobando los encabezados del mensaje en un buzón que muestre los resultados de DKIM-Signature y autenticación.