Dev Life
Verificación de emails con Python 3.9+ y la API de Mailgun
Tanto si envías emails transaccionales como newsletters o contenido promocional, mantener una lista de contactos limpia y verificada puede mejorar significativamente la entregabilidad, evitar que tus mensajes acaben en las carpetas de spam y proteger la reputación de tu dominio. Las direcciones de email no válidas o mal escritas pueden causar tasas de rebote más altas, lo que se traduce en penalizaciones por parte de los proveedores de servicios de envío de emails y una menor interacción. En esta guía aprenderás a verificar tus emails con la API de Mailgun.
¿Qué es la API de validación de emails para envíos masivos de Mailgun?
La API de validación de emails para envíos masivos de Mailgun está diseñada para que la verificación de emails sea escalable y eficiente. Con ella, el equipo de desarrollo puede validar listas de contactos enteras, comprobando si existen problemas habituales como errores de sintaxis, emails desechables o direcciones con servidores de email inactivos. La API proporciona resultados detallados para cada email, lo que facilita tomar las medidas oportunas.
En este tutorial, aprenderás a integrar la API de validación de emails para envíos masivos de Mailgun en tus aplicaciones Python. Desde la configuración de tu cuenta de Mailgun y el manejo seguro de credenciales hasta la escritura de código que valida emails y procesa los resultados, esta guía proporciona instrucciones paso a paso para verificar grandes listas de contactos de forma eficaz.
Implementación de la validación masiva de emails con Python y la API de Mailgun
Antes de empezar con este tutorial, asegúrate de tener lo siguiente:
- Conocimientos básicos de programación en Python
- Python 3.9+
- Un Cuenta de Mailgun; es necesario tener una cuenta de pago para este tutorial
- Experiencia en el desarrollo de aplicaciones basadas en Django REST framework
Este tutorial utiliza el sistema operativo Windows. Aunque todas las instrucciones también deberían funcionar en Linux y macOS, asegúrate de cambiar los delimitadores de ruta del sistema operativo o las referencias de sintaxis según sea necesario.
Configuración de Mailgun para la validación masiva de emails
Para empezar, inicia sesión en tu cuenta de Mailgun y dirígete al panel de control. Desplázate hacia abajo y selecciona la opción “API keys”:

Haz clic en “Add new key” para añadir una nueva clave de API:

Escribe una descripción (como “Bulk Email Validation API Key”) y haz clic en “Create Key”:

