{"id":2925,"library":"django-anymail","title":"django-anymail","description":"Django Anymail provides a unified interface for sending transactional emails and receiving webhooks across many Email Service Providers (ESPs) like SendGrid, Mailgun, Postmark, and more. It integrates seamlessly with Django's `EmailBackend` and `send_mail` functions, adding advanced features like ESP-specific options, transactional tracking, and inbound email signals. The library is actively maintained with frequent releases, currently at version 14.0, and supports various Django and Python versions.","status":"active","version":"14.0","language":"en","source_language":"en","source_url":"https://github.com/anymail/django-anymail","tags":["django","email","email-backend","webhook","smtp","transactional-email","mailgun","sendgrid","postmark"],"install":[{"cmd":"pip install django-anymail","lang":"bash","label":"Install base package"},{"cmd":"pip install django-anymail[mailgun]","lang":"bash","label":"Install with ESP-specific dependencies (e.g., Mailgun)"}],"dependencies":[{"reason":"Core framework integration; requires Django >= 3.2","package":"Django","optional":false},{"reason":"Used internally for HTTP requests to most ESP APIs","package":"requests","optional":false}],"imports":[{"note":"For setting up webhook receivers in your urls.py to process inbound email or status updates.","symbol":"AnymailWebhooksView","correct":"from anymail.webhooks.views import AnymailWebhooksView"},{"note":"A subclass of Django's EmailMessage, used for adding ESP-specific features like tags, merge data, or metadata.","symbol":"AnymailMessage","correct":"from anymail.message import AnymailMessage"}],"quickstart":{"code":"import os\nfrom django.conf import settings\nfrom django.core.mail import send_mail\nfrom anymail.message import AnymailMessage\n\n# --- Minimal Django settings configuration for standalone execution ---\n# In a real Django project, these settings would be in your settings.py file.\n# This block ensures the script can run without a full Django project setup.\nif not settings.configured:\n    settings.configure(\n        ANYMAIL={\n            \"MAILGUN_API_KEY\": os.environ.get(\"ANYMAIL_MAILGUN_API_KEY\", \"your-mailgun-api-key\"),\n            \"MAILGUN_SENDER_DOMAIN\": os.environ.get(\"ANYMAIL_MAILGUN_SENDER_DOMAIN\", \"mg.example.com\"),\n            # For other ESPs, replace MAILGUN_API_KEY/SENDER_DOMAIN with appropriate keys/settings\n        },\n        EMAIL_BACKEND=\"anymail.backends.mailgun.EmailBackend\", # Or your chosen ESP backend\n        DEFAULT_FROM_EMAIL=\"sender@example.com\",\n        DEBUG=True, # Minimal required setting for settings.configure\n    )\n\nprint(\"Attempting to send an email using Django's send_mail...\")\n\ntry:\n    # Example 1: Basic email using Django's send_mail\n    send_mail(\n        subject=\"Hello from django-anymail!\",\n        message=\"This is a test email sent via Mailgun through django-anymail.\",\n        from_email=settings.DEFAULT_FROM_EMAIL,\n        recipient_list=[\"recipient@example.com\"], # Replace with a valid recipient email\n        fail_silently=False,\n    )\n    print(\"Basic email send attempt successful.\")\n\n    # Example 2: Using AnymailMessage for ESP-specific features (e.g., tags)\n    message = AnymailMessage(\n        subject=\"AnymailMessage with Tags\",\n        body=\"This email demonstrates using AnymailMessage with custom tags.\",\n        from_email=settings.DEFAULT_FROM_EMAIL,\n        to=[\"recipient2@example.com\"], # Replace with another valid recipient email\n    )\n    message.tags = [\"transactional\", \"test-tag\"]\n    # You can also add merge_data, metadata, etc.\n    message.send(fail_silently=False)\n    print(\"AnymailMessage with tags send attempt successful.\")\n\nexcept Exception as e:\n    print(f\"\\nFailed to send email: {e}\")\n    print(\"Please ensure your environment variables (e.g., ANYMAIL_MAILGUN_API_KEY, ANYMAIL_MAILGUN_SENDER_DOMAIN) \")\n    print(\"are correctly set and your ESP configuration in settings.py is valid.\")\n    print(\"Note: In a real Django project, you'd only need the settings in settings.py \")\n    print(\"and directly call send_mail or AnymailMessage.send().\")","lang":"python","description":"This quickstart demonstrates how to configure and use django-anymail to send emails via an ESP (Mailgun is used as an example). It shows both the standard `send_mail` function and the extended `AnymailMessage` for ESP-specific features like tags. Remember to replace placeholder API keys and recipient emails. For a real Django project, the `settings.configure` block is not needed, as settings would be in your project's `settings.py`."},"warnings":[{"fix":"Review your email content and templates for non-ASCII characters. Consult the Anymail documentation's 'International email' section (https://anymail.dev/en/stable/tips/international_email/) for guidance on handling specific character sets and ESP limitations.","message":"Version 14.0 introduced improved Unicode handling that might surface new `AnymailUnicodeError` exceptions for previously silently-handled problematic non-ASCII characters. This helps identify and fix issues at the source.","severity":"breaking","affected_versions":">=14.0"},{"fix":"Update your `esp_extra` usage for Amazon SES to pass a dictionary (instead of a string). For SparkPost, ensure `use_org_defaults=True` is included in your template parameters when `template_id` is set.","message":"Version 11.0 introduced breaking changes for Amazon SES and SparkPost. For Amazon SES, `anymail.message.AnymailMessage.esp_extra` now strictly requires a dictionary. For SparkPost, using `template_id` requires explicitly setting `use_org_defaults=True`.","severity":"breaking","affected_versions":">=11.0"},{"fix":"When configuring webhook URLs in `urls.py`, wrap `AnymailWebhooksView.as_view()` with `django.views.decorators.csrf.csrf_exempt`. Example: `path('anymail/webhooks/', csrf_exempt(AnymailWebhooksView.as_view()), name='anymail-webhooks')`.","message":"Anymail webhook views, designed to receive POST requests from external ESPs, are incompatible with Django's default CSRF protection. If not properly exempted, these webhook requests will be blocked.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure `ANYMAIL = { 'YOUR_ESP_API_KEY': '...' }` is correctly defined in `settings.py`, and the key name matches the exact environment variable/setting name required by your chosen ESP as per Anymail documentation.","message":"The most common issue is incorrectly configuring the `ANYMAIL` dictionary in `settings.py` or omitting the specific ESP API key (e.g., `MAILGUN_API_KEY`, `SENDGRID_API_KEY`, `POSTMARK_API_TOKEN`). This will prevent Anymail from authenticating with your chosen ESP.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}