Flask email verification

Verify emails in Flask with Verifly

Add a live email verification step to any Flask route in a few lines. Verifly is an API-first, pay-as-you-go service — call it server-side from your view with the requests library, fail open on errors, and reject disposable or dead mailboxes before you create the account.

Verify an email in a Flask routeAPI
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)
VERIFLY_KEY = "vf_YOUR_KEY"  # keep in an env var, never in client code

@app.post("/signup")
def signup():
    email = request.form["email"]
    r = requests.get(
        "https://verifly.email/api/v1/verify",
        params={"email": email},
        headers={"Authorization": f"Bearer {VERIFLY_KEY}"},
        timeout=8,
    )
    data = r.json()
    if data["result"] == "undeliverable" or data.get("disposable"):
        return jsonify(error="Please use a valid, permanent email"), 400
    # create_user(email)
    return jsonify(ok=True), 201

Real-time SMTP mailbox checks

Single, batch, and async bulk verification

Disposable, role account, and catch-all detection

Pay-as-you-go credits with no subscription lock-in

Search fit

Built for Flask email verification

Use Verifly when you need a simple API, predictable pricing, and clean JSON results before emails hit your product, CRM, or campaign tool.

Block disposable and dead emails inside a /signup route
Validate a contact-form submission before storing it
Clean an imported list from a Flask admin action
Guard a password-reset flow so codes only go to real mailboxes

Why verify emails server-side in Flask

Flask gives you a thin, explicit request cycle, which is exactly where an email check belongs. The signup route is the highest-value place to catch a bad address: it is the last moment before you create a user, send a confirmation, and start building sender reputation on that mailbox. WTForms or a regex validator only confirm that user@dead-domain.com looks like an email — they cannot tell you the mailbox bounces. Verifly performs a live SMTP probe over one HTTPS call and tells you the truth.

The check must run server-side. Your Verifly key is a Bearer secret, so it belongs in an environment variable read inside the view, never in a template or client-side JavaScript. A single requests.get against /api/v1/verify inside the route returns a JSON verdict with a result field of deliverable, undeliverable, or risky plus boolean flags for disposable, role, catch_all, and smtp. Branch on those to reject throwaway domains and dead mailboxes right there in the handler.

Pricing fits a Flask app cleanly: no seat to justify, just credits from $2 per 1,000 checks down to $0.60 per 1,000 at volume. Self-register for 100 free credits with no card at the autonomous register endpoint and drop the key into your config.

Fail open so a network blip never blocks signup

The one rule that keeps a verification step from becoming an outage: fail open. If Verifly is briefly unreachable or slow, you never want a legitimate user turned away at registration. Wrap the call in a try/except with a short timeout and, on any RequestException, let the signup proceed. You trade a rare unverified address for never blocking a real customer — the right bargain at the front door.

A reusable fail-open verification helper
import requests

VERIFLY_KEY = "vf_YOUR_KEY"

def email_is_acceptable(email: str) -> bool:
    """Return False only when we KNOW the address is bad."""
    try:
        r = requests.get(
            "https://verifly.email/api/v1/verify",
            params={"email": email},
            headers={"Authorization": f"Bearer {VERIFLY_KEY}"},
            timeout=8,
        )
        data = r.json()
    except requests.RequestException:
        return True  # fail open: never block signup on a network error

    if data.get("disposable") or data.get("role"):
        return False
    return data["result"] != "undeliverable"

Batch-clean a list from a Flask admin action

Beyond the signup route, Flask apps often need to clean an imported list — leads pasted into an admin form, a CSV upload, or rows pulled from your own database. Do not loop the single endpoint; send them together to POST /api/v1/verify/batch. It accepts a JSON body with an emails array and returns a results array in the same order, so you can zip it back against your source rows and keep only the deliverable ones.

Batch verification inside a view
import requests
from flask import request, jsonify

@app.post("/admin/clean-list")
def clean_list():
    emails = request.json["emails"]  # e.g. ["a@x.com", "b@y.com", ...]
    r = requests.post(
        "https://verifly.email/api/v1/verify/batch",
        json={"emails": emails},
        headers={
            "Authorization": f"Bearer {VERIFLY_KEY}",
            "Content-Type": "application/json",
        },
        timeout=30,
    )
    results = r.json()["results"]
    deliverable = [x["email"] for x in results if x["result"] == "deliverable"]
    return jsonify(kept=deliverable, total=len(results))

FAQ

Frequently asked questions

Where should I put my Verifly key in a Flask app?

In an environment variable read server-side, for example os.environ['VERIFLY_KEY'], loaded into app.config. It is a Bearer secret and must never appear in a Jinja template, static asset, or client-side JavaScript — only your Flask view should hold it.

Should the verification call block the signup request?

For a single signup it is one fast request, so a synchronous call inside the route is fine. Wrap it in a try/except with an 8-second timeout and fail open, so a transient network error lets the registration through instead of turning a real user away.

What does fail open mean here?

It means when Verifly is unreachable or times out, you allow the signup rather than block it. You only reject an address when the API positively reports it undeliverable or disposable. The rare unverified address is a better outcome than blocking a legitimate customer.

How do I clean a whole list, not just one signup?

Send the addresses to POST /api/v1/verify/batch as a JSON emails array and read the results array back. That is far faster and cheaper than calling the single endpoint per row inside a Python loop.

What fields does the response return?

Each result includes result (deliverable, undeliverable, or risky), is_valid, and boolean flags disposable, role, catch_all, and smtp. Branch on result and the disposable/role flags to decide what to reject at the door.

What does Flask email verification cost?

Pricing is pay-as-you-go from $2 per 1,000 checks down to $0.60 per 1,000 at volume, with 100 free credits when you self-register. There is no subscription, so a low-traffic signup route costs only the credits it spends.