Una vez creada la clave de API, copia la información de la clave, ya que la necesitarás más adelante. Si te olvidas de copiar la información o pierdes la clave, no te preocupes; puedes eliminar tu clave de API y crear una nueva.
Configuración de un directorio de proyecto y dependencias
Crea un directorio de proyecto en tu equipo y accede a dicho directorio. A continuación, abre un terminal con la ruta apuntando al directorio actual del proyecto y ejecuta el siguiente comando para crear un entorno virtual de Python para este tutorial:
python -m venv venv
Activa el entorno virtual:
venvScriptsactivate
A continuación, instala la biblioteca requests, que se utilizará para interactuar con los puntos de conexión de Mailgun:
pip install requests
Creación de un proyecto y un script de configuración de Python
Una vez activado tu entorno virtual, crea un nuevo directorio dentro del proyecto llamado standalone_python_scripts, donde desarrollarás los módulos de Python necesarios para llevar a cabo la validación de emails masiva utilizando los puntos de conexión de Mailgun.
En este nuevo directorio, crea un nuevo archivo Python llamado config.py y pega el siguiente código para configurar la aplicación del script independiente de Python:
import osrnrn# Retrieve the API key from the environment variablernrnAPI_KEY = os.getenv("MAILGUN_API_KEY")rnrn# Mailgun API base URLrnrnMAILGUN_API_URL = "https://api.mailgun.net/v4/address/validate/bulk"rnrnLIST_NAME = "bulk_mailing_list_validation_1"rnrnFILE_PATH = "mailing_list.csv"rnrnCOMMAND = "submit_job" # Possible values are "submit_job" and "get_job_status"rn
Este módulo de configuración contiene las variables de configuración necesarias para llevar a cabo la solicitud de validación de emails masiva.
Configura una nueva variable de entorno, MAILGUN_API_KEY, en tu máquina host ejecutando el siguiente comando:
SET MAILGUN_API_KEY=
Esto asigna la clave de API que generaste anteriormente durante la configuración de la cuenta de Mailgun.
Si utilizas Linux o Mac, ejecuta el siguiente comando:
export MAILGUN_API_KEY=
Por ahora, deja los valores de las demás variables tal y como están. La variable LIST_NAME contiene el nombre identificador de la lista de correo que se va a validar. FILE_PATH contiene la ruta al archivo CSV de entrada que incluye la lista de emails a validar.
Preparación del archivo de tu lista de correo
Ahora que el script de configuración hace referencia a una ruta de archivo de lista de correo a través de la variable FILE_PATH, necesitas una lista de correo real con direcciones de email para validarla. Crea un archivo llamado mailing_list.csv en el directorio standalone_python_scripts directory y pega unas cuantas direcciones de email de muestra para empezar:
email
dummy_email@dummydomain.com
test_email@testemaildomain.com
non_existent_email_id_123456789@gmail.com
hellojohnsemail123@gmail.com
Las direcciones de email utilizadas aquí no son válidas. Puedes añadir otras válidas o no válidas con el mismo formato usando tu propia lista de direcciones de email.
Creación de un trabajo de validación de emails masiva
Con la lista de correo en su sitio y la configuración para el proceso de validación lista, es el momento de establecer una función para crear un trabajo de validación de emails masiva con Mailgun.
En el mismo directorio, crea un archivo de script de Python llamado bulk_email_validation.py y define una función create_bulk_validation_job que admita dos parámetros, list_name (nombre de la lista de correo) y file_path (ruta al archivo que contiene los emails):
def create_bulk_validation_job(list_name, file_path):rn """rn This function creates a bulk validation job using the Mailgun API.rnrn :param list_name: The name of the mailing list for validation.rn :param file_path: The path to the CSV file containing emails to validate.rn :return: Response object containing the job details.rn """rn url = f"{MAILGUN_API_URL}/{list_name}"rnrn try:rn # Send a POST request with the file to Mailgun's APIrn with open(file_path, "rb") as file_data:rn response = requests.post(rn url, auth=("api", API_KEY), files={"file": file_data}rn )rnrn # Check if the request was successfulrn if response.status_code == 202:rn logging.info(f"Bulk validation job created successfully for {list_name}")rn else:rn logging.error(rn f"Error creating validation job: {response.status_code} {response.text}"rn )rnrn return response.json()rnrn except Exception as e:rn logging.error(f"An error occurred while creating bulk validation job: {str(e)}")rn return Nonern
La función envía una solicitud POST a la API de Mailgun para iniciar el proceso de validación de emails masiva. La solicitud incluye el archivo de direcciones de email que hay que validar.
Desarrollo de funciones para comprobar el estado del trabajo y descargar los resultados
Ahora, escribe otra función llamada get_bulk_validation_status en el mismo script bulk_email_validation.py que compruebe el estado del trabajo de validación enviado. La respuesta incluye un enlace para descargar los resultados de la validación en formatos CSV y JSON. En este tutorial, utilizarás JSON para descargar y procesar posteriormente los resultados.
Para descargar los resultados de la validación, la función get_bulk_validation_status realiza una llamada a otra función llamada download_validation_results. El resultado descargado se coloca dentro del directorio validation_results en forma de archivo JSON. Crea este directorio dentro del directorio standalone_python_scripts, y pega el siguiente código definiendo estas dos funciones en bulk_email_validation.py:
def get_bulk_validation_status(list_name):rn """rn This function checks the status of a bulk validation job and downloads the validation results.rnrn :param list_name: The name of the mailing list for which the status is being checked.rn :return: Validation results or None in case of failure.rn """rn url = f"{MAILGUN_API_URL}/{list_name}"rnrn try:rn # Send a GET request to Mailgun's API to fetch the job statusrn response = requests.get(url, auth=("api", API_KEY))rnrn # Check if the request was successfulrn if response.status_code == 200:rn logging.info(f"Successfully retrieved validation status for {list_name}")rnrn # Parse the JSON responsern result = response.json()rnrn # Fetch the download URL for JSON resultsrn download_url = result.get("download_url", {}).get("json")rn if download_url:rn # Fetch and return the validation resultsrn return download_validation_results(download_url)rn else:rn logging.info("Download URL not available.")rn return Nonernrn else:rn logging.error(rn f"Error fetching job status: {response.status_code} {response.text}"rn )rn return Nonernrn except Exception as e:rn logging.error(rn f"An error occurred while fetching bulk validation status: {str(e)}"rn )rn return Nonernrndef download_validation_results(download_url):rn """rn This function downloads and processes the bulk validation results from the provided URL.rnrn :param download_url: The URL to download the validation results in JSON format.rn :return: Parsed results or None in case of failure.rn """rn try:rn response = requests.get(download_url)rnrn # Check if the request was successfulrn if response.status_code == 200:rn logging.info("Successfully downloaded validation results.")rnrn with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:rn validation_results_path = os.path.join(rn os.getcwd(), "validation_results"rn )rn zip_ref.extractall(path=validation_results_path)rnrn with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:rn for file in zip_ref.namelist():rn if file.endswith(".json"):rn with zip_ref.open(file) as json_file:rn validation_results = json.load(json_file)rnrn # Process the results (handle invalid emails, etc.)rn process_validation_results(validation_results)rnrn return validation_resultsrnrn else:rn logging.error(rn f"Error downloading results: {response.status_code} {response.text}"rn )rn return Nonernrn except Exception as e:rn logging.error(rn f"An error occurred while downloading validation results: {str(e)}"rn )rn return Nonern
La función envía una solicitud POST a la API de Mailgun para iniciar el proceso de validación de emails masiva. La solicitud incluye el archivo de direcciones de email que hay que validar.
Desarrollo de funciones para comprobar el estado del trabajo y descargar los resultados
Ahora, escribe otra función llamada get_bulk_validation_status en el mismo script bulk_email_validation.py que compruebe el estado del trabajo de validación enviado. La respuesta incluye un enlace para descargar los resultados de la validación en formatos CSV y JSON. En este tutorial, utilizarás JSON para descargar y procesar posteriormente los resultados.
Para descargar los resultados de la validación, la función get_bulk_validation_status realiza una llamada a otra función llamada download_validation_results. El resultado descargado se coloca dentro del directorio validation_results en forma de archivo JSON. Crea este directorio dentro del directorio standalone_python_scripts, y pega el siguiente código definiendo estas dos funciones en bulk_email_validation.py:
def get_bulk_validation_status(list_name):rn """rn This function checks the status of a bulk validation job and downloads the validation results.rnrn :param list_name: The name of the mailing list for which the status is being checked.rn :return: Validation results or None in case of failure.rn """rn url = f"{MAILGUN_API_URL}/{list_name}"rnrn try:rn # Send a GET request to Mailgun's API to fetch the job statusrn response = requests.get(url, auth=("api", API_KEY))rnrn # Check if the request was successfulrn if response.status_code == 200:rn logging.info(f"Successfully retrieved validation status for {list_name}")rnrn # Parse the JSON responsern result = response.json()rnrn # Fetch the download URL for JSON resultsrn download_url = result.get("download_url", {}).get("json")rn if download_url:rn # Fetch and return the validation resultsrn return download_validation_results(download_url)rn else:rn logging.info("Download URL not available.")rn return Nonernrn else:rn logging.error(rn f"Error fetching job status: {response.status_code} {response.text}"rn )rn return Nonernrn except Exception as e:rn logging.error(rn f"An error occurred while fetching bulk validation status: {str(e)}"rn )rn return Nonernrndef download_validation_results(download_url):rn """rn This function downloads and processes the bulk validation results from the provided URL.rnrn :param download_url: The URL to download the validation results in JSON format.rn :return: Parsed results or None in case of failure.rn """rn try:rn response = requests.get(download_url)rnrn # Check if the request was successfulrn if response.status_code == 200:rn logging.info("Successfully downloaded validation results.")rnrn with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:rn validation_results_path = os.path.join(rn os.getcwd(), "validation_results"rn )rn zip_ref.extractall(path=validation_results_path)rnrn with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref:rn for file in zip_ref.namelist():rn if file.endswith(".json"):rn with zip_ref.open(file) as json_file:rn validation_results = json.load(json_file)rnrn # Process the results (handle invalid emails, etc.)rn process_validation_results(validation_results)rnrn return validation_resultsrnrn else:rn logging.error(rn f"Error downloading results: {response.status_code} {response.text}"rn )rn return Nonernrn except Exception as e:rn logging.error(rn f"An error occurred while downloading validation results: {str(e)}"rn )rn return Nonern
Análisis y tratamiento de los resultados de la validación
Una vez que hayas descargado y obtenido acceso a los resultados de la validación, tienes que analizarlos y procesarlos. Este paso es crucial para identificar los emails entregables y los no entregables.
Pega el siguiente código para definir la función en bulk_email_validation.py que realiza esta tarea:
def process_validation_results(results):rn """rn This function processes the validation results and handles invalid and risky emails.rnrn :param results: The JSON object containing the validation results.rn """rn try:rn count_of_deliverable_addresses: int = 0rn emails_tobe_verified = set()rnrn # Extract the results summaryrn logging.info(f"Total email addresses validated: {len(results)}")rnrn for result in results:rn # Access data within each dictionaryrn deliverable = (rn result["result"] == "deliverable"rn ) # Check if result is deliverablern undeliverable = result["result"] != "deliverable"rn risk = result["risk"]rnrn # Count the number of deliverable addressesrn if deliverable:rn count_of_deliverable_addresses += 1rnrn # Count the number of undeliverable addresses and add them to the list for verificationrn if undeliverable:rn emails_tobe_verified.add(result["address"])rnrn # Count the number of risky addresses and add them to the list for verificationrn if risk != "low":rn emails_tobe_verified.add(result["address"])rnrn # Log the results summaryrn logging.info(f"Found {count_of_deliverable_addresses} deliverable emails")rnrn if len(emails_tobe_verified) > 0:rn logging.warning(rn "Found some emails that need to be verified because of its risky or undeliverable state."rn )rn logging.warning(rn "Total emails to be verified: {}".format(len(emails_tobe_verified))rn )rn logging.warning(f"Emails to be verified: {', '.join(emails_tobe_verified)}")rnrn except Exception as e:rn logging.error(rn f"An error occurred while processing validation results: {str(e)}"rn )rn
Ahora, define las instrucciones para llamar a estas funciones con el siguiente código:
if __name__ == "__main__":rnrn if COMMAND == "submit_job":rn create_bulk_validation_job(LIST_NAME, FILE_PATH)rn elif COMMAND == "get_job_status":rn get_bulk_validation_status(LIST_NAME)rn else:rn logging.error("Invalid command. Please use 'submit_job' or 'get_job_status'.")rn
El script final
En general, todo el script bulk_email_validation.py hace el trabajo duro de enviar una solicitud de validación de emails masiva, consultar el estado de la solicitud de trabajo de validación enviada y procesar los resultados. Una vez que hayas terminado con todos los cambios, tu script bulk_email_validation.py debería verse así esta.
A lo largo del script, el uso de bloques try…except garantiza que se controle cualquier error durante las llamadas a la API. Esto es especialmente importante para los entornos de producción donde pueden surgir problemas inesperados, como cuando tu aplicación no puede comunicarse con los puntos de conexión del servicio de Mailgun o cuando intentas enviar una solicitud de trabajo de validación con el mismo nombre de lista. Los mensajes de error y los comentarios en el código fuente ayudan a explicar los otros escenarios que se controlan.
Si vuelves a enviar la solicitud de trabajo de validación con el mismo nombre de lista (aprenderás a ejecutar el script en las siguientes secciones), el script está equipado para manejar también tales escenarios mostrando el correspondiente mensaje de error con una descripción:
2024-09-28 07:34:39,081 - ERROR - create_bulk_validation_job - Error creating validation job: 409 {"message":"List already exists."}
Si experimentas un error de red entre tu aplicación y el servicio de Mailgun, puedes implementar un mecanismo de reintento. Puedes crear el tuyo propio o usar una biblioteca como tenacity para definir cuántas veces hay que reintentar la solicitud si falla debido a problemas temporales de conectividad. También puedes implementar otros escenarios de tratamiento de errores según lo exija tu proyecto o tu negocio.
Llegados a este punto, ya has terminado de configurar el proyecto y es el momento de ejecutarlo.
Prueba mediante el envío de una solicitud de trabajo de validación de emails masiva
Para ejecutar el script y enviar la solicitud de trabajo de validación de emails masiva, abre un terminal y ve al directorio standalone_python_scripts. Ejecuta el siguiente comando:
python bulk_email_validation.py
Deberías ver una salida que indique que la solicitud de trabajo se ha enviado con éxito al servicio de Mailgun:
2024-09-28 19:47:34,012 - INFO - create_bulk_validation_job - Bulk validation job created successfully for bulk_mailing_list_validation_1
Prueba obteniendo el estado del trabajo de validación de emails masiva
A veces, en función del volumen de direcciones de email a validar, el trabajo enviado tardará más o menos en completarse. Para obtener el estado de la solicitud de trabajo y procesar los resultados si la solicitud está completa, edita el código config.py para actualizar la configuración de COMMAND con el valor get_job_status. Una vez hecho esto, ejecuta el siguiente comando para llevar a cabo esta tarea:
python bulk_email_validation.py
Deberías ver una salida que indique que el trabajo de validación se ha completado, que los resultados se han descargado y que luego se procesan:
2024-09-28 19:48:07,723 - INFO - get_bulk_validation_status - Successfully retrieved validation status for bulk_mailing_list_validation_1
2024-09-28 19:48:08,375 - INFO - download_validation_results - Successfully downloaded validation results.
2024-09-28 19:48:08,378 - INFO - process_validation_results - Total email addresses validated: 191
2024-09-28 19:48:08,378 - INFO - process_validation_results - Found 178 deliverable emails
2024-09-28 19:48:08,378 - WARNING - process_validation_results - Found some emails that need to be verified because of its risky or undeliverable state.
2024-09-28 19:48:08,378 - WARNING - process_validation_results - Total emails to be verified: 13
2024-09-28 19:48:08,378 - WARNING - process_validation_results - Emails to be verified:
dummy_email@dummydomain1.com, dummy_email@dummydomain2.com, hellojohnsemail123@gmail.com, dummy_email@dummydomain3.com, non_existent_email_id_123456789@gmail.com, dummy_email@dummydomain.com, dummy_email@dummydomain4.com, dummy_email@dummydomain5.com, dummy_email@dummydomain6.com, dummy_email@dummydomain7.com, dummy_email@dummydomain8.com, test_email@testemaildomain.com, electronix84@gmail.com
Esta salida indica que de las 191 direcciones de email enviadas para su validación, 178 están categorizadas como entregables y las 13 restantes no.
El script identifica las direcciones de email que hay que verificar analizando la respuesta de las API de Mailgun y mostrando la lista en la salida. Ahora te toca a ti decidir qué medidas tomar en función de las necesidades de tu proyecto o de tu negocio.
Integración de la validación masiva de emails en una aplicación Django
Ahora que has aprendido estas técnicas, puedes optar por aplicarlas en tu aplicación Django creando puntos de conexión para subir una lista de correo con direcciones de email de forma masiva y validarlas.
Puedes consultar o clonar este repositorio de GitHub si quieres experimentar y seguir paso a paso. El directorio bulk_email_validation contiene el código fuente del proyecto Django. Los siguientes son los archivos clave que intervienen en este proyecto:
- El El archivo requirements.txt contiene las dependencias necesarias para este proyecto Django.
- El El archivo config.py contiene la configuración relacionada con el servicio de Mailgun.
- El El archivo views.py contiene la lógica para procesar la solicitud y las respuestas relacionadas con los puntos de conexión de validación de emails masiva.
- El El archivo serializers.py contiene clases que ayudan a traducir datos entre objetos de Python y formatos como JSON, lo que facilita el envío o recepción de datos para las API de validación masiva de emails con solo unas pocas líneas de código.
- El El archivo urls.py contiene los dos puntos de conexión relacionados con la validación de emails masiva. Un punto de conexión sirve para enviar la solicitud de trabajo de validación de emails masiva; el otro sirve para obtener el estado y procesar los resultados.
Las siguientes capturas de pantalla están pensadas para ayudarte a comprender los puntos de conexión que trata esta aplicación Django y la salida que puede generar.
La primera captura de pantalla muestra la solicitud POST con las opciones para subir el archivo de la lista de correo y establecer un nombre para dicha lista, junto con la respuesta recibida tras el envío correcto de la solicitud de trabajo de validación de emails masiva:

