All posts

Email Verification API: Integration Guide (with Python, JavaScript & PHP Examples)

Wire real email verification into your signup form or backend. Code examples in Python, JavaScript, and PHP included.

Sabbir Hossain
Sabbir Hossain
SF Email Verifier team
August 10, 2026

Regex catches malformed strings. It doesn’t catch a well-formatted address at a domain with no mail server, or a mailbox that never existed. If you’re building a signup flow, the fix for that gap is a server-side call to a verification API — not another regex pattern.

This is a working integration guide, not a marketing page. Here’s the request shape, response handling, where to actually put the check in your flow, and the practical details — rate limits, error handling, caching — that determine whether an integration works smoothly in production or causes headaches six months in.

Where to call it in your flow

Two common patterns, and they’re not mutually exclusive:

  • Inline, at signup. Call the API server-side when the form submits, before creating the account. Reject or flag addresses that come back invalid, and consider a softer UX for “risky” or “catch-all” (a warning rather than a hard block — see what “email verified” actually means for why those statuses aren’t the same as invalid).

  • Batch, on a schedule. Run existing user or lead records through the API periodically to catch addresses that decayed after signup — mailboxes get closed, domains lose MX records, and a valid address today isn’t a guarantee for next year.

A third pattern worth mentioning, less common but useful for specific cases: triggered re-verification, where you call the API again for a specific address only when something prompts it — a bounced transactional email, a support ticket about not receiving a password reset, a user reporting they never got a notification. This targets the check exactly where a problem has already surfaced, rather than checking proactively or on a blanket schedule.

Basic request shape

Most verification APIs, including sfemailverifier.com’s, follow a simple pattern: send an email address, get back a status. A typical request looks like this:

GET https://api.sfemailverifier.com/v1/verify?email=someone@example.com&api_key=YOUR_KEY

And a typical response:

{
  "email": "someone@example.com",
  "status": "valid",
  "mx_found": true,
  "disposable": false,
  "catch_all": false
}

Check the current API docs for your account for exact endpoint names and parameters — this is illustrative of the shape, not a guarantee of the literal field names on your plan.

Python example

import requests

def verify_email(email, api_key):
    response = requests.get(
        "https://api.sfemailverifier.com/v1/verify",
        params={"email": email, "api_key": api_key},
        timeout=5
    )
    data = response.json()
    return data.get("status")

status = verify_email("someone@example.com", "YOUR_KEY")
if status == "invalid":
    print("Reject signup — mailbox does not exist")
elif status in ("risky", "catch_all"):
    print("Flag for review, don't hard-block")
else:
    print("Proceed with signup")

Note the explicit timeout parameter — a real production integration should never make a network call without one, since a hung request with no timeout can quietly stall your entire signup flow if the API is ever slow to respond.

JavaScript (Node.js) example

async function verifyEmail(email, apiKey) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 5000);

  try {
    const url = `https://api.sfemailverifier.com/v1/verify?email=${encodeURIComponent(email)}&api_key=${apiKey}`;
    const response = await fetch(url, { signal: controller.signal });
    const data = await response.json();
    return data.status;
  } finally {
    clearTimeout(timeoutId);
  }
}

verifyEmail("someone@example.com", "YOUR_KEY").then((status) => {
  if (status === "invalid") {
    console.log("Reject signup");
  } else if (["risky", "catch_all"].includes(status)) {
    console.log("Flag, don't block");
  } else {
    console.log("Proceed");
  }
});

PHP example

<?php
function verifyEmail($email, $apiKey) {
    $url = "https://api.sfemailverifier.com/v1/verify?" . http_build_query([
        "email" => $email,
        "api_key" => $apiKey,
    ]);

    $context = stream_context_create(["http" => ["timeout" => 5]]);
    $response = file_get_contents($url, false, $context);
    if ($response === false) {
        return null; // treat as unknown, don't block signup
    }
    $data = json_decode($response, true);
    return $data["status"] ?? null;
}

$status = verifyEmail("someone@example.com", "YOUR_KEY");
if ($status === "invalid") {
    echo "Reject signup";
} elseif (in_array($status, ["risky", "catch_all"])) {
    echo "Flag for review";
} elseif ($status === null) {
    echo "Verification unavailable, proceed with caution";
} else {
    echo "Proceed";
}

Handling each status in code

Don’t treat the response as a boolean. Build for the actual status values:

  • valid → proceed

  • invalid → reject, with a clear error message asking the user to double-check their address

  • disposable → your call — many products block these outright for marketing-list quality; others allow it but exclude from nurture sequences

  • risky / catch_all → don’t hard-block. These aren’t confirmed bad, just unconfirmed. A soft warning (“we couldn’t fully confirm this address — please double check it”) respects the ambiguity honestly.

Rate limits and error handling

Build for the API being occasionally slow or unavailable, same as any external dependency — don’t let a verification timeout block your entire signup flow. A reasonable pattern: if the API call fails or times out, fall back to regex-only validation for that request rather than blocking signups outright, and flag the account for a background re-check later.

