<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anthony</title>
    <description>The latest articles on DEV Community by Anthony (@atek).</description>
    <link>https://dev.to/atek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3990455%2Fe2d388ec-af96-4678-ac54-94bdd7efb59e.png</url>
      <title>DEV Community: Anthony</title>
      <link>https://dev.to/atek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/atek"/>
    <language>en</language>
    <item>
      <title>How to Validate Email Addresses in Python (Beyond Regex)</title>
      <dc:creator>Anthony</dc:creator>
      <pubDate>Fri, 19 Jun 2026 09:43:50 +0000</pubDate>
      <link>https://dev.to/atek/how-to-validate-email-addresses-in-python-beyond-regex-5i4</link>
      <guid>https://dev.to/atek/how-to-validate-email-addresses-in-python-beyond-regex-5i4</guid>
      <description>&lt;p&gt;Search "validate email Python" and you'll drown in regexes. Regex has its place, but it only answers &lt;em&gt;"does this look like an email?"&lt;/em&gt;, not &lt;em&gt;"can this address actually receive mail?"&lt;/em&gt; Here are the layers, and how to add the ones regex can't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: syntax (regex)
&lt;/h2&gt;

&lt;p&gt;A simple pattern rejects obvious nonsense. Don't chase the giant RFC 5322 monster regex; it's huge and still tells you nothing about whether the domain exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="n"&gt;EMAIL_RE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[^@\s]+@[^@\s]+\.[^@\s]+$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;looks_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EMAIL_RE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Layer 2: domain and MX records
&lt;/h2&gt;

&lt;p&gt;A real address needs a domain that actually accepts mail. Check its MX records with &lt;code&gt;dnspython&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dns.resolver&lt;/span&gt;  &lt;span class="c1"&gt;# pip install dnspython
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;domain_accepts_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removes dead domains and a lot of fakes. It's server-side only, though, and it won't tell you whether an address is disposable, a role account, or a likely typo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: disposable, role, and typo detection
&lt;/h2&gt;

&lt;p&gt;These need data you don't really want to maintain yourself (a current list of throwaway domains, a list of common providers for typo suggestions, and so on). In practice most people reach for an email verification API here, which returns the lot in one call. The response shape is similar across providers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://your-verification-api.example/v1/verify&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Typical response:
# {
#   "status": "risky", "score": 75,
#   "checks": {"syntax": True, "mx_found": True, "disposable": False, "role": False},
#   "did_you_mean": "gmail.com"
# }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting it together at signup
&lt;/h2&gt;

&lt;p&gt;In a Flask or Django view, verify on the server before creating the account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/signup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;undeliverable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;That email can&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t receive mail.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
    &lt;span class="c1"&gt;# "risky" -&amp;gt; warn but allow; "deliverable" -&amp;gt; proceed
&lt;/span&gt;    &lt;span class="nf"&gt;create_account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Show the &lt;code&gt;did_you_mean&lt;/code&gt; hint in your form to recover typos, reject &lt;code&gt;undeliverable&lt;/code&gt; on submit, and treat &lt;code&gt;risky&lt;/code&gt; as a soft warning rather than a hard block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Regex = "looks like an email." Keep it simple.&lt;/li&gt;
&lt;li&gt;MX lookup = "the domain can receive mail." Worth doing.&lt;/li&gt;
&lt;li&gt;Disposable, role, and typo detection plus a single score = the part that actually cleans your signups, and the part not worth maintaining yourself.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I build an email verification API, so this is a topic I work on daily.&lt;br&gt;
The approach above is vendor-neutral; any verification API follows the same shape.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>email</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How email verification works: syntax, MX, and SMTP explained</title>
      <dc:creator>Anthony</dc:creator>
      <pubDate>Fri, 19 Jun 2026 09:40:12 +0000</pubDate>
      <link>https://dev.to/atek/how-email-verification-works-syntax-mx-and-smtp-explained-4k2n</link>
      <guid>https://dev.to/atek/how-email-verification-works-syntax-mx-and-smtp-explained-4k2n</guid>
      <description>&lt;p&gt;"Email verification" sounds like one thing, but it's really a stack of checks of increasing depth and cost. Knowing what each layer actually proves helps you pick the right level instead of overpaying for verification you don't need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: syntax
&lt;/h2&gt;

&lt;p&gt;The cheapest check: does the string look like a valid email address? A pragmatic regex catches obvious garbage (&lt;code&gt;asdf&lt;/code&gt;, &lt;code&gt;a@@b&lt;/code&gt;, trailing spaces). It's instant and free, but weak on its own: &lt;code&gt;nobody@asdf.asdf&lt;/code&gt; passes syntax and can't receive a single message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2: domain and MX records
&lt;/h2&gt;

&lt;p&gt;Next, does the domain actually accept mail? Every domain that receives email publishes &lt;strong&gt;MX (mail exchanger)&lt;/strong&gt; records in DNS pointing to its mail servers. A quick DNS lookup tells you whether any exist. No MX (and no fallback A record) means the domain can't receive mail, so the address is undeliverable no matter how it's spelled. This single step removes a large class of fakes and dead domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: SMTP mailbox check
&lt;/h2&gt;

