<?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: J Kelly</title>
    <description>The latest articles on DEV Community by J Kelly (@jak220).</description>
    <link>https://dev.to/jak220</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3825532%2F160a5a4a-ceda-47f1-9239-4a9165c26868.png</url>
      <title>DEV Community: J Kelly</title>
      <link>https://dev.to/jak220</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jak220"/>
    <language>en</language>
    <item>
      <title>Week 1 of Launching EmailCheck.dev - What I Learned</title>
      <dc:creator>J Kelly</dc:creator>
      <pubDate>Sun, 22 Mar 2026 15:41:14 +0000</pubDate>
      <link>https://dev.to/jak220/week-1-of-launching-emailcheckdev-what-i-learned-3nnc</link>
      <guid>https://dev.to/jak220/week-1-of-launching-emailcheckdev-what-i-learned-3nnc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Week 1 of Launching EmailCheck.dev - What I Learned About Zero to One&lt;/strong&gt;&lt;br&gt;
The unglamorous reality of launching a developer tool with no audience&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;br&gt;
Launched my email validation API a week ago. Posted on Indie Hackers and Dev.to. Got 50 visitors and exactly zero signups. Here's what I learned (and what I'm fixing).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Launch Plan (That Didn't Work)&lt;/strong&gt;&lt;br&gt;
Last week I launched EmailCheck.dev - an email validation API that's 90% cheaper than NeverBounce ($9/mo vs $40/mo). I built it after getting a $487 bill from NeverBounce for my main SaaS.&lt;br&gt;
My launch strategy was textbook indie hacker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post on Indie Hackers ✓&lt;/li&gt;
&lt;li&gt;Write Dev.to article ✓&lt;/li&gt;
&lt;li&gt;Share on Twitter ✓&lt;/li&gt;
&lt;li&gt;Send DMs to founders ✓&lt;/li&gt;
&lt;li&gt;Wait for signups ✓&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Results after 48 hours:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indie Hackers: 0 upvotes, 0 comments&lt;/li&gt;
&lt;li&gt;Dev.to: 0 reactions, 0 comments&lt;/li&gt;
&lt;li&gt;Twitter: Crickets&lt;/li&gt;
&lt;li&gt;DM responses: 0&lt;/li&gt;
&lt;li&gt;Beta signups: 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ouch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Actually Happened&lt;/strong&gt;&lt;br&gt;
I thought my posts would get buried because of bad timing or unlucky algorithms. But that wasn't the real problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real problem: I have no audience.&lt;/strong&gt;&lt;br&gt;
When you post on Indie Hackers with 0 followers, you need engagement in the first 1-2 hours or the algorithm buries you forever. Same with Dev.to. Same with Twitter.&lt;/p&gt;

&lt;p&gt;No followers → No early engagement → Algorithm thinks nobody cares → Post dies on page 5&lt;/p&gt;

&lt;p&gt;I basically threw my launch into the void and expected it to echo back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Analytics Don't Lie&lt;/strong&gt;&lt;br&gt;
By day 3, I had 50 visitors total. Let me break that down:&lt;br&gt;
&lt;strong&gt;Traffic sources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Me checking the site: ~15 visits&lt;/li&gt;
&lt;li&gt;A few random Twitter clicks: ~10 visits&lt;/li&gt;
&lt;li&gt;Unknown (probably me on different devices): ~15 visits&lt;/li&gt;
&lt;li&gt;Actual interested developers: ~10 visits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conversion rate:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50 visitors → 0 signups = 0%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For context, a decent beta signup conversion is 10-20%. Even a terrible conversion rate is 2-5%.&lt;br&gt;
0% means something is fundamentally broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Form Friction Discovery&lt;/strong&gt;&lt;br&gt;
Here's the embarrassing part. My beta signup form asked for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;"What will you use it for?" (text area)&lt;/li&gt;
&lt;li&gt;Twitter (optional)&lt;/li&gt;
&lt;li&gt;Company (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I thought this was reasonable. I wanted to know my users!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But here's what actually happens in someone's brain:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sees form&lt;/li&gt;
&lt;li&gt;"Name... okay"&lt;/li&gt;
&lt;li&gt;"Email... fine"&lt;/li&gt;
&lt;li&gt;"What will I use it for? Ugh, I have to write something?"&lt;/li&gt;
&lt;li&gt;"Is my use case good enough?"&lt;/li&gt;
&lt;li&gt;"This seems like work..."&lt;/li&gt;
&lt;li&gt;[closes tab]&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Form friction is conversion death.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Industry benchmarks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 field (email): 20-30% conversion&lt;/li&gt;
&lt;li&gt;2 fields: 10-15% conversion&lt;/li&gt;
&lt;li&gt;3+ fields: 2-5% conversion&lt;/li&gt;
&lt;li&gt;My form: 0% (because even the 2% bounced)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was literally asking people to write an essay to try my API for free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Wrong Traffic Problem&lt;/strong&gt;&lt;br&gt;
The 10-15 people who weren't me checking the site? They probably weren't even developers.&lt;/p&gt;

&lt;p&gt;I posted on Twitter without any relevant hashtags. I DMed founders but sent them to my homepage instead of the beta page. I had no targeting, no strategy, just "post it everywhere and hope."&lt;/p&gt;

&lt;p&gt;You can't convert visitors who don't have the problem you're solving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A consumer seeing my site:&lt;/strong&gt;&lt;br&gt;
"Email validation API... what's an API? Eh, not for me." [leaves]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A developer who doesn't deal with signups:&lt;/strong&gt;&lt;br&gt;
"Email validation... I don't have this problem." [leaves]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A developer who uses NeverBounce and is happy:&lt;/strong&gt;&lt;br&gt;
"$9/mo is cheap but switching APIs is annoying." [leaves]&lt;/p&gt;

&lt;p&gt;I needed developers who:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have fake signup problems&lt;/li&gt;
&lt;li&gt;Are currently paying too much&lt;/li&gt;
&lt;li&gt;Are actively looking for solutions&lt;/li&gt;
&lt;li&gt;Are willing to try new tools&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's maybe 0.1% of developers. No wonder I got zero signups from random traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'm Changing&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Fix #1: Email-only signup&lt;/strong&gt;&lt;br&gt;
Changed the beta form to literally just email. No name, no use case, no optional fields. Just:&lt;br&gt;
[&lt;a href="mailto:your@email.com"&gt;your@email.com&lt;/a&gt;]&lt;br&gt;
[Get Free Access]&lt;/p&gt;

&lt;p&gt;I can ask about their use case AFTER they sign up, in a welcome email. Or never, and just watch what they do with the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix #2: Target actual developers&lt;/strong&gt;&lt;br&gt;
Instead of posting everywhere, I'm focusing on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hacker News - Actual developers, chronological feed, everyone gets seen&lt;/li&gt;
&lt;li&gt;Reddit r/webdev - Developers dealing with forms/validation&lt;/li&gt;
&lt;li&gt;Dev.to with better tags - Using #api, #saas, #webdev, #showdev&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Places where my target users actually hang out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix #3: Lead with the pain, not the product&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Old approach:&lt;/strong&gt;&lt;br&gt;
"Check out EmailCheck.dev - an email validation API!"&lt;br&gt;
&lt;strong&gt;New approach:&lt;/strong&gt;&lt;br&gt;
"I got a $487 bill from NeverBounce last month. That's 25% of my revenue just validating emails. So I built my own for $9/mo."&lt;br&gt;
Story first, product second. Nobody cares about features. They care about their own problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix #4: Build an engagement network&lt;/strong&gt;&lt;br&gt;
I reached out to 5 developer friends and asked them to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upvote my posts&lt;/li&gt;
&lt;li&gt;Leave real comments/questions&lt;/li&gt;
&lt;li&gt;Share if they think it's useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not fake engagement, but genuine help from people who want to see me succeed.&lt;br&gt;
Early engagement = algorithm boost = more visibility = actual users see it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Learned About Building in Public&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;The hard truth about launching with no audience:&lt;/strong&gt;&lt;br&gt;
You need to post the same thing 10-20 times across different channels, with different angles, over 3-4 weeks before something sticks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your first post will probably die.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrong time&lt;/li&gt;
&lt;li&gt;Wrong title&lt;/li&gt;
&lt;li&gt;No engagement&lt;/li&gt;
&lt;li&gt;Algorithm buries it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Your second post will do better.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better timing&lt;/li&gt;
&lt;li&gt;Better title&lt;/li&gt;
&lt;li&gt;A few upvotes from network&lt;/li&gt;
&lt;li&gt;Maybe 10 signups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Your 10th post might blow up.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perfect timing&lt;/li&gt;
&lt;li&gt;Compelling story&lt;/li&gt;
&lt;li&gt;Catches algorithm wave&lt;/li&gt;
&lt;li&gt;Hits front page&lt;/li&gt;
&lt;li&gt;500 visitors, 100 signups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Most people quit after post #2.&lt;/strong&gt;&lt;br&gt;
I'm on post #3. I need 7 more attempts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Technical Side (For Nerds)&lt;/strong&gt;&lt;br&gt;
Since this is Dev.to, here's what I built:&lt;br&gt;
&lt;strong&gt;Stack:&lt;/strong&gt;&lt;br&gt;
.NET 10 API (yeah, I know, but it's FAST)&lt;br&gt;
Fly.io hosting ($3/month)&lt;br&gt;
MaxMind GeoIP for IP validation&lt;br&gt;
Supabase for auth/database&lt;br&gt;
Stripe for payments&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API endpoint:&lt;/strong&gt;&lt;br&gt;
bashPOST &lt;a href="https://api.emailcheck.dev/v1/validate" rel="noopener noreferrer"&gt;https://api.emailcheck.dev/v1/validate&lt;/a&gt;&lt;br&gt;
X-API-Key: your_key&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "email": "&lt;a href="mailto:user@gmial.com"&gt;user@gmial.com&lt;/a&gt;"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
json{&lt;br&gt;
  "valid": false,&lt;br&gt;
  "email": "&lt;a href="mailto:user@gmial.com"&gt;user@gmial.com&lt;/a&gt;",&lt;br&gt;
  "syntax_valid": true,&lt;br&gt;
  "mx_records_found": false,&lt;br&gt;
  "disposable": false,&lt;br&gt;
  "suggested_email": "&lt;a href="mailto:user@gmail.com"&gt;user@gmail.com&lt;/a&gt;",&lt;br&gt;
  "confidence_score": 20&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hard parts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disposable detection (used GitHub's 10k domain list)&lt;/li&gt;
&lt;li&gt;Typo suggestions (custom Levenshtein distance algo)&lt;/li&gt;
&lt;li&gt;Rate limiting per API key&lt;/li&gt;
&lt;li&gt;MX record caching (DNS lookups are slow)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The easy parts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax validation (regex)&lt;/li&gt;
&lt;li&gt;Basic API structure&lt;/li&gt;
&lt;li&gt;Stripe integration (surprisingly simple)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Current Status&lt;br&gt;
Week 1 metrics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visitors: 50&lt;/li&gt;
&lt;li&gt;Signups: 0&lt;/li&gt;
&lt;li&gt;Lessons learned: Many&lt;/li&gt;
&lt;li&gt;Times I wanted to quit: 3&lt;/li&gt;
&lt;li&gt;Times I actually quit: 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Week 2 goals:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplify signup form (email only) ✓&lt;/li&gt;
&lt;li&gt;Post on Hacker News&lt;/li&gt;
&lt;li&gt;Post on Reddit r/webdev&lt;/li&gt;
&lt;li&gt;Get first 10 signups&lt;/li&gt;
&lt;li&gt;Actually respond to feedback this time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Realistic timeline:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Week 1: 0-5 signups (learning)&lt;/li&gt;
&lt;li&gt;Week 2: 10-20 signups (fixing conversion)&lt;/li&gt;
&lt;li&gt;Week 3: 30-50 signups (momentum)&lt;/li&gt;
&lt;li&gt;Week 4: 80-100 signups (beta full)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not the overnight success I fantasized about, but achievable.&lt;/p&gt;

&lt;p&gt;What I'm Still Figuring Out&lt;br&gt;
Pricing:&lt;/p&gt;

&lt;p&gt;Am I too cheap? ($9 vs $40)&lt;br&gt;
Should I raise prices?&lt;br&gt;
Or is cheap pricing my only advantage?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Features:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do people actually need catch-all detection?&lt;br&gt;
Is email health scoring (0-100) useful?&lt;br&gt;
Should I build CSV bulk upload first?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Marketing:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keep posting organically?&lt;br&gt;
Try paid ads? (Reddit, Google)&lt;br&gt;
Sponsor dev newsletters?&lt;br&gt;
Just focus on making it really good?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target market:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Indie developers? (they love cheap)&lt;br&gt;
Small SaaS companies? (they need features)&lt;br&gt;
Agencies? (they need white-label)&lt;br&gt;
All of the above? (confusing positioning)&lt;/p&gt;

&lt;p&gt;No idea. I'm making it up as I go.&lt;/p&gt;

&lt;p&gt;The Unglamorous Reality&lt;br&gt;
Building in public sounds sexy:&lt;/p&gt;

&lt;p&gt;Daily progress tweets&lt;br&gt;
Indie Hackers success posts&lt;br&gt;
"Made $10k MRR in 3 months!" screenshots&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But the reality is:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Posting and getting crickets&lt;br&gt;
Refreshing analytics obsessively&lt;br&gt;
Checking beta signups every 10 minutes&lt;br&gt;
Getting exactly zero for days&lt;br&gt;
Wondering if you should quit&lt;br&gt;
Deciding to try one more channel&lt;br&gt;
Repeat&lt;/p&gt;

&lt;p&gt;Nobody sees the 47 posts that flopped.&lt;br&gt;
They only see the one that hit front page.&lt;br&gt;
I'm still on flop #3. Got a long way to go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to Help?&lt;/strong&gt;&lt;br&gt;
If you made it this far, here's how you can help:&lt;br&gt;
If you deal with user signups:&lt;br&gt;
Try EmailCheck.dev for free. Tell me what sucks. I'll fix it.&lt;br&gt;
If you've launched before:&lt;br&gt;
What actually worked for you? I'm all ears.&lt;br&gt;
If you just want to follow along:&lt;br&gt;
I'm posting daily updates on Twitter &lt;a href="https://dev.tomostly%20failures,%20occasionally%20wins"&gt;@yourhandle&lt;/a&gt;.&lt;br&gt;
And if you have literally any feedback - good, bad, brutal - leave a comment. Zero engagement is way worse than negative engagement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Beta signup: &lt;a href="https://emailcheck.dev/beta" rel="noopener noreferrer"&gt;https://emailcheck.dev/beta&lt;/a&gt; (now just email, I promise)&lt;/li&gt;
&lt;li&gt;API docs: &lt;a href="https://emailcheck.dev/docs" rel="noopener noreferrer"&gt;https://emailcheck.dev/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub (disposable domains): &lt;a href="https://github.com/disposable/disposable-email-domains" rel="noopener noreferrer"&gt;https://github.com/disposable/disposable-email-domains&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Thought&lt;/strong&gt;&lt;br&gt;
Launching a product with no audience is humbling.&lt;br&gt;
You think: "If I build something good, people will come."&lt;br&gt;
Reality: Nobody will see it unless you make them see it, repeatedly, for weeks.&lt;br&gt;
The difference between success and failure isn't the product.&lt;br&gt;
It's whether you keep posting after post #7 gets zero engagement.&lt;br&gt;
I'm on post #3. See you at post #10.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>api</category>
      <category>buildinpublic</category>
      <category>startup</category>
    </item>
    <item>
      <title>How I Stopped Worrying About Fake Signups (And Built an API Instead)</title>
      <dc:creator>J Kelly</dc:creator>
      <pubDate>Tue, 17 Mar 2026 18:24:01 +0000</pubDate>
      <link>https://dev.to/jak220/how-i-stopped-worrying-about-fake-signups-and-built-an-api-instead-24hb</link>
      <guid>https://dev.to/jak220/how-i-stopped-worrying-about-fake-signups-and-built-an-api-instead-24hb</guid>
      <description>&lt;p&gt;If you’ve ever built a signup flow, you’ve probably run into this at some point:&lt;/p&gt;

&lt;p&gt;You launch something… and within a few days you start seeing signups like:&lt;/p&gt;

&lt;p&gt;&lt;a href="mailto:asdf123@mailinator.com"&gt;asdf123@mailinator.com&lt;/a&gt;&lt;br&gt;
&lt;a href="mailto:test@test.com"&gt;test@test.com&lt;/a&gt;&lt;br&gt;
&lt;a href="mailto:qweqwe@tempmail.xyz"&gt;qweqwe@tempmail.xyz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first I ignored it. Then it started affecting things:&lt;br&gt;
noisy user data&lt;br&gt;
wasted emails&lt;br&gt;
skewed metrics&lt;br&gt;
occasional abuse&lt;/p&gt;

&lt;p&gt;That’s when I realized basic email validation wasn’t doing much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regex isn’t enough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like most people, I started with a simple regex check.&lt;/p&gt;

&lt;p&gt;It works for catching obvious mistakes, but that’s about it.&lt;br&gt;
It doesn’t tell you:&lt;br&gt;
if the domain exists&lt;br&gt;
if the domain can receive email&lt;br&gt;
if it’s a disposable address&lt;/p&gt;

&lt;p&gt;So technically valid emails were still completely useless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually helped&lt;/strong&gt;&lt;br&gt;
I started layering in a few additional checks.&lt;br&gt;
Nothing fancy — just practical stuff that could run fast enough during signup.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Domain validation&lt;br&gt;
Make sure the domain actually exists.&lt;br&gt;
This alone filters out a surprising number of junk entries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MX record check&lt;br&gt;
Check if the domain can receive email.&lt;br&gt;
If there are no MX records, it’s not going anywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disposable email detection&lt;br&gt;
This made the biggest difference.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are tons of disposable email providers, and once you start blocking those, a lot of low-effort spam disappears.&lt;/p&gt;

&lt;p&gt;The tradeoff: speed vs accuracy&lt;/p&gt;

&lt;p&gt;At one point I looked into SMTP-level verification (checking if the mailbox actually exists).&lt;/p&gt;

&lt;p&gt;It works… but it’s slow.&lt;/p&gt;

&lt;p&gt;And for a signup form, speed matters more than being 100% certain.&lt;/p&gt;

&lt;p&gt;I’d rather:&lt;br&gt;
let a few bad emails through&lt;br&gt;
than slow down the signup experience for real users&lt;/p&gt;

&lt;p&gt;So I focused on checks that are:&lt;br&gt;
fast&lt;br&gt;
reliable enough&lt;br&gt;
easy to run in real time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I ended up doing&lt;/strong&gt;&lt;br&gt;
After experimenting with this across a couple projects, I ended up wrapping these checks into a small API so I could reuse it.&lt;/p&gt;

&lt;p&gt;The goal wasn’t to build something overly complex — just something simple that:&lt;/p&gt;

&lt;p&gt;validates emails quickly&lt;br&gt;
catches obvious junk&lt;br&gt;
works well during signup&lt;/p&gt;

&lt;p&gt;Example request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /v1/validate
{
  "email": "user@example.com"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"normalizedEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"suggestedEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidenceScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverabilityLikelihood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"syntaxValid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mxRecordsFound"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"disposable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"roleBased"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"freeProvider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"custom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"catchAll"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"warnings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Where I landed&lt;/strong&gt;&lt;br&gt;
This setup has been a good balance so far:&lt;br&gt;
fewer fake signups&lt;br&gt;
cleaner data&lt;br&gt;
no noticeable impact on signup speed&lt;/p&gt;

&lt;p&gt;It’s not perfect, but it’s good enough for most real-world use cases.&lt;br&gt;
Curious what others are doing&lt;br&gt;
I’m interested in how others are handling this.&lt;/p&gt;

&lt;p&gt;Are you:&lt;br&gt;
sticking with basic validation?&lt;br&gt;
using a third-party service?&lt;br&gt;
doing anything more advanced?&lt;/p&gt;

&lt;p&gt;Always curious to see how people balance accuracy vs performance here.&lt;/p&gt;

&lt;p&gt;If anyone’s interested, happy to share more about what I’ve been building around this.&lt;/p&gt;

&lt;p&gt;I'm looking for active developers to be Beta testers:&lt;br&gt;


&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://emailcheck.dev/beta" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Femailcheck.dev%2Fog-image.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://emailcheck.dev/beta" rel="noopener noreferrer" class="c-link"&gt;
            Join Beta Program - Get 50% Off Forever | EmailCheck.dev
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Pro plan free for 3 months, then 50% off forever. Limited spots available.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Femailcheck.dev%2Ffavicon-96x96.png"&gt;
          emailcheck.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;





</description>
      <category>webdev</category>
      <category>api</category>
      <category>saas</category>
      <category>startup</category>
    </item>
    <item>
      <title>How I Stopped Worrying About Fake Signups (And Built an API Instead)</title>
      <dc:creator>J Kelly</dc:creator>
      <pubDate>Sun, 15 Mar 2026 15:51:55 +0000</pubDate>
      <link>https://dev.to/jak220/how-i-stopped-worrying-about-fake-signups-and-built-an-api-instead-3b1g</link>
      <guid>https://dev.to/jak220/how-i-stopped-worrying-about-fake-signups-and-built-an-api-instead-3b1g</guid>
      <description>&lt;p&gt;&lt;strong&gt;The $500/month wake-up call&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three months ago, I checked my SaaS expenses and nearly fell off my chair. NeverBounce was charging me $487 for the month. For &lt;em&gt;email validation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I run a small project management tool with about 15,000 users. Every signup, I validate their email to make sure it's real. Seems responsible, right? Well, at $0.008 per validation, those pennies add up fast.&lt;/p&gt;

&lt;p&gt;The worst part? About 30% of my "validations" were just people mistyping their email on the signup form. I was literally paying money to tell users "you spelled Gmail wrong."&lt;/p&gt;

&lt;p&gt;So I did what any developer does when they're annoyed: I spent a weekend building my own solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why email validation matters (the hard way)
&lt;/h2&gt;

&lt;p&gt;Before I was paying NeverBounce, I learned the hard way why you need email validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 1 without validation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;47 signups from &lt;code&gt;tempmail.com&lt;/code&gt; addresses&lt;/li&gt;
&lt;li&gt;23 from &lt;code&gt;10minutemail.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Users creating accounts just to leave spam in comments&lt;/li&gt;
&lt;li&gt;Support tickets from people who couldn't log in (they typo'd their email)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The breaking point:&lt;/strong&gt;&lt;br&gt;
One user created 150 accounts using &lt;code&gt;mailinator.com&lt;/code&gt; addresses. Why? To spam our public roadmap with feature requests for cryptocurrency integration. Fun times.&lt;/p&gt;

&lt;p&gt;So yeah, email validation became non-negotiable.&lt;/p&gt;


&lt;h2&gt;
  
  
  The "just use an API" trap
&lt;/h2&gt;

&lt;p&gt;At first, using NeverBounce seemed smart:&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="c1"&gt;// Simple, right?&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;neverBounce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user@example.com&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&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="s1"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked! For about three months. Then my user base grew, my bill tripled, and I started looking for alternatives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternatives I tried:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ZeroBounce&lt;/strong&gt; - $0.008/validation (same price)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hunter.io&lt;/strong&gt; - Better pricing, but wanted me on annual contract&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kickbox&lt;/strong&gt; - Great features, even more expensive&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;They're all good services. They're just... expensive for a bootstrapped side project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building my own (the technical bits)
&lt;/h2&gt;

&lt;p&gt;I figured: how hard can email validation be?&lt;/p&gt;

&lt;p&gt;Spoiler: Harder than I thought, but not impossible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Syntax validation (the easy part)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ValidateSyntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&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="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parts&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="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&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="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Basic checks&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&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="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&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="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Regex for the nitty-gritty&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;regex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Regex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"&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;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsMatch&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This catches like 80% of garbage right away. Emails without @ signs, multiple @ signs, missing domains, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: MX record verification (the actually useful part)
&lt;/h3&gt;

&lt;p&gt;This is where things get interesting. You're checking if the domain can actually &lt;em&gt;receive&lt;/em&gt; emails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CheckMxRecordsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LookupClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QueryAsync&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="n"&gt;QueryType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mxRecords&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Answers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MxRecords&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;mxRecords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&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="k"&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 catches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typos like &lt;code&gt;gmial.com&lt;/code&gt; (no MX records)&lt;/li&gt;
&lt;li&gt;Made-up domains&lt;/li&gt;
&lt;li&gt;Expired domains&lt;/li&gt;
&lt;li&gt;Domains that can't receive email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not perfect (some domains have MX records but still reject mail), but it's pretty good.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Disposable email detection (the pain point)
&lt;/h3&gt;

&lt;p&gt;This is where I initially underestimated the problem.&lt;/p&gt;

&lt;p&gt;Me: "I'll just hardcode the common ones"&lt;br&gt;
Also me, 2 weeks later: "WHY ARE THERE 10,000 DISPOSABLE EMAIL DOMAINS"&lt;/p&gt;

&lt;p&gt;My first attempt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Terrible idea, don't do this&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsDisposable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bad&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"tempmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"10minutemail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"guerrillamail.com"&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;bad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This caught maybe 20% of disposable emails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better approach:&lt;/strong&gt;&lt;br&gt;
I ended up using the GitHub disposable domains list (&lt;a href="https://github.com/disposable/disposable-email-domains" rel="noopener noreferrer"&gt;https://github.com/disposable/disposable-email-domains&lt;/a&gt;). It's maintained by the community and has 10,000+ domains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;HashSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;LoadDisposableDomains&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;domains&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HashSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;StringComparer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAllLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"disposable-domains.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;domains&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Trim&lt;/span&gt;&lt;span class="p"&gt;());&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;domains&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;Much better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Typo suggestions (the surprisingly useful feature)
&lt;/h3&gt;

&lt;p&gt;This one came from user feedback. People kept emailing support: "I can't log in!"&lt;/p&gt;

&lt;p&gt;Turns out, they typo'd their email on signup. &lt;code&gt;gmial.com&lt;/code&gt; instead of &lt;code&gt;gmail.com&lt;/code&gt;. Easy mistake.&lt;/p&gt;

&lt;p&gt;I built a simple typo checker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;SuggestCorrection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;common&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"gmial.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gmail.com"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"gmai.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gmail.com"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"yahooo.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"yahoo.com"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"hotmial.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hotmail.com"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;// ... etc&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;common&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValueOrDefault&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Support tickets dropped by 40% after this. Worth it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making it an API (because why not)
&lt;/h2&gt;

&lt;p&gt;Once I had it working for my own app, I figured: might as well make it an API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.NET API (I know, I know, but it's fast)&lt;/li&gt;
&lt;li&gt;Fly.io for hosting (~$3/month)&lt;/li&gt;
&lt;li&gt;Supabase for auth/database&lt;/li&gt;
&lt;li&gt;Stripe for billing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Endpoint:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST https://api.emailcheck.dev/v1/validate
Content-Type: application/json
X-API-Key: your_key_here

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"email"&lt;/span&gt;: &lt;span class="s2"&gt;"user@gmial.com"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@gmial.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"syntax_valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mx_records_found"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"disposable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"suggested_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"warnings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"MX records not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Did you mean gmail.com?"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The pricing part (where I probably screwed up)
&lt;/h2&gt;

&lt;p&gt;I wanted to make it stupid cheap. Like, "I would have used this instead of NeverBounce" cheap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My pricing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free: 100/month&lt;/li&gt;
&lt;li&gt;Starter: $9/mo (5,000 validations)&lt;/li&gt;
&lt;li&gt;Pro: $29/mo (50,000 validations)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NeverBounce equivalent:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5,000 validations: $40/month&lt;/li&gt;
&lt;li&gt;50,000 validations: $250/month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah, I'm probably leaving money on the table. But honestly? I built this to solve my own problem. If other people find it useful and I can cover server costs, I'm happy.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Things that were easier than expected:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic validation logic&lt;/li&gt;
&lt;li&gt;DNS lookups&lt;/li&gt;
&lt;li&gt;Building the API itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Things that were harder:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disposable email detection (there are SO MANY)&lt;/li&gt;
&lt;li&gt;Edge cases (seriously, email specs are wild)&lt;/li&gt;
&lt;li&gt;Pricing strategy (still not sure I got this right)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Things I'm still working on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Catch-all detection (checking if domain accepts everything)&lt;/li&gt;
&lt;li&gt;Email health scoring (is this a good email or sketchy?)&lt;/li&gt;
&lt;li&gt;Better typo detection (using Levenshtein distance)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it yourself (the sales pitch, I guess)
&lt;/h2&gt;

&lt;p&gt;If you're dealing with fake signups or paying too much for validation, I built this for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick integration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node.js:&lt;/strong&gt;&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;response&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="s1"&gt;https://api.emailcheck.dev/v1/validate&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="s1"&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="s1"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your_key&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="s1"&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="s1"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test@example.com&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="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;response&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&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;requests&lt;/span&gt;

&lt;span class="n"&gt;response&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://api.emailcheck.dev/v1/validate&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test@example.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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="n"&gt;response&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="ow"&gt;not&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;valid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Invalid email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PHP:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api.emailcheck.dev/v1/validate'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_POSTFIELDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test@example.com'&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'X-API-Key: your_key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;true&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="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'valid'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Invalid email'&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;h2&gt;
  
  
  The beta program thing
&lt;/h2&gt;

&lt;p&gt;I'm looking for 50 beta testers to stress test this before I do a proper launch. If you sign up now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free Pro plan for 3 months ($29/mo value)&lt;/li&gt;
&lt;li&gt;50% lifetime discount after beta&lt;/li&gt;
&lt;li&gt;Direct Slack channel with me for support/feature requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interested? &lt;a href="https://emailcheck.dev/beta" rel="noopener noreferrer"&gt;https://emailcheck.dev/beta&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Look, I'm not saying this will replace NeverBounce or ZeroBounce for everyone. They're great services with way more features than mine.&lt;/p&gt;

&lt;p&gt;But if you're a solo dev or small team just trying to keep fake emails out of your database without spending $500/month, I built this for you.&lt;/p&gt;

&lt;p&gt;Also, if you have ideas for features or find bugs, hit me up. I'm literally one person running this thing, so feedback is super valuable.&lt;/p&gt;

&lt;p&gt;Happy validating!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tech details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API: .NET 10&lt;/li&gt;
&lt;li&gt;Hosting: Fly.io&lt;/li&gt;
&lt;li&gt;Database: Supabase (Postgres)&lt;/li&gt;
&lt;li&gt;Payments: Stripe&lt;/li&gt;
&lt;li&gt;Frontend: Vanilla JS (don't judge me)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://emailcheck.dev" rel="noopener noreferrer"&gt;https://emailcheck.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://emailcheck.dev/docs" rel="noopener noreferrer"&gt;https://emailcheck.dev/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Beta signup: &lt;a href="https://emailcheck.dev/beta" rel="noopener noreferrer"&gt;https://emailcheck.dev/beta&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Tags: #webdev #api #saas #email #validation #indie&lt;/em&gt;&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>api</category>
      <category>saas</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
