Detect disposable, temporary, and burner email addresses in real time from Python, using the isitdisposable.com Application Programming Interface (API).
pip install isitdisposableor with uv:
uv add isitdisposableYou will need an API key from isitdisposable.com. A free tier is available, so you can try this out without a credit card.
from isitdisposable import Client
client = Client(api_key="isid_live_your_key_here")
result = client.check(email="someone@example.com")
if result.disposable:
print("This looks like a disposable email address.")
else:
print("This looks like a real, ongoing email address.")api_key can also come from the ISITDISPOSABLE_API_KEY environment variable, so in most setups you can just write Client().
# forms.py
from django import forms
from isitdisposable import Client
client = Client() # reads ISITDISPOSABLE_API_KEY from the environment
class SignupForm(forms.Form):
email = forms.EmailField()
def clean_email(self):
email = self.cleaned_data["email"]
result = client.check(email=email)
if result.disposable:
raise forms.ValidationError(
"Please sign up with an email address you check regularly. "
"Disposable or throwaway addresses are not allowed."
)
return email# dependencies.py
from fastapi import Depends, HTTPException
from isitdisposable import AsyncClient
async_client = AsyncClient() # reads ISITDISPOSABLE_API_KEY from the environment
async def reject_disposable_email(email: str) -> str:
result = await async_client.check(email=email)
if result.disposable:
raise HTTPException(
status_code=400,
detail="Please use an email address you check regularly, not a disposable one.",
)
return email
# In a route:
# @app.post("/signup")
# async def signup(email: str = Depends(reject_disposable_email)):
# ...Check up to 100 emails or domains in a single request. Items can be plain strings (anything containing an "@" is treated as an email, everything else as a domain) or dicts:
result = client.check_batch(
[
"someone@example.com",
"example.org",
{"domain": "another-example.com"},
]
)
for item in result.results:
print(item.domain, item.disposable, item.action)
print(f"Checked {result.count} items.")Signup forms and checkout flows should never break because of an email checking service having a bad moment. This client fails open by default: if the isitdisposable.com service cannot be reached, times out, is rate limiting you, or has an internal error, check() and check_batch() do not raise. Instead they return a result where checked is False, disposable is None, and action is "allow", and a warning is logged through the standard logging module under the logger name "isitdisposable". Your form keeps working; you can watch the warning logs to notice if this starts happening a lot.
If you would rather see failures as exceptions (for example in a background job where you want to retry later), turn this off:
client = Client(fail_open=False)With fail_open=False, a connection problem raises NetworkError, a rate limit response raises RateLimitError, and a server error raises ServerError. Invalid requests (a missing or malformed API key, or a request that is missing both an email and a domain) always raise, in either mode, because those indicate something in your own code needs fixing rather than a temporary service problem.
Every check returns a result with these fields. Any field can be None if the underlying signal was not evaluated for that request.
| Field | Type | Meaning |
|---|---|---|
checked |
bool |
Whether a real check was performed (False on a fail-open response). |
normalized_email |
str or None |
The email address you sent, normalized. |
domain |
str or None |
The domain that was evaluated. |
disposable |
bool or None |
The core verdict: whether the domain is a disposable or throwaway email provider. |
mx_valid |
bool or None |
Whether the domain has a working mail server. |
role_account |
bool or None |
Whether the local part looks like a role address (for example support@). |
relay |
bool or None |
Whether the domain is a mail relay or forwarding service. |
public_domain |
bool or None |
Whether the domain is a large public provider (for example a well known free email service). |
spam_risk |
bool or None |
Whether the domain appears on a spam or abuse reputation list. Only populated if your account has this signal enabled. |
mx_masked |
bool or None |
Whether the domain's mail server is masked or hidden behind a routing service. |
did_you_mean |
str or None |
A suggested correction if the domain looks like a likely typo. |
mx_records |
list[str] or None |
The mail server records found for the domain. |
action |
str |
The recommended action for your account's policy: "allow", "warn", or "block". |
reason |
str or None |
A machine readable reason code. This set is open ended; treat unknown values as informational. |
raw |
dict |
The full parsed response, including any fields not yet listed above, for forward compatibility. |
- Website: https://isitdisposable.com
- Documentation: https://isitdisposable.com/docs
- A free tier is available, so you can get started without a credit card.