&lt;p&gt;The deepest level connects to the domain's mail server and begins the motions of sending a message to ask whether that &lt;em&gt;specific mailbox&lt;/em&gt; exists, without actually delivering anything. It's the only layer that can hint a particular inbox is real, but it comes with real caveats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's &lt;strong&gt;slow&lt;/strong&gt; (a live connection per address).&lt;/li&gt;
&lt;li&gt;Many servers are &lt;strong&gt;"accept-all"&lt;/strong&gt; and say yes to everything, so the answer is often meaningless.&lt;/li&gt;
&lt;li&gt;Lots of providers &lt;strong&gt;block or throttle&lt;/strong&gt; these probes, and outbound port 25 is blocked on most modern hosting, so it's frequently unavailable anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SMTP checks matter most for cleaning old, cold lists, and far less for stopping junk at signup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The heuristics layer
&lt;/h2&gt;

&lt;p&gt;Alongside those, useful verification adds signal that has nothing to do with deliverability per se:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disposable detection:&lt;/strong&gt; is it a throwaway provider?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role detection:&lt;/strong&gt; is it &lt;code&gt;info@&lt;/code&gt; or &lt;code&gt;admin@&lt;/code&gt; rather than a person?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typo suggestions:&lt;/strong&gt; "did you mean gmail.com?" for &lt;code&gt;gmial.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A deliverability score:&lt;/strong&gt; one 0–100 number that rolls it all up so you can just threshold on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Which layers do you actually need?
&lt;/h2&gt;

&lt;p&gt;For the most common job, &lt;strong&gt;validating emails at signup&lt;/strong&gt;, syntax + MX + the heuristics catch the overwhelming majority of bad addresses, instantly and cheaply. Live SMTP probing adds cost and unreliability for little extra benefit in that context, and is mainly worth it for bulk cleaning of stale lists.&lt;/p&gt;

&lt;p&gt;So before reaching for the heaviest, slowest option, ask what you're actually trying to prevent. For most signup forms, the first two layers plus a few heuristics are all you need.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I build an email verification API, which is why I spend time thinking about this. The post is vendor-neutral; the layers apply to any tool you choose.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>email</category>
      <category>deliverability</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Reduce Your Signup Bounce Rate (a Practical Playbook)</title>
      <dc:creator>Anthony</dc:creator>
      <pubDate>Thu, 18 Jun 2026 08:23:24 +0000</pubDate>
      <link>https://dev.to/atek/how-to-reduce-your-signup-bounce-rate-a-practical-playbook-3eba</link>
      <guid>https://dev.to/atek/how-to-reduce-your-signup-bounce-rate-a-practical-playbook-3eba</guid>
      <description>&lt;p&gt;A high email bounce rate isn't just annoying; it actively damages your &lt;strong&gt;sender reputation&lt;/strong&gt;, which means even your &lt;em&gt;good&lt;/em&gt; emails start landing in spam. Most bounces trace back to bad addresses captured at signup. Fix the front door and the bounce rate fixes itself.&lt;/p&gt;

&lt;p&gt;Here's the playbook, roughly in order of impact-per-effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Understand why addresses go bad
&lt;/h2&gt;

&lt;p&gt;Four culprits cover almost all of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Typos:&lt;/strong&gt; &lt;code&gt;jane@gmial.com&lt;/code&gt;, &lt;code&gt;bob@hotmial.com&lt;/code&gt;. The user &lt;em&gt;meant&lt;/em&gt; a real address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fakes:&lt;/strong&gt; &lt;code&gt;asdf@asdf.com&lt;/code&gt;, entered to skip your form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disposable:&lt;/strong&gt; &lt;code&gt;x@mailinator.com&lt;/code&gt;, used once then abandoned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role/dead:&lt;/strong&gt; &lt;code&gt;info@&lt;/code&gt;, or addresses at domains with no mail server at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each needs a slightly different defence.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Catch typos in the form (recover real users)
&lt;/h2&gt;

&lt;p&gt;Typos are the highest-value fix because the person &lt;em&gt;wants&lt;/em&gt; to sign up; you just need to nudge them. Verify on blur and surface a suggestion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;emailInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://mailguard-api.atek.workers.dev/v1/verify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;emailInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;did_you_mean&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;did_you_mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;showHint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Did you mean &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;localPart&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;did_you_mean&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single "did you mean gmail.com?" prompt recovers signups you'd otherwise lose to a slipped finger.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Block clearly undeliverable addresses on submit
&lt;/h2&gt;

&lt;p&gt;On submit, reject addresses with no mail server (no MX record) and flag disposable ones. A verification API returns this in one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checks&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undeliverable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;That email can't receive mail.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disposable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please use a permanent email address.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't be &lt;em&gt;too&lt;/em&gt; aggressive: treat "risky" as a soft warning, not a hard block, so you don't lose legitimate users on edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Use double opt-in for anything marketing-related
&lt;/h2&gt;

