Beta privadaMailbeam está en beta privada: la API pública todavía no está abierta.Únete a la lista de espera

Mailbeam
Django + DRFIniciación15 minutosActualizado en enero de 2025

Verificación de email en Django

Este tutorial muestra cómo añadir verificación de email en tiempo real a un proyecto Django. Construirás un ayudante reutilizable y lo conectarás tanto a un formulario clásico de Django como a un serializer de Django REST Framework (DRF), de forma que las direcciones inválidas, desechables y no entregables se rechacen antes de crear ningún usuario.

Qué vas a construir

  • Un ayudante verify_email que envuelve la API de Mailbeam y falla en abierto ante errores
  • Un validador validate_email_deliverable para formularios de Django
  • Un validador de campo de serializer de DRF para proyectos orientados a API

Requisitos previos

  • Python 3.9+ y Django 4.2 o posterior
  • Una clave de API de Mailbeam (regístrate gratis)
  • python-dotenv (o el cargador de settings que prefieras)

Paso 1 — Añade un cliente HTTP

No hay ningún SDK que instalar. Mailbeam es un único endpoint HTTP, así que la integración de abajo es todo el cliente que necesitas. Los SDK oficiales están en la hoja de ruta.

pip install httpx        # o usa requests, si ya está en el proyecto

Paso 2 — Configura los settings

Deja la clave fuera del control de versiones. Cárgala desde el entorno en settings.py:

# settings.py
import os

MAILBEAM_KEY = os.environ["MAILBEAM_KEY"]

# Puntuación mínima de calidad para aceptar (0-100). 60 es un valor razonable.
MAILBEAM_MIN_SCORE = int(os.environ.get("MAILBEAM_MIN_SCORE", "60"))
# .env
MAILBEAM_KEY=mb_live_xxxxxxxxxxxxxxxxxxxx

Paso 3 — Escribe un ayudante de verificación reutilizable

Pon la integración en un solo sitio para que formularios, serializers y vistas compartan el mismo comportamiento.

# accounts/verification.py
import logging

import httpx
from django.conf import settings

logger = logging.getLogger(__name__)

ENDPOINT = "https://api.mailbeam.dev/v1/verify"


def verify_email(email: str) -> tuple[bool, str | None]:
    """
    Devuelve (es_aceptable, codigo_de_motivo).
    Falla EN ABIERTO (devuelve True) ante errores de la API de Mailbeam, para
    que una caída no bloquee nunca registros legítimos.
    """
    try:
        response = httpx.post(
            ENDPOINT,
            json={"email": email.strip().lower()},
            headers={"Authorization": f"Bearer {settings.MAILBEAM_KEY}"},
            timeout=3.0,
        )
        response.raise_for_status()
        result = response.json()
    except httpx.HTTPError as exc:
        logger.error("Ha fallado la verificación de Mailbeam: %s", exc)
        return True, None  # falla en abierto

    if not result["valid"] or result["score"] < settings.MAILBEAM_MIN_SCORE:
        return False, result["reason"] or "invalid_email"
    return True, None

Paso 4 — Úsalo en un formulario de Django

# accounts/forms.py
from django import forms
from django.core.exceptions import ValidationError
from .verification import verify_email


class SignupForm(forms.Form):
    email = forms.EmailField()
    password = forms.CharField(widget=forms.PasswordInput)

    def clean_email(self):
        email = self.cleaned_data["email"]
        ok, reason = verify_email(email)
        if not ok:
            raise ValidationError(
                "Escribe una dirección de email válida y entregable.",
                code=reason,
            )
        return email

Django ejecuta clean_email automáticamente durante form.is_valid(), así que tu vista no cambia:

# accounts/views.py
from django.shortcuts import render, redirect
from .forms import SignupForm


def signup(request):
    if request.method == "POST":
        form = SignupForm(request.POST)
        if form.is_valid():
            create_user(email=form.cleaned_data["email"])
            return redirect("welcome")
    else:
        form = SignupForm()
    return render(request, "accounts/signup.html", {"form": form})

Paso 5 — Úsalo en un serializer de DRF

En proyectos orientados a API, valida dentro del serializer:

# accounts/serializers.py
from rest_framework import serializers
from .verification import verify_email


class SignupSerializer(serializers.Serializer):
    email = serializers.EmailField()
    password = serializers.CharField(write_only=True)

    def validate_email(self, value):
        ok, reason = verify_email(value)
        if not ok:
            raise serializers.ValidationError(
                f"Email rechazado: {reason}"
            )
        return value
# accounts/views.py (DRF)
from rest_framework.generics import CreateAPIView
from .serializers import SignupSerializer


class SignupView(CreateAPIView):
    serializer_class = SignupSerializer
    # DRF devuelve un 400 con los errores de campo automáticamente si falla la validación

Paso 6 — Añade caché

Evita reverificar la misma dirección (por ejemplo, cuando alguien reintenta el formulario). El framework de caché de Django encaja bien:

# accounts/verification.py
from django.core.cache import cache

def verify_email(email: str) -> tuple[bool, str | None]:
    normalized = email.strip().lower()
    cached = cache.get(f"mb:{normalized}")
    if cached is not None:
        return cached

    try:
        result = _client.verify_sync(normalized)
        ok = result.valid and result.score >= settings.MAILBEAM_MIN_SCORE
        outcome = (ok, None if ok else (result.reason or "invalid_email"))
        cache.set(f"mb:{normalized}", outcome, timeout=86_400)  # 24 h
        return outcome
    except mailbeam.APIError as exc:
        logger.error("Ha fallado la verificación de Mailbeam: %s", exc)
        return True, None  # falla en abierto (no caches los fallos)

Pruebas

Los dominios de prueba de Mailbeam devuelven resultados deterministas y no consumen cuota:

# accounts/tests.py
from django.test import TestCase
from .forms import SignupForm


class SignupFormTests(TestCase):
    def test_valid_email_passes(self):
        form = SignupForm(data={
            "email": "user@valid.mailbeam-test.dev",
            "password": "pass1234",
        })
        self.assertTrue(form.is_valid())

    def test_disposable_email_rejected(self):
        form = SignupForm(data={
            "email": "temp@disposable.mailbeam-test.dev",
            "password": "pass1234",
        })
        self.assertFalse(form.is_valid())
        self.assertIn("email", form.errors)

Buenas prácticas

PrácticaPor qué
Centralizar en verification.pyFormularios, serializers y vistas se comportan igual
Fallar en abierto ante mailbeam.APIErrorUna caída no debería romper los registros
Normalizar (strip().lower())Caché coherente y menos consultas duplicadas
Cachear ~24 hReduce las llamadas a la API en reintentos y reenvíos
Usar dominios de pruebaTests deterministas y sin consumo de cuota

Checklist de producción

  • MAILBEAM_KEY cargada desde el entorno, no subida al repositorio
  • Registro de errores configurado para mailbeam.APIError
  • Backend de caché definido (Redis o Memcached en producción)
  • MAILBEAM_MIN_SCORE ajustado a tu embudo
  • El mensaje de error que ve el usuario es amable en tus plantillas

Siguientes pasos