diff --git a/apimail/admin.py b/apimail/admin.py
index 6ea48c9d3e78691675e90ac96297cc8bfc46672b..e7be3ceba704a0ff769a7c1a25c683dcd09b7687 100644
--- a/apimail/admin.py
+++ b/apimail/admin.py
@@ -11,7 +11,9 @@ from .models import (
     ComposedMessage, ComposedMessageAPIResponse,
     Event,
     StoredMessage,
-    UserTag)
+    UserTag,
+    ValidatedAddress, AddressValidation
+)
 
 
 admin.site.register(Domain)
@@ -66,3 +68,15 @@ class UserTagAdmin(admin.ModelAdmin):
     pass
 
 admin.site.register(UserTag, UserTagAdmin)
+
+
+class AddressValidationInline(admin.StackedInline):
+    model = AddressValidation
+    extra = 0
+    min_num = 0
+
+
+class ValidatedAddressAdmin(admin.ModelAdmin):
+    inlines = [AddressValidationInline,]
+
+admin.site.register(ValidatedAddress, ValidatedAddressAdmin)
diff --git a/apimail/migrations/0026_addressvalidation_validatedaddress.py b/apimail/migrations/0026_addressvalidation_validatedaddress.py
new file mode 100644
index 0000000000000000000000000000000000000000..355e9a2e0ae4d79c2a271e42f05f77641ceeb460
--- /dev/null
+++ b/apimail/migrations/0026_addressvalidation_validatedaddress.py
@@ -0,0 +1,38 @@
+# Generated by Django 2.2.16 on 2020-10-24 15:51
+
+import datetime
+import django.contrib.postgres.fields.jsonb
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('apimail', '0025_composedmessage_headers_added'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ValidatedAddress',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('address', models.EmailField(max_length=512, unique=True)),
+            ],
+            options={
+                'ordering': ['address'],
+            },
+        ),
+        migrations.CreateModel(
+            name='AddressValidation',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('data', django.contrib.postgres.fields.jsonb.JSONField(default=dict)),
+                ('datestamp', models.DateField(default=datetime.date.today)),
+                ('address', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='validations', to='apimail.ValidatedAddress')),
+            ],
+            options={
+                'ordering': ['address__address', '-datestamp'],
+            },
+        ),
+    ]
diff --git a/apimail/models/__init__.py b/apimail/models/__init__.py
index 4f8395d53ee2ec0bc63bbef7012e0b5bfe279768..d4cc703bc356bf44abae880faadbfa4151c72479 100644
--- a/apimail/models/__init__.py
+++ b/apimail/models/__init__.py
@@ -15,3 +15,5 @@ from .event import Event
 from .stored_message import StoredMessage
 
 from .tag import UserTag
+
+from .validated_address import ValidatedAddress, AddressValidation
diff --git a/apimail/models/stored_message.py b/apimail/models/stored_message.py
index 08b87f2601a92e52c8de7523354c86739a93f3ec..24690aee94c7ed7b6cc66fbbc1fb51f0698838d3 100644
--- a/apimail/models/stored_message.py
+++ b/apimail/models/stored_message.py
@@ -1,8 +1,7 @@
 __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
-
-#import pytz
+import pytz
 import uuid as uuid_lib
 
 from django.conf import settings
diff --git a/apimail/models/validated_address.py b/apimail/models/validated_address.py
new file mode 100644
index 0000000000000000000000000000000000000000..2d0341285615b7207a4c6a004446037795b5b3b6
--- /dev/null
+++ b/apimail/models/validated_address.py
@@ -0,0 +1,83 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+import datetime
+import requests
+
+from django.conf import settings
+from django.contrib.postgres.fields import JSONField
+from django.db import models
+
+
+class ValidatedAddress(models.Model):
+    """
+    Email address (lowercased) with related validation info.
+
+    The Mailgun email validation v4 API is queried at least once per year
+    and the response is saved as a related AddressValidation object.
+    """
+    address = models.EmailField(
+        max_length=512, # as per Mailgun limit
+        unique=True
+    )
+
+    class Meta:
+        ordering = ['address',]
+
+    def save(self, *args, **kwargs):
+        self.address = self.address.lower()
+        return super().save(*args, **kwargs)
+
+    def __str__(self):
+        return self.address
+
+    @property
+    def is_good_for_sending(self):
+        """
+        Return the status of the latest Mailgun validation.
+        """
+        self.update_mailgun_validation()
+        try:
+            return self.validations.first().data['result'] \
+                in ('deliverable', 'catch_all', 'unknown')
+        except AttributeError:
+            return False
+
+    def update_mailgun_validation(self):
+        """
+        If no validation check within last year, call the Mailgun validation v4 API.
+        """
+        one_year_ago = datetime.date.today() - datetime.timedelta(days=365)
+        if not self.validations.filter(datestamp__gt=one_year_ago).exists():
+            response = requests.get(
+                "https://api.mailgun.net/v4/address/validate",
+                auth=("api", settings.MAILGUN_API_KEY),
+                params={"address": self.address}
+            ).json()
+            validation = AddressValidation(
+                address=self,
+                data=response
+            )
+            validation.save()
+
+
+class AddressValidation(models.Model):
+    """
+    For a given ValidatedAddress, timestamped response from a Mailgun API validation v4 query.
+    """
+    address = models.ForeignKey(
+        'apimail.ValidatedAddress',
+        related_name='validations',
+        on_delete=models.CASCADE)
+    data = JSONField(default=dict)
+    datestamp = models.DateField(default=datetime.date.today)
+
+    class Meta:
+        ordering = [
+            'address__address',
+            '-datestamp'
+        ]
+
+    def __str__(self):
+        return "%s: %s (%s)" % (self.address, self.data['result'], self.datestamp)