Beyond simple timeouts, it’s worth planning for a few other realistic failure modes. Rate limit responses (typically an HTTP 429 status) mean you’ve exceeded your plan’s request limit — a well-built integration catches this specifically and either queues the request for retry after a backoff period, or gracefully degrades to regex-only for that request, rather than treating it the same as a generic failure. Malformed responses (an unexpected JSON shape, a field that’s missing when your code expects it) should be handled defensively — wrap your JSON parsing in error handling that assumes the response might not match your expectations exactly, rather than letting an unexpected format crash your signup flow entirely. Network-level failures (DNS resolution issues, connection refused, SSL errors) are distinct from a slow-but-eventually-successful response, and your retry logic should probably treat them differently — a network failure might warrant an immediate retry, while a slow response might warrant simply waiting longer before giving up.

Caching and avoiding redundant calls

If your signup form calls the verification API on every keystroke or every form submission attempt (common if you’re validating as the user types, or if a submission fails for an unrelated reason and gets resubmitted), you can end up making the same verification call multiple times for the same address in a short window. A simple in-memory or short-lived cache — keyed on the email address, with a reasonable expiration (a few minutes is usually enough) — avoids burning through your API request allowance on redundant checks for an address that hasn’t changed.

This matters more as your form’s traffic grows. A form with client-side validation retries or a multi-step signup wizard that re-validates the same field across steps can easily triple or quadruple the actual number of API calls relative to the number of unique addresses being checked, without a caching layer to catch the duplicates.

Deciding between synchronous and asynchronous verification

For most signup flows, a synchronous call — make the request, wait for the response, then proceed — is the simplest and most appropriate pattern, since the whole round trip typically completes in under a second. But for some architectures, particularly high-throughput signup flows or systems already built around asynchronous, queue-based processing, it’s worth considering an asynchronous pattern instead: create the account provisionally, queue a verification job, and update the account’s status once the result comes back, rather than making the user’s browser wait on the API call directly.

The trade-off is real either way. Synchronous verification gives you an immediate, clean answer before the account even exists, which is simpler to reason about and avoids ever having an unverified account in your system at all. Asynchronous verification keeps your signup flow fast and decoupled from any external API’s response time, at the cost of briefly having accounts in an unconfirmed state that your application logic needs to handle gracefully (What can an unverified user do in the meantime? Do they see a different UI state?). Most teams start with the synchronous pattern, since it’s simpler, and only move to asynchronous if signup volume or latency requirements specifically demand it.

Logging and observability for your verification calls

It’s worth building basic logging into your integration from day one, not as an afterthought once something goes wrong. At minimum, log the email domain (not necessarily the full address, depending on your data privacy practices), the returned status, and the response time for each verification call. This gives you the data to answer questions that inevitably come up later: is our invalid rate creeping up, suggesting a problem with how we’re collecting addresses? Is our average API response time increasing, suggesting we should look at our timeout settings? Are we hitting rate limits more often than expected, suggesting we need a higher-tier plan?

Without this logging, these questions require guesswork or a support ticket to your verification provider. With it, they’re a quick query against your own logs. This is a small investment at implementation time that pays off significantly the first time something about your signup flow’s behavior looks off and you need to diagnose why.

Handling international and unusual email formats

A detail worth building into your integration deliberately rather than discovering through a bug report: email addresses from outside the most common Western formats can include characters and structures that a naive implementation might mishandle. Internationalized domain names (domains using non-Latin scripts, which get encoded into a specific ASCII-compatible format called Punycode for DNS purposes) and less common but technically valid local-part formats can trip up an integration that wasn’t built with this in mind.

Most well-built verification APIs handle this correctly on their end, but it’s worth confirming your own code isn’t mangling the address before it even reaches the API — for instance, through improper URL encoding of special characters in the email address when constructing the request. Testing with at least one internationalized domain example, if your user base is at all global, catches this category of bug before a real user hits it.

A quick example

An engineering team at a two-sided marketplace app — call it Fieldrun — was seeing a strange pattern: signups looked healthy, but a chunk of new users never opened their welcome email, never verified their account, and churned before doing anything. Nobody thought to check whether the addresses were even reachable, because the form’s regex check made everything look formally valid.

Adding an API call at signup, rejecting confirmed-invalid addresses and flagging catch-all/risky ones for a secondary email confirmation step, cut that silent-churn group by a large chunk within the first month. The accounts that would have gone nowhere anyway simply stopped being created in the first place.

Worth adding a detail from Fieldrun’s actual rollout: their first implementation didn’t include a timeout on the API call, and during a brief period when the verification service experienced elevated response times, their entire signup form effectively stalled for affected users, since the server-side request was blocking account creation indefinitely. Adding an explicit timeout with a documented fallback to regex-only validation — exactly the pattern shown in the code examples above — resolved this, and it’s a detail worth building in from the start rather than discovering the hard way after a real incident.

Testing your integration before it goes live