&lt;p&gt;For newsletters and marketing lists, a confirmation email (double opt-in) guarantees the address is real and that the person wants your mail. It's the single biggest long-term protector of your sender reputation.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Monitor and prune
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Watch your bounce rate per send; investigate spikes.&lt;/li&gt;
&lt;li&gt;Periodically clean your existing list (a batch/CSV verification endpoint makes this   easy) and suppress hard bounces.&lt;/li&gt;
&lt;li&gt;Never email purchased lists; it's the fastest way to get blocklisted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;The 80/20 is steps 2 and 3: &lt;strong&gt;a verification call on your email field&lt;/strong&gt; that powers a typo hint and blocks undeliverable addresses. That alone removes most of the bad emails before they ever reach your database.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I build an email verification API, so this is a topic I work on daily.&lt;br&gt;
The approach above is vendor-neutral; any verification API follows the same shape.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>email</category>
      <category>deliverability</category>
      <category>webdev</category>
      <category>growth</category>
    </item>
    <item>
      <title>How to Validate Email Addresses in JavaScript / Node.js (Beyond Regex)</title>
      <dc:creator>Anthony</dc:creator>
      <pubDate>Thu, 18 Jun 2026 08:22:36 +0000</pubDate>
      <link>https://dev.to/atek/how-to-validate-email-addresses-in-javascript-nodejs-beyond-regex-fk4</link>
      <guid>https://dev.to/atek/how-to-validate-email-addresses-in-javascript-nodejs-beyond-regex-fk4</guid>
      <description>&lt;p&gt;Search "validate email JavaScript" and you'll get a hundred regexes. Regex has its place, but it only answers &lt;em&gt;"does this look like an email?"&lt;/em&gt;, not &lt;em&gt;"can this address actually receive mail?"&lt;/em&gt; This post covers the layers of email validation and how to add the ones regex can't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: syntax (regex), necessary but weak
&lt;/h2&gt;

&lt;p&gt;A pragmatic pattern catches obvious garbage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;looksValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+@&lt;/span&gt;&lt;span class="se"&gt;[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't chase the "perfect" RFC 5322 regex: it's enormous and still won't tell you the domain exists. Use a simple pattern to reject nonsense, then move on.&lt;/p&gt;

&lt;p&gt;What regex &lt;strong&gt;can't&lt;/strong&gt; tell you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the domain have a mail server? (&lt;code&gt;@asdf.asdf&lt;/code&gt; passes regex, accepts no mail.)&lt;/li&gt;
&lt;li&gt;Is it disposable? (&lt;code&gt;@mailinator.com&lt;/code&gt; is perfectly valid syntactically.)&lt;/li&gt;
&lt;li&gt;Did the user mean &lt;code&gt;gmail.com&lt;/code&gt; instead of &lt;code&gt;gmial.com&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layer 2: domain / MX records
&lt;/h2&gt;

&lt;p&gt;A real address needs a domain with an MX (mail exchanger) record. In Node you can check DNS yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolveMx&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:dns/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;domainAcceptsMail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;resolveMx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This already removes a big class of fakes. But it runs only server-side, doesn't cover disposable detection or typo suggestions, and you'll end up maintaining disposable-domain lists yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: disposable, role, and typo detection
&lt;/h2&gt;

&lt;p&gt;This is where a verification API saves you a lot of list-maintenance and DNS plumbing. Rather than rolling it all yourself, one call returns the full picture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;mailguard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailGuard&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mailguard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MAILGUARD_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jane@gmial.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   status: "risky",&lt;/span&gt;
&lt;span class="c1"&gt;//   score: 75,&lt;/span&gt;
&lt;span class="c1"&gt;//   checks: { syntax: true, mx_found: true, disposable: false, role: false },&lt;/span&gt;
&lt;span class="c1"&gt;//   did_you_mean: "gmail.com"&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isDeliverable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// safe to accept&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK is dependency-free and works in Node 18+, Bun, Deno, Cloudflare Workers, and the browser, so the same code runs on your API or your frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting the layers together at signup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;On blur:&lt;/strong&gt; call the API, show a "did you mean…?" hint if &lt;code&gt;did_you_mean&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On submit:&lt;/strong&gt; reject &lt;code&gt;status === "undeliverable"&lt;/code&gt;; warn (don't hard-block) on &lt;code&gt;"risky"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side:&lt;/strong&gt; re-check on the backend too; never trust the client alone.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/signup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undeliverable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// proceed to create the account&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Regex = "looks like an email." Keep it simple.&lt;/li&gt;
&lt;li&gt;MX lookup = "the domain can receive mail." Worth doing.&lt;/li&gt;
&lt;li&gt;Disposable/role/typo detection + a single deliverability score = the part that actually cleans your signups, and the part not worth building from scratch.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I build an email verification API, so this is a topic I work on daily.&lt;br&gt;
The approach above is vendor-neutral; any verification API follows the same shape.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
