DEV Community

Daniel Igel
Daniel Igel

Posted on

Cut signup spam: 3-layer email validation with RFC syntax, MX check and disposable detection

Most signup forms accept anything that looks like an email. That costs you: bounced sends tank your deliverability, disposable addresses inflate your free tier, and role addresses like admin@example.com rarely convert.

Three layers catch what a regex alone misses. One GET returns all of them:

curl --request GET \
  --url 'https://api.sprytools.com/v1/email-validation/api/v1/validate?email=user@yopmail.com' \
  --header 'x-api-key: YOUR_API_KEY'
Enter fullscreen mode Exit fullscreen mode

The response tells you exactly why an address failed:

{
  "email": "user@yopmail.com",
  "valid": false,
  "reason": "disposable_email",
  "checks": { "syntax": true, "mxRecords": true, "disposable": true }
}
Enter fullscreen mode Exit fullscreen mode

reason is one of invalid_syntax, no_mx_records, or disposable_email. Valid addresses also return roleBased, freeProvider, and a typo suggestion — so you can surface a polite correction before the user submits (gnail.comgmail.com).

Batch-validate up to 100 addresses in one call by POSTing to the same path:

const res = await fetch(
  'https://api.sprytools.com/v1/email-validation/api/v1/validate',
  {
    method: 'POST',
    headers: {
      'x-api-key': process.env.SPRYTOOLS_API_KEY,
      'content-type': 'application/json',
    },
    body: JSON.stringify({ emails: importedList }),
  }
);
const { results, summary } = await res.json();
// summary: { total, valid, invalid }
Enter fullscreen mode Exit fullscreen mode

No ML model, no third-party lookups beyond DNS — syntax is a pure RFC 5322 regex, MX is a standard DNS check, disposable uses a curated blocklist.

Free key: 100 calls/day, no credit card — https://sprytools.com/apis/email-validation/

What's your current approach for email validation at signup — client-side regex only, or do you also check MX and disposable providers?

Top comments (0)