La segunda captura de pantalla indica la correcta recuperación de la respuesta del trabajo de validación tras su finalización:

Puedes desarrollar una aplicación frontend con cualquier tecnología frontend que desees y llamar a estos puntos de conexión de la aplicación Django para cumplir con tu objetivo de validación de emails masiva.
En resumen
Este tutorial te ha enseñado a integrar la API de validación de emails para envíos masivos de Mailgun en tus aplicaciones Python con el fin de garantizar la exactitud y la entregabilidad de tus listas de contactos. Desde la configuración de tu cuenta de Mailgun, la obtención de la clave de API y su gestión segura hasta la implementación de la validación de emails masiva, el envío de solicitudes POST y GET y el procesamiento de los resultados, ahora sabes cómo automatizar el proceso de verificación de emails. También has explorado cómo gestionar los emails no válidos, controlar los errores habituales e integrar la validación de emails en aplicaciones web más grandes como Django. Todo el código fuente presentado en este tutorial está disponible en este repositorio de GitHub.
Al seguir estos pasos, puedes identificar de forma eficaz los emails entregables de tu gran lista de correo, lo que te permitirá mejorar la calidad de tus campañas de email, reducir las tasas de rebote y proteger tu reputación como remitente. El uso de la potente API de validación de emails para envíos masivos de Mailgun no solo agiliza este proceso, sino que también garantiza que la entrega de tus emails sea más fiable y eficiente. A medida que continúas optimizando tu estrategia de email, consulta Mailgun, explora sus diversas ofertas de productos y utilízalos para mejorar aún más tus campañas de email y maximizar su impacto.