Before shipping any of this to production, it’s worth deliberately testing a handful of known cases rather than just trusting the happy path. Test a known-good address (your own email) and confirm it returns valid. Test an obviously malformed one and confirm your regex layer catches it before the API call even happens. Test a made-up address at a real domain with a working mail server and confirm it returns invalid. And if you can, test what your code actually does when the API call times out or returns an error — simulate this deliberately, rather than assuming your fallback logic works correctly just because you wrote it.

This last case is the one most commonly skipped, and it’s the one most likely to cause a real production issue, since it’s the scenario that’s hardest to catch through casual manual testing (the API is usually fast and reliable, so a developer testing the happy path repeatedly may never actually trigger the failure path before shipping).

Security considerations for your API key

A detail that’s easy to overlook in a rush to ship: your verification API key should never be exposed client-side, in JavaScript that runs in the user’s browser, or committed to a public code repository. All of the code examples above make the verification call server-side specifically for this reason — a key embedded in front-end code is visible to anyone who opens their browser’s developer tools, and a leaked key can be used by anyone to consume your API allowance, potentially exhausting your rate limit or running up unexpected usage charges.

Standard practice here is the same as for any third-party API key: store it as an environment variable or in a secrets manager, never hard-code it directly into source files that get committed to version control, and rotate it if you ever suspect it’s been exposed. This is basic hygiene, but it’s worth stating explicitly since a surprising number of real-world integration mistakes come down to exactly this oversight.

How to think about this as a phased rollout rather than a single launch

For teams nervous about adding a new dependency to a critical signup flow, it’s worth considering a phased approach rather than flipping a switch for all traffic at once. A reasonable first phase: add the verification call, log the results, but don’t act on them yet — just observe what proportion of your real signup traffic comes back valid versus invalid versus ambiguous, without changing any user-facing behavior. This gives you real data about your own signup traffic’s quality before you start making decisions based on it.

A second phase might enforce hard rejection only for confirmed-invalid results, still without touching risky or catch-all handling. A third phase adds the softer handling for ambiguous statuses — the confirmation email step, the review queue — once you’ve built confidence in how the earlier phases performed. This staged approach costs a bit more calendar time than a single big-bang rollout, but it substantially reduces the risk of an unexpected surprise (an unusually high false-positive rate on a specific ambiguous status, say) affecting real users before you’ve had a chance to observe and tune the system’s behavior on your actual traffic.

Get an API key and try a real request

Grab an API key from sfemailverifier.com and send a test request — the docs page has copy-paste examples in more languages than covered here. For checking an existing list instead of building live integration, the bulk verifier is the simpler starting point, and it’s worth running your current user base through it once even after you’ve added API-based checking at signup, to catch anyone who joined before the integration went live and clean up whatever accumulated in the meantime.



Share this post

Questions

Frequently asked questions

Will calling the API slow down my signup form?

Typically not noticeably. Most verification API calls return in under a second, making them fast enough to run inline without creating a visible delay for users.

What should I do if the API is down when someone signs up?

Fall back to regex-only validation instead of blocking the signup. You can then queue the email address for background verification once the API becomes available again.

Should I hard-block signups with a “risky” status?

Generally, no. A risky status means the address is unconfirmed, not necessarily invalid. A soft warning or an additional confirmation-email step is usually a better approach than blocking the signup.

Can I use the API for bulk checks instead of one-off signups?

Yes. However, if you’re checking a large existing file rather than integrating verification into a live signup flow, using a bulk verifier with CSV upload is usually simpler than making individual API calls yourself.

Do I need a paid plan to use the API?

API access is typically included in a paid tier rather than a free single-check tool. Check the current pricing and plan details because free tiers are generally designed for casual, one-off checks.

What timeout value should I use for the API call?

A few seconds is reasonable for most use cases. Five seconds is a common starting point because it allows time for backend retries or SMTP-level checks without letting a slow request noticeably delay signup. Once you have production data, tune the timeout based on your actual response times.

How do I handle a rate-limit error from the API?

Check for the provider’s specific rate-limit status code, commonly HTTP 429. You can either retry the request after a short delay with backoff or gracefully fall back to regex-only validation for that request instead of treating the rate limit as a hard failure.

Should I cache verification results to avoid repeated API calls for the same address?

Yes. A short-lived cache keyed by the email address can prevent unnecessary duplicate API calls, especially with multi-step forms or client-side re-validation during the same signup attempt.

Is it safe to expose my API key in front-end JavaScript?

No. Verification calls should always be made server-side. Store your API key in an environment variable or secrets manager, never in browser-accessible code. A front-end key can be extracted and misused by anyone inspecting the page’s network traffic.

Should I roll out API-based verification to all signup traffic at once, or gradually?

A phased rollout is generally safer for a critical signup flow. Start by logging verification results without taking action, then enforce rejection only for confirmed-invalid addresses. Once you understand the results on real traffic, introduce softer handling for ambiguous statuses.

Sabbir Hossain
Written by
Sabbir Hossain
CTO

Passionate about technology, innovation, and creating impactful digital solutions.

LinkedIn
/// stop guessing

Clean your list in 60 seconds.

Run your next campaign against a verified list. 20 free credits every day, no card required.

Start free