<?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: Onizuka</title>
    <description>The latest articles on DEV Community by Onizuka (@onizuka).</description>
    <link>https://dev.to/onizuka</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%2F4033651%2Fc6fdf8b7-8c05-4352-b14f-7cb402d37135.png</url>
      <title>DEV Community: Onizuka</title>
      <link>https://dev.to/onizuka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/onizuka"/>
    <language>en</language>
    <item>
      <title>I verified 1,000 emails via SMTP. 200 hit HIBP breach databases.</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Tue, 13 Oct 2026 17:39:30 +0000</pubDate>
      <link>https://dev.to/onizuka/i-verified-1000-emails-via-smtp-200-hit-hibp-breach-databases-5hdi</link>
      <guid>https://dev.to/onizuka/i-verified-1000-emails-via-smtp-200-hit-hibp-breach-databases-5hdi</guid>
      <description>&lt;p&gt;security, api, cybersecurity, python&lt;/p&gt;

&lt;p&gt;On October 13, 2026, at 17:19 UTC, I called the Email Validator API on &lt;code&gt;test@gmail.com&lt;/code&gt;. The response looked healthy at first glance: &lt;code&gt;valid: true&lt;/code&gt;, &lt;code&gt;mx_found: true&lt;/code&gt;, &lt;code&gt;breach_count: 0&lt;/code&gt;, &lt;code&gt;score: 75&lt;/code&gt;. Then I scrolled down. &lt;code&gt;smtp_verified: null&lt;/code&gt;. &lt;code&gt;is_trusted_identity: null&lt;/code&gt;. &lt;code&gt;breach_status: null&lt;/code&gt;, with the error &lt;code&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/code&gt;. The API had just told me, in effect, &lt;em&gt;"I can't prove this identity is clean, and I'm not going to pretend I can."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That honesty matters, because a week earlier I'd run a different experiment. I verified 1,000 addresses via SMTP for a cold-outreach campaign. Two hundred of them—one in five—were sitting in Have I Been Pwned breach databases. SMTP said deliverable. HIBP said compromised. Both were "true".&lt;/p&gt;

&lt;p&gt;That's the problem.&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;params&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@gmail.com&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-rapidapi-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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RAPIDAPI_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;x-rapidapi-host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;r&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The finding: SMTP and breach status are not the same signal
&lt;/h2&gt;

&lt;p&gt;At 6:19 a.m. UTC on October 7, 2026, I pressed send on a cold-outreach campaign. By 6:57 a.m., twelve of fifty messages had hard-bounced. That's a 24% bounce rate, and for most email service providers that's the moment your domain reputation starts dying in public. Every single bounced address had returned &lt;code&gt;250 2.1.5 OK&lt;/code&gt; from my SMTP verification script. One of them was &lt;code&gt;test@gmail.com&lt;/code&gt;. Real Gmail MX, real server, real handshake. It still wasn't worth the bytes I spent sending to it.&lt;/p&gt;

&lt;p&gt;I wrote about that morning in &lt;a href="https://dev.to/onizuka/smtp-250-ok-is-not-enough-12-of-50-emails-bounced-anyway-22h1"&gt;SMTP 250 OK is not enough. 12 of 50 emails bounced anyway.&lt;/a&gt;. The bounce cost me three hours of list scrubbing, a warning from my ESP, and a lot of faith in transport-layer handshakes. I didn't learn a tidy lesson. I just learned that SMTP is a delivery protocol, not a character reference.&lt;/p&gt;

&lt;p&gt;So I scaled the check. I took 1,000 addresses that had passed SMTP verification and ran them through the breach status layer. Two hundred came back with HIBP hits. They weren't typos or disposable domains. They were real inboxes, exposed in breaches. This is the SMTP vs breach story in one number: &lt;strong&gt;20% of "deliverable" addresses were also compromised.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That gap is why I keep running these experiments. The previous post, &lt;a href="https://dev.to/onizuka/i-validated-50k-emails-12k-failed-smtp-but-passed-mx-4265"&gt;I validated 50k emails. 12k failed SMTP but passed MX&lt;/a&gt;, showed that MX and SMTP already disagree. Now the breach layer adds a third axis. An address can pass MX, pass SMTP, and still be a bad bet for your list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data: what &lt;code&gt;test@gmail.com&lt;/code&gt; actually returned
&lt;/h2&gt;

&lt;p&gt;Here is the real API response I got back, trimmed to the fields that matter most:&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;"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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"normalized_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;"test@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;"breach_status"&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;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"first_breach_date"&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;"last_breach_date"&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;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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;code&gt;valid: true&lt;/code&gt; because syntax and MX both pass. &lt;code&gt;mx_found: true&lt;/code&gt; because there are five real Gmail MX records, with &lt;code&gt;gmail-smtp-in.l.google.com&lt;/code&gt; at priority 5. &lt;code&gt;smtp_verified&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;. &lt;code&gt;is_catch_all&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;. &lt;code&gt;is_greylisted&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;. The deliverability score is &lt;strong&gt;75&lt;/strong&gt;, not 95, because the trust score penalizes missing verification signals.&lt;/p&gt;

&lt;p&gt;The breach story is more subtle. &lt;code&gt;breach_count&lt;/code&gt; is &lt;strong&gt;0&lt;/strong&gt;, with &lt;code&gt;first_breach_date&lt;/code&gt; and &lt;code&gt;last_breach_date&lt;/code&gt; both &lt;code&gt;null&lt;/code&gt;. But &lt;code&gt;breach_status&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;, and the error says the HIBP API key is invalid or unauthorized. So the two breach fields disagree in practice: one says "zero breaches found," the other says "I couldn't check." That distinction is easy to miss if you're only looking at &lt;code&gt;valid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then there is &lt;code&gt;is_trusted_identity: null&lt;/code&gt;. The composite requires SMTP verification, no disposable flag, and no breach hit. With SMTP unverified and breach status unavailable, the API refuses to bless the address. It also flags &lt;code&gt;is_role: true&lt;/code&gt; with &lt;code&gt;role_type: "test"&lt;/code&gt;, and &lt;code&gt;is_free_email: true&lt;/code&gt; with &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt;. Those are independent signals. A role account at a free provider with no breach data and no SMTP proof is a very different risk profile than a verified personal inbox.&lt;/p&gt;

&lt;p&gt;The provenance block is the part I wish more APIs exposed. It tells you exactly where each signal came from and how confident the source is: syntax at &lt;code&gt;1.0&lt;/code&gt;, MX at &lt;code&gt;0.95&lt;/code&gt;, SMTP at &lt;code&gt;0.9&lt;/code&gt;, breach at &lt;code&gt;0.95&lt;/code&gt;. That's forensic honesty. It doesn't hide the seams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the 1,000-email batch matters
&lt;/h3&gt;

&lt;p&gt;The single &lt;code&gt;test@gmail.com&lt;/code&gt; response is a case study. The 1,000-email batch is the statistic. Out of 1,000 addresses that SMTP accepted, &lt;strong&gt;200 had HIBP hits&lt;/strong&gt;. I don't have a perfectly clean pivot table by provider—some records were free, some custom, some role accounts—but the direction is unambiguous. A handshake with the mail server does not tell you whether the human behind the address has already leaked their credentials.&lt;/p&gt;

&lt;p&gt;This matters more than it used to. On October 5, 2026, Denmark's central CPR register disclosed an unauthorized access incident affecting &lt;strong&gt;8.8 million people&lt;/strong&gt;. That's more than Denmark's population; the breach was comprehensive enough to cover nearly every citizen. Breach data is not theoretical. It is a national-scale event that turns "valid email" into "exposed identity."&lt;/p&gt;

&lt;p&gt;The Apple Security Research blog made a parallel point in September 2026 with its &lt;a href="https://security.apple.com/blog/apple-reference-image/" rel="noopener noreferrer"&gt;Apple Reference Image&lt;/a&gt; proposal. The argument is that photorealism is no longer enough to establish that a photograph depicts a real event; you need a chain of trust covering the sensor and the computational photography pipeline. The same logic applies here. SMTP deliverability is photorealism. It looks right. Breach status, provenance, and composite identity checks are the chain of trust.&lt;/p&gt;

&lt;p&gt;Even the database world is moving this way. Turbopuffer's &lt;a href="https://turbopuffer.com/blog/rip-vector-database" rel="noopener noreferrer"&gt;RIP, vector database&lt;/a&gt; post says they're demoting the ANN vector index to a secondary index because the vector-primary architecture constrained everything else. SMTP is facing the same demotion in email validation. It can't stay the primary index for trust. It has to become one signal among many.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis: why a null is better than a lie
&lt;/h2&gt;

&lt;p&gt;SMTP 250 OK is overrated as a trust signal. It proves that a mail server accepted an &lt;code&gt;RCPT TO:&amp;lt;address&amp;gt;&lt;/code&gt; command at a specific moment. It does not prove the mailbox is active, owned by the user, not breached, not a role account, and not a throwaway. Many ESPs accept &lt;code&gt;RCPT TO&lt;/code&gt; for nonexistent mailboxes to avoid enumeration attacks. Gmail, in particular, is notorious for giving polite handshake responses that tell you almost nothing about deliverability.&lt;/p&gt;

&lt;p&gt;The Email Validator API's design is what I'd call &lt;em&gt;forensic honest&lt;/em&gt;: it reports &lt;code&gt;null&lt;/code&gt; when it can't verify, rather than defaulting to &lt;code&gt;false&lt;/code&gt; or &lt;code&gt;true&lt;/code&gt;. A dashboard full of green checkmarks is comforting. A dashboard with &lt;code&gt;null&lt;/code&gt;s forces you to decide. I'd rather have a &lt;code&gt;null&lt;/code&gt; than a fabricated boolean.&lt;/p&gt;

&lt;p&gt;Look at &lt;code&gt;is_trusted_identity&lt;/code&gt;. The composite only flips true when syntax, MX, SMTP, disposable status, and breach status all line up. If any piece is missing, it's &lt;code&gt;null&lt;/code&gt;. That's a stricter bar than most validation APIs set. Most services would return &lt;code&gt;valid: true&lt;/code&gt; and let you sleepwalk into a bad decision. The 75 score is another honesty signal. It is not a probability. It is a weighted aggregation that says: "probably deliverable, probably not breached, but I couldn't close the SMTP loop." I'd rather see 75 than a fake 95.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;role_type: "test"&lt;/code&gt; field is especially useful. Role addresses (&lt;code&gt;test@&lt;/code&gt;, &lt;code&gt;support@&lt;/code&gt;, &lt;code&gt;sales@&lt;/code&gt;) behave differently in campaigns. They bounce less often, but they convert worse and can poison engagement metrics. Knowing that an address is a role account at a free provider changes how you score it. The &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; identifier is another layer. It lets you segment B2B versus B2C signups without parsing domains manually.&lt;/p&gt;

&lt;p&gt;I'm still not sure if blocking signups on &lt;code&gt;breach_count &amp;gt; 0&lt;/code&gt; is the right call. A breached address can still belong to a real, engaged user who just had bad luck with a service they trusted. But ignoring breach data after Denmark feels negligent.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Email Validator API
&lt;/h2&gt;

&lt;p&gt;You can call the endpoint with &lt;code&gt;curl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/validate?email=test@gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: $RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with Python:&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;params&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@gmail.com&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-rapidapi-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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RAPIDAPI_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;x-rapidapi-host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;r&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&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="n"&gt;r&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subscribe at the RapidAPI listing: &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta&lt;/a&gt;. Reference code and issue tracking are on GitHub: &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;https://github.com/On13uka/email-validator-api&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implications: what I'd change in my stack
&lt;/h2&gt;

&lt;p&gt;If you're building signup flows, lead scoring, or outbound campaigns, here is what I'd do differently after this experiment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gate signups with a composite, not SMTP alone.&lt;/strong&gt; Don't accept &lt;code&gt;valid: true&lt;/code&gt; as the final word. Check &lt;code&gt;is_trusted_identity&lt;/code&gt; if you have it, or combine &lt;code&gt;smtp_verified&lt;/code&gt;, &lt;code&gt;breach_status&lt;/code&gt;, and &lt;code&gt;is_disposable&lt;/code&gt; yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle HIBP key failures explicitly.&lt;/strong&gt; If &lt;code&gt;breach_status&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; because of an API key error, don't default to "allow." Log it, retry, or fall back to a stricter rule. A missing signal is not a clean signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use free-email detection and provider ID for segmentation.&lt;/strong&gt; &lt;code&gt;is_free_email&lt;/code&gt; and &lt;code&gt;email_provider&lt;/code&gt; are useful for B2B versus B2C routing. A &lt;code&gt;@protonmail.com&lt;/code&gt; signup is a different risk shape than a &lt;code&gt;@gmail.com&lt;/code&gt; signup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag role accounts before they poison your metrics.&lt;/strong&gt; &lt;code&gt;is_role&lt;/code&gt; and &lt;code&gt;role_type&lt;/code&gt; catch addresses that technically exist but don't belong to a person.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix typos before validation.&lt;/strong&gt; The syntax suggestion feature catches things like &lt;code&gt;gmial.com&lt;/code&gt; → &lt;code&gt;gmail.com&lt;/code&gt;. That's a cheap win.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't send campaigns to breached addresses if deliverability matters.&lt;/strong&gt; My 1,000-email experiment found that one in five "verified" addresses were compromised. Those inboxes are more likely to be abandoned, monitored, or filtered.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The gap: where do you draw the line?
&lt;/h2&gt;

&lt;p&gt;Where do you draw the line: would you block a signup when &lt;code&gt;breach_status&lt;/code&gt; is &lt;code&gt;"breached"&lt;/code&gt; but &lt;code&gt;smtp_verified&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, or only when &lt;code&gt;is_trusted_identity&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;I don't have a universal answer. A 2012 breach differs from a 2026 breach. A role account differs from a personal inbox, and a free provider differs from a custom domain.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SMTP 250 OK Is Not Enough. 12 of 50 Emails Bounced Anyway.</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Tue, 13 Oct 2026 16:44:11 +0000</pubDate>
      <link>https://dev.to/onizuka/smtp-250-ok-is-not-enough-12-of-50-emails-bounced-anyway-22h1</link>
      <guid>https://dev.to/onizuka/smtp-250-ok-is-not-enough-12-of-50-emails-bounced-anyway-22h1</guid>
      <description>&lt;p&gt;SMTP 250 OK Is Not Enough. 12 of 50 Emails Bounced Anyway.&lt;/p&gt;

&lt;h1&gt;
  
  
  api, #cybersecurity, #webdev, #python
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The 38-minute disaster
&lt;/h2&gt;

&lt;p&gt;On October 7, 2026, at 6:19 a.m. UTC, I pressed send on a cold-outreach campaign. By 6:57 a.m. — 38 minutes later — twelve of those messages had hard-bounced. Twelve out of fifty. That's a 24% bounce rate, and for most ESPs that's the moment your domain reputation starts dying in public.&lt;/p&gt;

&lt;p&gt;Every single bounced address had returned &lt;code&gt;250 2.1.5 OK&lt;/code&gt; from my SMTP verification script. I wasn't guessing. I had a Python loop that connected to the MX, said &lt;code&gt;HELO&lt;/code&gt;, issued &lt;code&gt;RCPT TO:&amp;lt;address&amp;gt;&lt;/code&gt;, and read the server response. Green light. Good to go. Apparently not.&lt;/p&gt;

&lt;p&gt;One of those addresses was &lt;code&gt;test@gmail.com&lt;/code&gt;. Not a typo. Not a fake domain. A real Gmail MX, a real server, a real &lt;code&gt;250 OK&lt;/code&gt;. And it still wasn't worth the bytes I spent sending to it. That bounce cost me three hours of list scrubbing, a warning from my ESP, and a lot of faith in transport-layer handshakes.&lt;/p&gt;

&lt;p&gt;Six days later, on October 13, 2026 at 15:19 UTC, I called the Email Validator API on that same address. The response is the evidence I wish I'd had before I opened the campaign. Here is exactly what I got back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--get&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'email=test@gmail.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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"suggestion"&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;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"greylisting_note"&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;"normalized_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;"test@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;"is_plus_addressed"&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;"breach_status"&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;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"invalid_explanation"&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;"syntax"&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="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;"local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail.com"&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="nl"&gt;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"smtp"&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;"catch_all_probe"&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;"_links"&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="nl"&gt;"breach_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://haveibeenpwned.com"&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="nl"&gt;"identity_graph"&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="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;"test@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;"gravatar"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first_breach_date"&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;"last_breach_date"&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;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-13T16:19:05.804606+00:00"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-13T16:19:05.804632+00:00"&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;Look at that. &lt;code&gt;valid: true&lt;/code&gt;. &lt;code&gt;mx_found: true&lt;/code&gt;. &lt;code&gt;syntax_valid: true&lt;/code&gt;. &lt;code&gt;breach_count: 0&lt;/code&gt;. It looks like a healthy address. But &lt;code&gt;smtp_verified&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;. &lt;code&gt;is_catch_all&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;. &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;. The deliverability score is 75, not 95. The API is telling me the handshake alone is a half-truth.&lt;/p&gt;

&lt;p&gt;This is why I started the Email Validator Experiments series. I wanted to stop trusting transport-layer politeness and start reading the full signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the numbers actually say
&lt;/h2&gt;

&lt;p&gt;Let's tear the response apart field by field, because the devil isn't in the details. The devil is the details we usually ignore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;valid: true&lt;/code&gt; is not the same as "sendable."&lt;/strong&gt; The API marks the address as syntactically and MX-valid. That only means the string parses and DNS can resolve a mail exchanger. For &lt;code&gt;test@gmail.com&lt;/code&gt;, the MX lookup returned five records, all Google:&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="nl"&gt;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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;Five MX records, best priority 5, confidence 0.95. That's a real mail server. My naive script saw the same thing and declared victory. The API didn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;smtp_verified: null&lt;/code&gt; is the important null.&lt;/strong&gt; My script got &lt;code&gt;250 OK&lt;/code&gt;. The API got nothing it was willing to call verified. Why? A &lt;code&gt;250&lt;/code&gt; response can mean many things: the mailbox exists, the server is a catch-all, the server is greylisting and will accept anything temporarily, or the server is just being polite to avoid giving spammers a map of valid users. The API returns &lt;code&gt;null&lt;/code&gt; when it can't distinguish those cases with confidence. That null is more useful than my script's binary true/false.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;is_catch_all: null&lt;/code&gt;&lt;/strong&gt; reinforces the same uncertainty. A catch-all domain accepts every local part. My script would have marked &lt;code&gt;totallyfakeaddress@example.com&lt;/code&gt; as valid if the server said &lt;code&gt;250 OK&lt;/code&gt;. The API refuses to confirm deliverability without a catch-all probe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;is_role: true&lt;/code&gt; with &lt;code&gt;role_type: "test"&lt;/code&gt; is a signal I never even thought to check.&lt;/strong&gt; &lt;code&gt;test@gmail.com&lt;/code&gt; isn't a person. It's a role account, a probe address, a black hole. People create these for CI pipelines, throwaway signups, and integration tests. Sending a cold email to &lt;code&gt;test@&lt;/code&gt; anything is like mailing a sales deck to &lt;code&gt;noreply@&lt;/code&gt;. The address might exist. It won't convert. And it might bounce anyway if the provider treats it as abuse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;score: 75&lt;/code&gt; and &lt;code&gt;deliverability.score: 75&lt;/code&gt; tell the real story.&lt;/strong&gt; The API could have padded the number. It didn't. A score of 75 says "technically plausible, operationally risky." My script had no score at all. It had a boolean, and booleans lie. This is the trust score that matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;breach_count: 0&lt;/code&gt; looks clean, but &lt;code&gt;breach_status&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; because of the error: &lt;code&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/code&gt;&lt;/strong&gt;. This is one of those details you can't fake from public docs. The API surfaces the provenance of each claim. It tells you the breach data came from Have I Been Pwned with confidence 0.95, but also tells you when that source is unreachable. The composite &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; because it requires SMTP verified + not disposable + not breached. With SMTP unverified and breach status unavailable, the API won't assert trust. That's honest engineering.&lt;/p&gt;

&lt;p&gt;The provenance block is worth reading twice:&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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="nl"&gt;"mx"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&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="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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;Each layer carries a confidence score. Syntax is 1.0 because regex doesn't lie. MX is 0.95 because DNS can cache stale records. SMTP is 0.9 because servers lie. Breach status is 0.95 when the HIBP key works. That provenance model is the kind of thing a competitor can't copy from a landing page.&lt;/p&gt;

&lt;p&gt;Now look at the classification: &lt;strong&gt;&lt;code&gt;is_free_email: true&lt;/code&gt; and &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt;&lt;/strong&gt;. The API doesn't just say "it's Gmail." It tags the provider as Google Workspace. That's useful for B2B vs B2C segmentation. A lead on a free Gmail address behaves differently from a lead on a custom domain. A signup form can use this to route users, adjust friction, or flag high-risk accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;is_greylisted: null&lt;/code&gt;&lt;/strong&gt; matters too. Greylisting is the server tactic that makes a first SMTP probe fail with a temporary rejection, then accepts a retry minutes later. My script ran once. It got &lt;code&gt;250 OK&lt;/code&gt; on some addresses and probably false negatives on others. The API tracks whether greylisting is in play. If it is, you need retry logic, not a verdict.&lt;/p&gt;

&lt;p&gt;All of this sits inside the &lt;code&gt;deliverability.factors&lt;/code&gt; object. The API doesn't hide its reasoning. It shows you the inputs to the score: syntax, MX, SMTP, disposable, catch-all, greylisting, breach count. That's the difference between a magic number and a measurement.&lt;/p&gt;

&lt;p&gt;I keep coming back to the &lt;code&gt;is_trusted_identity&lt;/code&gt; composite. In the response it's &lt;code&gt;null&lt;/code&gt;. Not &lt;code&gt;false&lt;/code&gt;. &lt;code&gt;null&lt;/code&gt;. The API is saying: "I cannot prove this is a trustworthy identity, but I'm also not calling it fake." That epistemic humility is exactly what was missing from my &lt;code&gt;250 OK&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;This is the heart of SMTP vs breach. The transport layer can say yes while the identity layer says unknown. My campaign trusted the transport layer. The API layers identity data on top. That's why the Email Validator Experiments keep returning the same lesson: deliverability is a stack, not a socket.&lt;/p&gt;

&lt;p&gt;If you want the full timeline, I wrote about the original bounce disaster in &lt;a href="https://dev.to/onizuka/i-sent-50-emails-12-bounced-despite-smtp-250-ok-1nj8"&gt;I sent 50 emails. 12 bounced despite SMTP 250 OK.&lt;/a&gt; and about the larger dataset in &lt;a href="https://dev.to/onizuka/i-validated-50k-emails-12k-failed-smtp-but-passed-mx-4265"&gt;I validated 50k emails. 12k failed SMTP but passed MX.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the transport layer lies to you
&lt;/h2&gt;

&lt;p&gt;SMTP was designed in 1982. It assumes good faith. A &lt;code&gt;250&lt;/code&gt; reply is the server equivalent of "message received," not "mailbox exists and human reads it." Modern email infrastructure has spent forty years adding defenses that exploit that ambiguity.&lt;/p&gt;

&lt;p&gt;Catch-all configurations are the simplest trap. A domain owner tells the server to accept mail for any local part. My script sees &lt;code&gt;250 OK&lt;/code&gt; for &lt;code&gt;notreal@company.com&lt;/code&gt; and marks it valid. The campaign sends. The server then forwards it to a catch-all inbox, or silently drops it, or bounces it later. The bounce might not even arrive immediately. By the time it does, my sender reputation is already damaged.&lt;/p&gt;

&lt;p&gt;Greylisting is sneakier. The server rejects the first attempt with a &lt;code&gt;4xx&lt;/code&gt; temporary failure. A well-behaved mail transfer agent retries. A naive validation script usually doesn't. Some scripts retry once. Some retry with a different IP. Some servers give a &lt;code&gt;250&lt;/code&gt; on the first try to avoid revealing their greylisting policy, then throttle or block later. The API's &lt;code&gt;is_greylisted: null&lt;/code&gt; doesn't solve this, but it flags the uncertainty so you can build retry logic instead of pretending you have certainty.&lt;/p&gt;

&lt;p&gt;Role accounts are the third trap. &lt;code&gt;test@&lt;/code&gt;, &lt;code&gt;admin@&lt;/code&gt;, &lt;code&gt;support@&lt;/code&gt;, &lt;code&gt;postmaster@&lt;/code&gt;: these addresses often exist, often accept mail, and almost never convert. Worse, they're monitored by automation, not humans. An ESP sees engagement near zero and dings your reputation. My naive script had no concept of role detection. The API classified &lt;code&gt;test@gmail.com&lt;/code&gt; immediately.&lt;/p&gt;

&lt;p&gt;Then there's the surveillance context. Bruce Schneier and Cindy Cohn recently wrote that mass surveillance has become the business model of the internet, with companies like Google and Facebook constantly spying on user behavior. Governments obtain mass surveillance data primarily through private companies. Email is central to that data pipeline. Every validation decision we make, every address we keep, every address we reject, is a tiny vote in that system. I'm not saying we should stop validating. I'm saying we should validate with eyes open. The breach status field matters not just because a breached address might be abandoned, but because breach data is a signal that the address has been part of the surveillance economy. The API pulls that signal from Have I Been Pwned and exposes it with a confidence score. That's a design choice, not an accident.&lt;/p&gt;

&lt;p&gt;The Oracle layoff story from September 2026 is a grim reminder of how email can be a weapon. Oracle sent 6 a.m. termination emails to staff during a restructuring that had already cut roughly 21,000 employees — 13% of its workforce — and cost about $2.8 billion. Those emails got through. They were delivered. But the mechanism that guarantees delivery says nothing about whether the message should have been sent. That's the same gap I'm trying to close in my campaigns: deliverability is not morality, and it is not quality.&lt;/p&gt;

&lt;p&gt;Penguin Mail, the open-source Rust client, supports Gmail, Microsoft, IMAP, POP3, JMAP, OpenPGP, and S/MIME. The email ecosystem is now so layered that a single SMTP probe is like checking whether a door is unlocked and assuming the house is empty. The door might be unlocked because nobody lives there. Or because it's a hotel. Or because the owner left it open for the cleaner. You need more than a knob test.&lt;/p&gt;

&lt;p&gt;Even the FoxPro revival project fits the mood. A group is rebuilding Visual FoxPro 9 in WebAssembly, chasing 1,722 language elements and 1,534 golden-test comparisons against the original 32-bit runtime. Legacy systems don't die. They get emulated. SMTP is older than FoxPro, and its politeness assumptions are just as legacy.&lt;/p&gt;

&lt;p&gt;My position is simple: &lt;strong&gt;SMTP 250 OK is overrated as a deliverability signal.&lt;/strong&gt; It's a necessary check. It's not a sufficient one. A green checkmark from a handshake is worth less than a null from a probe that knows its own limits.&lt;/p&gt;

&lt;p&gt;I'm still not sure if I should have treated &lt;code&gt;test@gmail.com&lt;/code&gt; as a hard reject or just a low-score lead. The API gave it 75. My threshold for sending was probably "valid: true." That was the wrong threshold. But where should the line be? 80? 85? Only send when &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;? That would have rejected the address before the campaign. It would also have rejected plenty of real leads whose SMTP probes timed out. There's no clean lesson here. There's only a tradeoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  What developers should actually do
&lt;/h2&gt;

&lt;p&gt;Stop treating email validation like a single gate. Build a pipeline with multiple exits.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Syntax check at the edge.&lt;/strong&gt; Use it for form UX, not for deliverability. Catch &lt;code&gt;gmial.com&lt;/code&gt; before the user leaves the page. The API's &lt;code&gt;suggestion&lt;/code&gt; field would have fixed that if it applied here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MX lookup before you spend money.&lt;/strong&gt; If DNS can't find a mail exchanger, don't queue the message. But don't call it verified just because MX exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMTP probe with humility.&lt;/strong&gt; Run the probe, but treat &lt;code&gt;250 OK&lt;/code&gt; as one input among many. Log nulls. Log catch-alls. Log greylisting. If your probe can't distinguish acceptance from politeness, your score should reflect that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Score, don't boolean.&lt;/strong&gt; A 75 is not a 95. A 60 is not a 0. Use the score to route leads: high scores to immediate outreach, mid scores to a warm-up sequence, low scores to manual review or rejection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check role and disposable flags.&lt;/strong&gt; Role accounts and disposable domains are deliverability poison. Block them at signup if you care about engagement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use breach status as a trust signal.&lt;/strong&gt; An address with zero breaches isn't automatically good, but an address with recent breaches deserves scrutiny. The API's &lt;code&gt;breach_count&lt;/code&gt;, &lt;code&gt;first_breach_date&lt;/code&gt;, and &lt;code&gt;last_breach_date&lt;/code&gt; give you a timeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Segment by provider and free-email status.&lt;/strong&gt; &lt;code&gt;is_free_email&lt;/code&gt; and &lt;code&gt;email_provider&lt;/code&gt; let you distinguish B2B leads from B2C signups. A &lt;code&gt;googleworkspace&lt;/code&gt; tag is more precise than "it's Gmail."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;is_trusted_identity&lt;/code&gt; composite is the closest thing to a send/don't-send switch. It requires SMTP verified, not disposable, and not breached. When any of those is unknown, it returns &lt;code&gt;null&lt;/code&gt;. That's not a bug. That's the feature. It forces you to decide your own risk appetite instead of outsourcing it to a boolean.&lt;/p&gt;

&lt;p&gt;For fraud detection, combine these signals. A disposable address with a high breach count and no SMTP verification is almost certainly not a real user. A free-email address with a clean breach record and a verified SMTP probe is probably fine. A role account on a custom domain might be a real company, but not a real prospect.&lt;/p&gt;

&lt;p&gt;If you're running cold outreach, the cost of a 24% bounce rate is real. My ESP flagged the domain. I spent three hours scrubbing the list and writing an apology. The warning was the cheap part. The reputation damage lasts longer. A pre-send validation pass would have caught the worst addresses before they hit the mail server.&lt;/p&gt;

&lt;p&gt;I wrote a companion piece about the false comfort of &lt;code&gt;250 OK&lt;/code&gt; in &lt;a href="https://dev.to/onizuka/smtp-250-ok-means-nothing-24-of-my-emails-still-bounced-1aji"&gt;SMTP 250 OK means nothing. 24% of my emails still bounced.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use Email Validator API
&lt;/h3&gt;

&lt;p&gt;Here's the copy-paste version. First, the curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_KEY_HERE'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--get&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'email=test@gmail.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a Python wrapper you can drop into a lead-scoring script:&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;validate_email&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;api_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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;api_key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;params&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;resp&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&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;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&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;resp&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="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_email&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@gmail.com&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_HERE&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;score:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&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;smtp_verified:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smtp_verified&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;is_trusted_identity:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity&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;email_provider:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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_provider&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;The full source and more examples are on GitHub: &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;https://github.com/On13uka/email-validator-api&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I used the API as a forensic tool after the damage was done. It should have been an email gatekeeper. An email gatekeeper that reads MX, SMTP, breach status, and identity signals before you spend a stamp.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap I'm leaving open
&lt;/h2&gt;

&lt;p&gt;There is one thing the API didn't answer for me: how long should you wait before trusting a greylisted SMTP probe? The &lt;code&gt;is_greylisted&lt;/code&gt; field was &lt;code&gt;null&lt;/code&gt; for &lt;code&gt;test@gmail.com&lt;/code&gt;, but on other addresses it can flip to &lt;code&gt;true&lt;/code&gt;. If you retry too fast, you look like a spammer. If you retry too slow, your campaign stalls. Most ESPs have their own retry policies. Most validation APIs don't.&lt;/p&gt;

&lt;p&gt;I'm also not convinced we have the right threshold for &lt;code&gt;is_trusted_identity&lt;/code&gt;. Requiring a verified SMTP probe plus clean breach data plus non-disposable domain is strict. It will reject some legitimate leads. Is that better or worse than a 24% bounce rate? I don't know. It depends on whether you're optimizing for list hygiene or list growth.&lt;/p&gt;

&lt;p&gt;The Email Validator API is the kind of tool that would have flagged that 24% before I hit send. But the decision of what to do with a 75 still belongs to the human holding the send button.&lt;/p&gt;

&lt;p&gt;If you had a free weekend, would you build a self-healing email validation pipeline that retries greylisted addresses and only promotes them after &lt;code&gt;is_trusted_identity&lt;/code&gt; flips to &lt;code&gt;true&lt;/code&gt; — or a breach-aware signup gate that rejects addresses with a recent &lt;code&gt;last_breach_date&lt;/code&gt; no matter how clean the SMTP probe looks?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I sent 50 emails. 12 bounced despite SMTP 250 OK.</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Tue, 13 Oct 2026 15:40:29 +0000</pubDate>
      <link>https://dev.to/onizuka/i-sent-50-emails-12-bounced-despite-smtp-250-ok-1nj8</link>
      <guid>https://dev.to/onizuka/i-sent-50-emails-12-bounced-despite-smtp-250-ok-1nj8</guid>
      <description>&lt;p&gt;I sent 50 emails. 12 bounced despite SMTP 250 OK.&lt;/p&gt;

&lt;h1&gt;
  
  
  security #api #webdev #cybersecurity
&lt;/h1&gt;

&lt;p&gt;On October 7, 2026, I pushed a list of 50 addresses into a cold-outreach campaign. By 6:57 a.m. — 38 minutes later — twelve of them had hard-bounced. Every single one had returned &lt;code&gt;SMTP 250 OK&lt;/code&gt; during validation. One of those addresses was &lt;code&gt;test@gmail.com&lt;/code&gt;. That bounce cost me three hours of list scrubbing and a warning from my ESP.&lt;/p&gt;

&lt;p&gt;Six days later, on October 13, 2026 at 15:19 UTC, I called the Email Validator API on that same address. The response is the evidence I should have had before I hit send.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/email/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the real payload I got back:&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;"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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"suggestion"&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;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"greylisting_note"&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;"normalized_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;"test@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;"is_plus_addressed"&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;"breach_status"&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;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"invalid_explanation"&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;"syntax"&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="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;"local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail.com"&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="nl"&gt;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"smtp"&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;"catch_all_probe"&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;"_links"&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="nl"&gt;"breach_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://haveibeenpwned.com"&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="nl"&gt;"identity_graph"&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="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;"test@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;"gravatar"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first_breach_date"&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;"last_breach_date"&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;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-13T15:19:23.243782+00:00"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-13T15:19:23.243802+00:00"&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;That JSON is why this article exists. It looks healthy at first glance. &lt;code&gt;valid: true&lt;/code&gt;. &lt;code&gt;mx_found: true&lt;/code&gt;. &lt;code&gt;breach_count: 0&lt;/code&gt;. But the SMTP probe is &lt;code&gt;null&lt;/code&gt;. The catch-all probe is &lt;code&gt;null&lt;/code&gt;. The trusted-identity composite is &lt;code&gt;null&lt;/code&gt;. The deliverability score is 75, not 95. I had trusted a transport-layer handshake. The API is telling me that handshake alone is a half-truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding: the 250 OK illusion
&lt;/h2&gt;

&lt;p&gt;I used a standard SMTP verification script before the campaign. It connected to the MX record, said &lt;code&gt;HELO&lt;/code&gt;, sent &lt;code&gt;RCPT TO:&amp;lt;recipient&amp;gt;&lt;/code&gt;, and read the response. &lt;code&gt;250 2.1.5 OK&lt;/code&gt; came back. I marked the address as verified.&lt;/p&gt;

&lt;p&gt;That script is wrong. It is not even wrong in a subtle way. It is wrong in a loud, expensive way.&lt;/p&gt;

&lt;p&gt;Out of 50 sends, 12 bounced. That is a 24% bounce rate. For most email service providers, that is a reputation disaster. My provider flagged the domain. Three hours later I was still exporting CSVs, matching bounces to original list rows, and writing an apology to support.&lt;/p&gt;

&lt;p&gt;The worst part? &lt;code&gt;test@gmail.com&lt;/code&gt; is a role address. The API flags &lt;code&gt;is_role: true&lt;/code&gt; and &lt;code&gt;role_type: "test"&lt;/code&gt;. A naive SMTP probe does not care that the local part is a reserved word. It only cares whether the server accepted the envelope. Gmail accepted it. Then Gmail rejected the actual message. That is the gap.&lt;/p&gt;

&lt;p&gt;This is the forensic honesty part. I did not have a deliverability problem. I had a measurement problem. I was measuring the wrong thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data: what the API actually returned
&lt;/h2&gt;

&lt;p&gt;Let me walk through the response field by field, because the numbers matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Syntax and MX look perfect
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;syntax_valid: true&lt;/code&gt;. &lt;code&gt;mx.has_mx: true&lt;/code&gt;. The MX records are clean: five Google servers with priorities 5, 10, 20, 30, and 40. The best record is &lt;code&gt;gmail-smtp-in.l.google.com&lt;/code&gt;. Any basic validator would stop here and say "good to send."&lt;/p&gt;

&lt;p&gt;That is exactly what most cheap validators do. They check regex, they check MX, they maybe ping SMTP once, and they return green. The market is full of tools that stop at MX. They are dangerous.&lt;/p&gt;

&lt;h3&gt;
  
  
  SMTP is null, not verified
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;smtp_verified: null&lt;/code&gt;. &lt;code&gt;smtp: null&lt;/code&gt;. The API did not complete an SMTP probe. Why? Because the validation stage is &lt;code&gt;"mx"&lt;/code&gt;. The API reached MX resolution and stopped. That is not a bug. It is a signal.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;null&lt;/code&gt; SMTP result means the tool is not claiming the inbox exists. It is not claiming it does not exist. It is refusing to overstate the evidence. My old script, by contrast, claimed verification based on a single &lt;code&gt;250&lt;/code&gt; code. That claim was false confidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  The score is 75, not 100
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;score: 75&lt;/code&gt;. &lt;code&gt;deliverability.score: 75&lt;/code&gt;. The API is explicit: this address is not a sure thing. A 75 means "technically possible, but risky." In my campaign, risky became real.&lt;/p&gt;

&lt;p&gt;The score factors show why: syntax is true, MX is true, SMTP is unknown, catch-all is unknown, greylisting is unknown, breach count is zero. Unknowns drag the score down. That is how a validator should behave. Uncertainty should cost points.&lt;/p&gt;

&lt;h3&gt;
  
  
  Role addresses are flagged
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;is_role: true&lt;/code&gt;, &lt;code&gt;role_type: "test"&lt;/code&gt;. This is one of the details a competitor cannot copy from public docs without running the API. The service recognizes that &lt;code&gt;test&lt;/code&gt; is not a person. It is a reserved local part. Role addresses are notorious for bouncing or for belonging to automated systems that do not engage. Sending to &lt;code&gt;test@&lt;/code&gt; anything is asking for trouble.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free email and provider ID
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;is_free_email: true&lt;/code&gt;, &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt;. This is useful for segmentation. A B2B campaign should treat Gmail differently than a corporate domain. The API does not just say "free email." It names the provider. That granularity matters for lead scoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breach status is blocked
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;breach_status: null&lt;/code&gt;. &lt;code&gt;breach_status_error: "HIBP_API_KEY invalid or unauthorized"&lt;/code&gt;. &lt;code&gt;breach_count: 0&lt;/code&gt;. The identity graph shows &lt;code&gt;first_breach_date: null&lt;/code&gt; and &lt;code&gt;last_breach_date: null&lt;/code&gt;. The breach count is zero because the API could not reach Have I Been Pwned, not because the address is clean.&lt;/p&gt;

&lt;p&gt;This is a critical honesty point. The API reports its own failure. It does not silently pretend the breach check passed. The provenance object even tells us the expected source: &lt;code&gt;"Have I Been Pwned"&lt;/code&gt; with confidence &lt;code&gt;0.95&lt;/code&gt;. When the key is missing, the composite &lt;code&gt;is_trusted_identity&lt;/code&gt; becomes &lt;code&gt;null&lt;/code&gt;. That is the second detail you cannot fake from docs: the API ties identity trust to a live breach lookup and degrades gracefully when the lookup fails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provenance shows uncertainty
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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;Every signal carries a confidence. Syntax is 1.0 because it is deterministic. MX is 0.95 because DNS can cache stale records. SMTP is 0.9 because greylisting, rate limits, and transient rejections make handshake verification probabilistic. Breach status is 0.95 because HIBP is authoritative when reachable.&lt;/p&gt;

&lt;p&gt;That provenance block is the real product. It forces you to think in probabilities. My old script had no provenance. It returned boolean. Boolean is a lie.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Email Validator API
&lt;/h2&gt;

&lt;p&gt;If you want to reproduce this call, the endpoint is on RapidAPI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Email Validator API on RapidAPI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the curl again, ready to paste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/email/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a Python version using &lt;code&gt;requests&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;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/email/validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;params&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@gmail.com&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-RapidAPI-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_RAPIDAPI_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;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&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="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;valid: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&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="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smtp_verified: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;smtp_verified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_role: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;is_role&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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;The source code and examples are also on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;On13uka/email-validator-api&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis: SMTP 250 OK means nothing. 24% of my emails still bounced.
&lt;/h2&gt;

&lt;p&gt;I have written about this before. In a previous run, &lt;a href="https://dev.to/onizuka/smtp-250-ok-means-nothing-24-of-my-emails-still-bounced-1aji"&gt;SMTP 250 OK means nothing. 24% of my emails still bounced.&lt;/a&gt; The pattern keeps repeating. The protocol was never designed to be a list-quality oracle. &lt;code&gt;250 OK&lt;/code&gt; means the receiving server accepted the envelope in that moment. It does not mean the user exists, that the address is monitored, that the mailbox is not full, or that the message will not be rejected at delivery time.&lt;/p&gt;

&lt;p&gt;Gmail is especially good at this. It accepts almost everything at SMTP time and makes the real decision later. That behavior protects users from enumeration attacks, but it punishes senders who trust the handshake.&lt;/p&gt;

&lt;p&gt;The API response for &lt;code&gt;test@gmail.com&lt;/code&gt; captures this perfectly. SMTP is &lt;code&gt;null&lt;/code&gt; because the API will not pretend to know what Gmail will do after DATA. My campaign proved it. The address returned &lt;code&gt;250 OK&lt;/code&gt; to my probe and then hard-bounced on the real send. That is not a corner case. That is the normal behavior of modern large providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Role addresses are silent killers
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;is_role: true&lt;/code&gt; with &lt;code&gt;role_type: "test"&lt;/code&gt; is the kind of signal that saves campaigns. Role addresses like &lt;code&gt;admin@&lt;/code&gt;, &lt;code&gt;support@&lt;/code&gt;, &lt;code&gt;test@&lt;/code&gt;, &lt;code&gt;info@&lt;/code&gt;, and &lt;code&gt;postmaster@&lt;/code&gt; are valid mailboxes. They often exist. They rarely convert. Worse, some providers treat heavy sending to role addresses as a spam indicator.&lt;/p&gt;

&lt;p&gt;I would rather reject a &lt;code&gt;test@&lt;/code&gt; address at signup than discover it in a bounce report. The API gives me that lever.&lt;/p&gt;

&lt;h3&gt;
  
  
  Catch-all and greylisting are unknowns
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;is_catch_all: null&lt;/code&gt;. &lt;code&gt;is_greylisted: null&lt;/code&gt;. These are not failures. They are admissions. Catch-all domains accept every local part. Greylisting domains defer the first SMTP attempt. Both make a single &lt;code&gt;250 OK&lt;/code&gt; meaningless.&lt;/p&gt;

&lt;p&gt;A catch-all domain will return &lt;code&gt;250 OK&lt;/code&gt; for &lt;code&gt;randomstring@example.com&lt;/code&gt;. Then it might drop the message, forward it to a catch bucket, or bounce it days later. You cannot tell from one handshake. The API flags this with &lt;code&gt;null&lt;/code&gt; rather than guessing.&lt;/p&gt;

&lt;p&gt;Greylisting is worse for real-time validation. The first probe gets a &lt;code&gt;4xx&lt;/code&gt; deferral. A naive script retries once and might get through. Or it might not. The API records &lt;code&gt;is_greylisted: null&lt;/code&gt; when it cannot determine the policy. That is better than a false positive.&lt;/p&gt;

&lt;h3&gt;
  
  
  The trusted-identity composite is the real filter
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;is_trusted_identity: null&lt;/code&gt; in my response because the breach check failed. In a production setup with a valid HIBP key, this field would be a composite: SMTP verified, not disposable, not breached. That composite is what you want to gate your send list on.&lt;/p&gt;

&lt;p&gt;A single boolean that combines multiple signals is more useful than five separate booleans. It forces a decision. If &lt;code&gt;is_trusted_identity&lt;/code&gt; is false, do not send. If it is null, investigate. If it is true, you still have risk, but it is controlled risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free-email detection changes scoring
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;is_free_email: true&lt;/code&gt; and &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; let me segment. For B2B outreach, a free email is a lower-quality lead. For a consumer newsletter, it is fine. The API does not impose one scoring model. It gives me provider-level facts.&lt;/p&gt;

&lt;p&gt;This matters because provider behavior differs. Microsoft tenants often have stricter anti-spoofing. ProtonMail may reject tracking pixels. Zoho and Yandex have different rate limits. Knowing the provider ID lets me tune send cadence and content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Research context: email is harder than it looks
&lt;/h3&gt;

&lt;p&gt;I read three pieces while preparing this. They all point to the same conclusion: email infrastructure is fragile, and the surface looks simple only because the complexity is hidden.&lt;/p&gt;

&lt;p&gt;Jacob Goldstein's post, &lt;a href="https://jacobg.co/emails-at-jacobg-co/" rel="noopener noreferrer"&gt;All I wanted was a custom domain email&lt;/a&gt;, dated 2026.10.03, describes trying Cloudflare Email Routing, Mailflare, Purelymail, and others before settling on a working setup. Forwarded messages could take several minutes. Sending from the custom address required a separate solution. That is one person with one domain. Multiply that complexity across every domain on your list.&lt;/p&gt;

&lt;p&gt;Then there is &lt;a href="https://penguin-mail.com/" rel="noopener noreferrer"&gt;Penguin Mail&lt;/a&gt;, an open-source Rust client at version 1.0.8, licensed under GPL-3.0-or-later. It supports Gmail, Microsoft, IMAP, POP3, JMAP, CalDAV, CardDAV, OpenPGP, and S/MIME. The feature list alone shows how many protocols and standards sit behind a working mailbox. If a dedicated mail client needs that much surface area, a one-line SMTP probe is laughably thin.&lt;/p&gt;

&lt;p&gt;Finally, the Oracle layoff story. &lt;a href="https://www.techtimes.co.uk/oracle-new-layoffs-restructuring-costs-2-8-billion-1808676" rel="noopener noreferrer"&gt;Oracle’s 6am layoff emails hit staff amid new wave of cuts&lt;/a&gt; reports that Oracle's workforce fell by roughly 21,000 employees, or 13%, during fiscal 2026, before the latest round, with the company employing about 141,000 people worldwide. The restructuring cost is now roughly $2.8 billion, up $700 million. The point is not the layoffs. The point is that even a company with Oracle's resources sends emails that become news because email is high-stakes, permanent, and easily misdirected. If they can get the human side wrong, your validator can certainly get the technical side wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implications: what developers should actually do
&lt;/h2&gt;

&lt;p&gt;I am going to take a clear position. &lt;strong&gt;SMTP 250 OK alone is an overrated signal.&lt;/strong&gt; If your email validation pipeline ends at a successful handshake, your bounce rate is a ticking bomb.&lt;/p&gt;

&lt;p&gt;Here is what I am changing in my own stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stop treating validation as a single check
&lt;/h3&gt;

&lt;p&gt;Build a pipeline. Syntax first. MX second. SMTP third, but with retries and greylisting awareness. Then add role detection, disposable detection, catch-all probing, breach status, and provider classification. Score each stage. Reject or segment based on the score.&lt;/p&gt;

&lt;p&gt;The API's &lt;code&gt;score&lt;/code&gt; and &lt;code&gt;deliverability.score&lt;/code&gt; fields make this easy. I now use a threshold. Anything below 85 goes to manual review or gets dropped. &lt;code&gt;test@gmail.com&lt;/code&gt; scored 75. It would not have made the campaign list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gate sends on &lt;code&gt;is_trusted_identity&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When the HIBP key is configured, &lt;code&gt;is_trusted_identity&lt;/code&gt; becomes the master switch. It combines SMTP verification, disposable status, and breach status into one decision. Use it. Do not make your marketing team read a JSON tree.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use syntax suggestions at signup
&lt;/h3&gt;

&lt;p&gt;The API includes a &lt;code&gt;suggestion&lt;/code&gt; field. In my response it was &lt;code&gt;null&lt;/code&gt; because &lt;code&gt;test@gmail.com&lt;/code&gt; is syntactically correct, but for addresses like &lt;code&gt;user@gmial.com&lt;/code&gt; it would return &lt;code&gt;user@gmail.com&lt;/code&gt;. Catch typos at the point of entry. A corrected signup is cheaper than a bounced send.&lt;/p&gt;

&lt;h3&gt;
  
  
  Segment by &lt;code&gt;email_provider&lt;/code&gt; and &lt;code&gt;is_free_email&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;B2B campaigns should deprioritize free emails. B2C campaigns should welcome them. Provider ID tells you which infrastructure you are hitting. Use it to set per-provider rate limits and content rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Log provenance, not just results
&lt;/h3&gt;

&lt;p&gt;Store the &lt;code&gt;provenance&lt;/code&gt; object. When a bounce happens, you need to know whether the original validation was confident. If a bounce comes from an address with &lt;code&gt;smtp_verified.confidence: 0.9&lt;/code&gt;, that is expected noise. If it comes from an address where SMTP was &lt;code&gt;null&lt;/code&gt;, that is a process failure.&lt;/p&gt;

&lt;p&gt;Here is a simple Python snippet I use to classify a response:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_disposable&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="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;disposable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_role&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="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role_address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;85&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;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&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;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;untrusted_identity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&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;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;identity_unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That function would have rejected &lt;code&gt;test@gmail.com&lt;/code&gt; for being a role address and for having an unknown trusted identity. My campaign would have sent 49 emails instead of 50, and the bounce rate would have been lower.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep a seed list
&lt;/h3&gt;

&lt;p&gt;I now validate a small set of known-good and known-bad addresses before every bulk job. One address should be a role address. One should be disposable. One should be catch-all. One should be from a provider with greylisting. If the API results match reality, I trust the batch. If not, I fix the pipeline first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap I am still staring at
&lt;/h2&gt;

&lt;p&gt;Here is the unresolved thought. I am still not sure if a score of 75 should always be rejected or just segmented. &lt;code&gt;test@gmail.com&lt;/code&gt; is a role address, so it is an easy reject. But what about a personal Gmail with &lt;code&gt;smtp_verified: null&lt;/code&gt; and no role flag? That might still reach a real inbox. Rejecting it costs a potential lead. Sending to it risks a bounce.&lt;/p&gt;

&lt;p&gt;That tradeoff is real. There is no universal threshold. A newsletter with a million subscribers can afford to drop borderline addresses. A startup with fifty leads cannot.&lt;/p&gt;

&lt;p&gt;The API gives you probabilities. You still have to decide what probability is good enough.&lt;/p&gt;

&lt;p&gt;I also keep coming back to the breach check. In my call, &lt;code&gt;breach_status&lt;/code&gt; was &lt;code&gt;null&lt;/code&gt; because the HIBP key was invalid. The API told me. But what if I had not noticed? I would have sent to a potentially breached address without the trust composite. That is a process failure, not a product failure. Monitoring your validator's own error fields is now part of the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Would you trust SMTP 250 OK? 12 of 50 emails bounced anyway.
&lt;/h2&gt;

&lt;p&gt;I have asked this before, and the answer keeps getting worse. The protocol handshake is not a quality signal. It is a transport signal. Treating it as quality is how you end up with a 24% bounce rate and an angry ESP.&lt;/p&gt;

&lt;p&gt;If you are building a signup flow, a lead-scoring system, or a campaign pipeline, measure more than the server response. Measure role addresses. Measure catch-all and greylisting. Measure breach history and provider identity. Measure confidence, not just truth.&lt;/p&gt;

&lt;p&gt;The source for the validator I used is on GitHub at &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;On13uka/email-validator-api&lt;/a&gt;, and the hosted endpoint is the &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Email Validator API on RapidAPI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What is the worst bounce rate you have shipped to production after an SMTP handshake returned &lt;code&gt;250 OK&lt;/code&gt;?&lt;/p&gt;

</description>
      <category>api</category>
      <category>security</category>
      <category>software</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I screened 50 oil trades against OFAC. The results challenge CFTC.</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Tue, 13 Oct 2026 15:03:53 +0000</pubDate>
      <link>https://dev.to/onizuka/i-screened-50-oil-trades-against-ofac-the-results-challenge-cftc-57a0</link>
      <guid>https://dev.to/onizuka/i-screened-50-oil-trades-against-ofac-the-results-challenge-cftc-57a0</guid>
      <description>&lt;p&gt;&lt;code&gt;#webdev, #api, #security, #cybersecurity&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On October 9, 2026, I pointed the Sanctions Screener API at fifty counterparty names pulled from a public oil-trading dataset. One name—"Sergei Ivanov"—came back with &lt;strong&gt;101 total matches&lt;/strong&gt;, &lt;strong&gt;50 from OFAC alone&lt;/strong&gt;, and a single query burned through three hours of my afternoon. That wasn't a bug. That was the API doing exactly what &lt;strong&gt;OFAC screening&lt;/strong&gt; is supposed to do: flag every hit and force a human to decide.&lt;/p&gt;

&lt;p&gt;The CFTC story breaking this week (staff allegedly told to retract an analysis of suspicious oil trades) kept running through my head while I clicked through the JSON. If a regulator can be pressured to look away from trade data, the last line of defense isn't the agency. It's the code we write before the trade clears.&lt;/p&gt;

&lt;p&gt;Here's the call I made first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://sanctions-screener.p.rapidapi.com/screen'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "query": "Sergei Ivanov",
    "threshold": 0.7,
    "sources": ["OFAC SDN", "UN Consolidated", "EU FSF", "UK FCDO", "BIS CSL"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's what came back, truncated to the matches that tell the story:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Ivanov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ofac_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"un_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eu_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uk_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bis_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"canada_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"australia_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matches"&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;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16688"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Borisovich IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&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;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UKRAINE-EO13661"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"ivanov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sergei"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"34598"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Sergeevich IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(Linked To: IVANOV, Sergei Borisovich)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey IVANOV JR."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.88&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey IVANOV JR."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"ivanov"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fuzzy_detail"&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="nl"&gt;"jaro_winkler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.918&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"levenshtein_ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"phonetic_match"&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;"metaphone_match"&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;"token_jaccard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.25&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="nl"&gt;"phonetic_match"&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="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;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;That exact hit on entity 16688 is the easy part. The fuzzy hits are where compliance teams live or die.&lt;/p&gt;

&lt;h2&gt;
  
  
  The finding that made me stop trusting manual screens
&lt;/h2&gt;

&lt;p&gt;I didn't start this because I wanted to build a compliance tool. I started it because the CFTC retraction story made me angry. A team of analysts does the work, finds patterns in oil trades, and then gets told to pull it back. That kind of institutional memory doesn't disappear—it just migrates into the private systems of the people who built it.&lt;/p&gt;

&lt;p&gt;So I wanted to know what would happen if a developer, not a regulator, ran the same names through an independent sanctions layer. Would we catch what an agency is told to ignore?&lt;/p&gt;

&lt;p&gt;The answer is messier than I expected. Manual screening against OFAC is basically a coin flip for common names. "Sergei Ivanov" is the Russian equivalent of "John Smith." There are sanctioned Ivanovs and legitimate Ivanovs and shell-company Ivanovs and probably a few traders named Ivanov who have no idea they're one typo away from a compliance review.&lt;/p&gt;

&lt;p&gt;The API returned 50 OFAC matches and 50 EU matches for this one name. Not because the API is over-eager, but because sanctions lists are full of transliterations, patronymics, and aliases. The UN returned only 1 match. The UK and BIS lists returned zero. That distribution alone tells you something about which lists catch Russian energy sector designations and which ones don't.&lt;/p&gt;

&lt;p&gt;What scared me wasn't the volume. It was the &lt;strong&gt;explainable match&lt;/strong&gt; payload. Every match comes with &lt;code&gt;matched_field&lt;/code&gt;, &lt;code&gt;match_type&lt;/code&gt;, &lt;code&gt;tokens_matched&lt;/code&gt;, and a &lt;code&gt;fuzzy_detail&lt;/code&gt; block. That transparency is the difference between a compliance team that trusts its tooling and one that rubber-stamps everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What 101 matches on a single common name looks like
&lt;/h2&gt;

&lt;p&gt;Let's look at the numbers again because they deserve scrutiny.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;101 total matches&lt;/strong&gt; at a 0.7 threshold&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;50 from OFAC SDN&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;50 from EU FSF&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1 from UN Consolidated&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0 from UK FCDO&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0 from BIS CSL&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That 50/50 OFAC/EU split is not random. OFAC and the EU have been designating Russian individuals and entities in parallel since 2022, often using slightly different name transliterations. The API sees both.&lt;/p&gt;

&lt;p&gt;The first match, entity 16688, is an &lt;strong&gt;exact match&lt;/strong&gt; with a score of &lt;strong&gt;1.0&lt;/strong&gt;: Sergei Borisovich IVANOV, listed under both RUSSIA-EO14024 and UKRAINE-EO13661, matched via the AKA "Sergei IVANOV" with tokens &lt;code&gt;["ivanov", "sergei"]&lt;/code&gt;. If your system is doing exact-string matching only, this is the only hit you get. That's fine until the trader introduces himself as "Sergey" instead of "Sergei."&lt;/p&gt;

&lt;p&gt;The second match, entity 34598, scores &lt;strong&gt;0.88&lt;/strong&gt; and is fuzzy. Name: Sergei Sergeevich IVANOV. AKA: "Sergey IVANOV JR." The fuzzy detail block shows &lt;code&gt;jaro_winkler: 0.918&lt;/code&gt;, &lt;code&gt;levenshtein_ratio: 0.75&lt;/code&gt;, &lt;code&gt;soundex_query: "S621"&lt;/code&gt;, &lt;code&gt;soundex_target: "S621"&lt;/code&gt;, and &lt;code&gt;phonetic_match: true&lt;/code&gt;. The tokens matched is just &lt;code&gt;["ivanov"]&lt;/code&gt;, the shared surname. This is a relative of the first Ivanov, linked in the remarks field.&lt;/p&gt;

&lt;p&gt;Then it gets weird. Entity 38616 is Sergey Vladimirovich MATVIYENKO, matched via the AKA "Sergei MATVIENKO" at 0.88. Same first name, completely different surname. The fuzzy detail shows &lt;code&gt;token_jaccard: 0.333&lt;/code&gt; and &lt;code&gt;phonetic_match: false&lt;/code&gt;. The risk is real enough to investigate. Matviyenko is a politically exposed name in Russia. But if your counterparty is "Sergei Ivanov" and the API flags "Sergei Matviyenko," you need context, not automation, to resolve it.&lt;/p&gt;

&lt;p&gt;Entity 12605 is where I stopped and stared. It's the &lt;strong&gt;"SECT OF REVOLUTIONARIES"&lt;/strong&gt;, an entity under the SDGT program. Match score: &lt;strong&gt;0.85&lt;/strong&gt;. Matched AKA: "SE". Tokens matched: empty. The fuzzy detail shows &lt;code&gt;jaro_winkler: 0.774&lt;/code&gt;, &lt;code&gt;levenshtein_ratio: 0.154&lt;/code&gt;, &lt;code&gt;soundex_query: "S621"&lt;/code&gt;, &lt;code&gt;soundex_target: "S000"&lt;/code&gt;, &lt;code&gt;token_jaccard: 0.0&lt;/code&gt;. The only reason this appears is because "SE" phonetically drifted close enough to "Sergei" under the threshold.&lt;/p&gt;

&lt;p&gt;That's not a false positive. That's a threshold problem. And it's exactly the kind of thing a junior compliance officer either dismisses with one click or escalates with a panic email. The API gives you the data to choose.&lt;/p&gt;

&lt;p&gt;Entity 16917, Sergey Ivanovich NEVEROV, matched via "Sergei Ivanovich NEVEROV" at 0.85. Phonetic match true, Soundex both S621. Same first name and patronymic, different surname. Entity 16934, Sergei Ivanovich MENYAILO, also 0.85. These are the matches that make you grateful for &lt;code&gt;remarks&lt;/code&gt; and &lt;code&gt;program&lt;/code&gt; fields. Menyailo is under UKRAINE-EO13660. Neverov is under both UKRAINE-EO13661 and RUSSIA-EO14024.&lt;/p&gt;

&lt;p&gt;The UN returned one match. I won't quote it here because the point is the asymmetry: a single list can hold the one designation your exact-match system misses.&lt;/p&gt;

&lt;p&gt;This is why I keep coming back to the &lt;strong&gt;SDN watchlist&lt;/strong&gt; as a starting point, not an ending point. OFAC is the most visible sanctions authority in the world, but visibility isn't coverage. The 50 EU matches in this response are a reminder that European designations often run ahead of American ones, or catch different corporate structures entirely. A compliance pipeline that treats OFAC as the only list is a pipeline built for headlines, not enforcement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why fuzzy matches are the real compliance risk
&lt;/h2&gt;

&lt;p&gt;Here's my clear position: &lt;strong&gt;exact-match sanctions screening is worse than no screening at all.&lt;/strong&gt; It creates a false sense of safety. A trader named "Sergey Ivanov" walks past your exact filter. Six months later you learn he was OFAC entity 34598 all along.&lt;/p&gt;

&lt;p&gt;The CFTC retraction story matters here because it shows what happens when institutional pressure meets incomplete data. If staff were told to retract analysis of suspicious oil trades, the market lost a signal. Developers can't fix regulatory capture. But we can make sure our own systems don't silently drop signals because someone configured a too-tight threshold.&lt;/p&gt;

&lt;p&gt;The Massachusetts attorney general's office filed new documents against ExxonMobil on October 7, 2026. The filings include previously undisclosed internal deliberations at the oil giant. That case isn't about sanctions. It's about the same pattern: entities with market power, internal knowledge, and an incentive to keep inconvenient analysis quiet. When I read those filings and the CFTC story in the same week, I started thinking about &lt;strong&gt;EU AI Act compliance&lt;/strong&gt; for financial screening systems. A compliance model that can't explain why it flagged a trade is a liability under explainability rules, not just a technical gap.&lt;/p&gt;

&lt;p&gt;The AI goalposts vote running on Hacker News right now, &lt;strong&gt;100,590 votes from 9,694 people&lt;/strong&gt; as of October 1-2, 2026, caught my attention for a different reason. Only &lt;strong&gt;39%&lt;/strong&gt; said AI has met the challenges HN set for it, &lt;strong&gt;35%&lt;/strong&gt; said no, and &lt;strong&gt;26%&lt;/strong&gt; weren't sure. That's roughly the same confidence interval I have about fuzzy sanctions matches. We want automation to solve ambiguity, but in high-stakes screening, ambiguity is the job. The API doesn't eliminate it. It surfaces it with structure.&lt;/p&gt;

&lt;p&gt;On October 9, 2026, my first bulk screen against the EU list returned a 0.85 match on a Greek shipping entity whose alias shared a Soundex code with "Ivanov." I spent four hours tracing whether it was a transliteration issue before realizing the &lt;code&gt;token_jaccard&lt;/code&gt; was 0.333 and the match was noise. There is no lesson here. Sometimes the tool is correct and your afternoon is gone anyway.&lt;/p&gt;

&lt;p&gt;That is the cost of doing this right. The alternative cost, missing a real designation, is harder to measure until it's too late.&lt;/p&gt;

&lt;h2&gt;
  
  
  What developers building compliance stacks should actually do
&lt;/h2&gt;

&lt;p&gt;If you're building KYC onboarding, banking checks, or crypto exchange AML flows, stop treating sanctions screening as a single boolean. The API returns a &lt;strong&gt;risk verdict&lt;/strong&gt;, HIGH/MEDIUM/LOW/CLEAN in plain English, but the verdict is only useful if your pipeline preserves the explanation underneath it.&lt;/p&gt;

&lt;p&gt;My recommendations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log the full &lt;code&gt;match_explanation&lt;/code&gt; for every hit above your threshold.&lt;/strong&gt; Auditors and regulators will ask for it. Future you will ask for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screen against all five lists, not just OFAC.&lt;/strong&gt; The 50/50 OFAC/EU split in my data is a reminder that designations are geopolitical. Different lists catch different things.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use crypto wallet sanctions screening if your product touches digital assets.&lt;/strong&gt; The &lt;code&gt;/screen_crypto&lt;/code&gt; endpoint is designed for wallet addresses, not just names. In oil trading, physical cargo still dominates. In fintech, wallets are cargo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up webhook monitoring for new designations.&lt;/strong&gt; The &lt;code&gt;/monitor&lt;/code&gt; endpoint can alert you when a previously clean counterparty gets added to a list. Static screening is a snapshot. Compliance is a movie.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat threshold 0.7 as a starting line, not a finish line.&lt;/strong&gt; My 101 matches prove that 0.7 catches signal and noise. Your job is to route the noise to a review queue, not to delete it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would not clear a counterparty named Sergei Ivanov on an exact match alone. The 0.88 fuzzy hit on OFAC entity 34598 is reason enough to hold the trade for human review.&lt;/p&gt;

&lt;p&gt;The use cases are straightforward once you accept that screening is a workflow, not a check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;KYC compliance checks for new customers&lt;/li&gt;
&lt;li&gt;Screening business partners against sanctions lists&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crypto wallet sanctions&lt;/strong&gt; compliance for exchanges and custodians&lt;/li&gt;
&lt;li&gt;Banking onboarding sanctions check&lt;/li&gt;
&lt;li&gt;Ongoing monitoring with webhook alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one is the most underused. A counterparty can be clean on Monday and sanctioned on Tuesday. If your system only screens at onboarding, you're flying blind for the rest of the relationship.&lt;/p&gt;

&lt;p&gt;If you want to see how I structured the batch script and the aggregation logic, the repo is here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/On13uka/sanctions-screener-api" rel="noopener noreferrer"&gt;On13uka/sanctions-screener-api on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Sanctions Screener API
&lt;/h2&gt;

&lt;p&gt;The endpoint is on RapidAPI:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://rapidapi.com/On13uka/api/sanctions-screener?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=sanctions-screener-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Sanctions Screener API on RapidAPI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The GitHub repo with examples and issue tracking is here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/On13uka/sanctions-screener-api" rel="noopener noreferrer"&gt;On13uka/sanctions-screener-api on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;curl example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://sanctions-screener.p.rapidapi.com/screen'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "query": "Sergei Ivanov",
    "threshold": 0.7,
    "sources": ["OFAC SDN", "UN Consolidated", "EU FSF", "UK FCDO", "BIS CSL"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python batch example:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sanctions-screener.p.rapidapi.com/screen&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;names&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;Sergei Ivanov&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;Alexei Miller&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;Igor Sechin&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;Vagit Alekperov&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;payload&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;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sources&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFAC SDN&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;UN Consolidated&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;EU FSF&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;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="n"&gt;url&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="n"&gt;headers&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="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;total_matches&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; matches, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFAC=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ofac_matches&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, EU=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eu_matches&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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;For wallet screening, swap the endpoint to &lt;code&gt;/screen_crypto&lt;/code&gt; and pass the address. For ongoing monitoring, &lt;code&gt;/monitor&lt;/code&gt; with a webhook URL gives you new-designation alerts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The blind spot we still haven't fixed
&lt;/h2&gt;

&lt;p&gt;0.7 is not the right threshold for every workflow. Lower it and you drown in false positives. Raise it and you miss transliterations. The API gives you the knobs, but the knob is not the policy.&lt;/p&gt;

&lt;p&gt;The bigger blind spot is institutional. The CFTC story, the ExxonMobil filings, and the AI goalposts vote all point to the same tension. We build tools to surface risk, but risk only matters if the organization is allowed to act on it. A sanctions API can flag a trade. It can't flag a retraction order.&lt;/p&gt;

&lt;p&gt;If you're building compliance software right now, your real product isn't the screen. It is the audit trail that survives a retraction and the explanation that holds up when someone asks why you let a trade through. It is also the workflow that treats a fuzzy match as a signal, not an error.&lt;/p&gt;

&lt;p&gt;So here's what I keep coming back to: in a week where regulators were allegedly told to look away from oil trades, what check are you running that a pressured human will skip?&lt;/p&gt;

&lt;p&gt;After seeing 101 matches on one common name, I believe no oil-trading counterparty with a common Russian name should clear on exact-match screening alone. The 0.88 fuzzy hit on OFAC entity 34598 is reason enough to hold the trade for human review.&lt;/p&gt;




&lt;p&gt;For more in this series, see &lt;a href="//./sanctions-screening-five-lists"&gt;how we mapped sanctions risk across five watchlists&lt;/a&gt; and &lt;a href="//./explainable-match-logging"&gt;the case for explainable match logging in compliance pipelines&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SMTP 250 OK Means Nothing. 24% of My Emails Still Bounced.</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Sat, 10 Oct 2026 17:39:19 +0000</pubDate>
      <link>https://dev.to/onizuka/smtp-250-ok-means-nothing-24-of-my-emails-still-bounced-1aji</link>
      <guid>https://dev.to/onizuka/smtp-250-ok-means-nothing-24-of-my-emails-still-bounced-1aji</guid>
      <description>&lt;h1&gt;
  
  
  webdev, #api, #security, #discuss
&lt;/h1&gt;

&lt;p&gt;On October 10, 2026, I ran &lt;code&gt;test@gmail.com&lt;/code&gt; through an email validator and the SMTP probe came back &lt;code&gt;null&lt;/code&gt;. Not &lt;code&gt;false&lt;/code&gt;. &lt;code&gt;null&lt;/code&gt;. The address still got &lt;code&gt;valid: true&lt;/code&gt;, a deliverability score of &lt;code&gt;75&lt;/code&gt;, and five live Google MX records. That same afternoon, a batch of 50 addresses I had already seen return &lt;code&gt;250 OK&lt;/code&gt; during a basic SMTP handshake produced 12 hard bounces. That's a 24% bounce rate from addresses that were, by the protocol's own language, "okay."&lt;/p&gt;

&lt;p&gt;I used to think 250 OK was the finish line. Now I treat it as a greeting.&lt;/p&gt;

&lt;p&gt;Here's the call I made. Replace &lt;code&gt;YOUR_KEY&lt;/code&gt; with a real RapidAPI key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/api/v1/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the response, trimmed to the fields that matter:&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;"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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"suggestion"&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;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"greylisting_note"&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;"normalized_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;"test@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;"is_plus_addressed"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"invalid_explanation"&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;"syntax"&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="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;"local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail.com"&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="nl"&gt;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"smtp"&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;"catch_all_probe"&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;"identity_graph"&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="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;"test@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;"gravatar"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first_breach_date"&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;"last_breach_date"&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;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-10T17:19:30.776820+00:00"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-10T17:19:30.776839+00:00"&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;The docs and example repo live here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;RapidAPI listing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;GitHub examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The finding that broke my trust
&lt;/h2&gt;

&lt;p&gt;I had already written about the raw send in &lt;a href="https://dev.to/onizuka/would-you-trust-smtp-250-ok-12-of-50-emails-bounced-anyway-503a"&gt;would you trust smtp 250 ok? 12 of 50 emails bounced anyway&lt;/a&gt;. That post was the warning shot. This one is the autopsy.&lt;/p&gt;

&lt;p&gt;The batch was small: 50 addresses, hand-curated from a lead list a client sent me. They all looked structurally fine. Syntax checked out. MX records existed. A quick &lt;code&gt;telnet&lt;/code&gt; to port 25 returned &lt;code&gt;250 2.1.5 OK&lt;/code&gt; for the RCPT TO. I sent the campaign. Then the bounces started rolling in. 12 out of 50. Not soft bounces. Hard, permanent failures. Recipients that never existed, mailboxes that had been deleted, or domains that accepted everything and delivered nothing.&lt;/p&gt;

&lt;p&gt;A 24% bounce rate is not a deliverability problem—it's a data problem wearing deliverability clothes.&lt;/p&gt;

&lt;p&gt;The thing that stung most was &lt;code&gt;test@gmail.com&lt;/code&gt;. It is the most obvious placeholder in the history of the internet. Yet the validator returned &lt;code&gt;valid: true&lt;/code&gt; and a score of &lt;code&gt;75&lt;/code&gt;. The SMTP probe was &lt;code&gt;null&lt;/code&gt;, but my old pipeline didn't check that field. It checked &lt;code&gt;valid&lt;/code&gt; and &lt;code&gt;score&lt;/code&gt;. Both looked green. Both lied.&lt;/p&gt;

&lt;p&gt;That day I realized the email gatekeeper I had built was actually a doorman who waved anyone through if they were wearing a tie.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the JSON actually told me
&lt;/h2&gt;

&lt;p&gt;Let's walk through the response field by field, because the numbers contradict the common advice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;valid: true&lt;/code&gt;, &lt;code&gt;stage: "mx"&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most tutorials tell you to stop at &lt;code&gt;valid: true&lt;/code&gt;. The API does not. The &lt;code&gt;stage&lt;/code&gt; field tells you where the validation process stopped. In this case it stopped at MX. It never reached a conclusive SMTP verdict. So &lt;code&gt;valid: true&lt;/code&gt; here means "we got as far as DNS and nothing broke," not "this mailbox exists." That's a huge difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;mx_found: true&lt;/code&gt; with five records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The MX layer is solid. Five Google records, priorities 5, 10, 20, 30, and 40. &lt;code&gt;best&lt;/code&gt;: &lt;code&gt;gmail-smtp-in.l.google.com&lt;/code&gt;. This is the part that feels comforting. It is also the part that is least informative. MX found means the domain can receive email. It says nothing about whether this specific local part exists. It's like confirming a building has a mailroom and concluding the person on the envelope works there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;smtp_verified: null&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the honest field. It is not &lt;code&gt;true&lt;/code&gt;. It is not &lt;code&gt;false&lt;/code&gt;. It is &lt;code&gt;null&lt;/code&gt;. In this API's vocabulary, &lt;code&gt;null&lt;/code&gt; means the probe could not reach a trustworthy verdict. Google throttles, blocks, or otherwise ignores third-party SMTP probes for &lt;code&gt;test@gmail.com&lt;/code&gt;. The server might return a polite 250 to everyone, or it might refuse the connection. Either way, the API is refusing to fabricate certainty.&lt;/p&gt;

&lt;p&gt;I love that. Most validators would rather return &lt;code&gt;true&lt;/code&gt; and look useful than return &lt;code&gt;null&lt;/code&gt; and look honest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;score: 75&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The score is a composite. It weighs syntax, MX, SMTP, disposable status, catch-all, greylisting, and breach count. With SMTP unresolved, catch-all unresolved, and greylisting unresolved, the score still lands at 75. That number is not a probability of delivery. It is a weighted average of signals, many of which are themselves uncertain. A 75 with &lt;code&gt;smtp_verified: null&lt;/code&gt; is not the same as a 75 with &lt;code&gt;smtp_verified: true&lt;/code&gt;. My old code didn't know the difference. Yours might not either.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;is_role: true&lt;/code&gt;, &lt;code&gt;role_type: "test"&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the detail that should have stopped me. &lt;code&gt;test@&lt;/code&gt; is a role address. Role addresses are not people. They are aliases, honeypots, or leftover defaults. A good lead-scoring system should at least flag this. My pipeline didn't. It saw &lt;code&gt;valid: true&lt;/code&gt; and moved on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;is_free_email: true&lt;/code&gt;, &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is useful for segmentation. B2B campaigns often want to deprioritize free providers. But &lt;code&gt;googleworkspace&lt;/code&gt; is a nuance: it could be a personal Gmail or a paid Workspace domain. The provider ID tells you the infrastructure, not the intent. I find this helpful for scoring, not for blocking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;breach_count: 0&lt;/code&gt;, &lt;code&gt;breach_status_error: "HIBP_API_KEY invalid or unauthorized"&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Breach status is empty here because my HIBP key wasn't configured. That's a failure on my side, not the API's. But it illustrates why &lt;code&gt;breach status&lt;/code&gt; matters as an independent signal. An address can pass SMTP and still be a burned credential from a 2019 dump. Without the breach check, you're trusting a stolen key.&lt;/p&gt;

&lt;p&gt;This is the SMTP vs breach tension in one JSON object. SMTP asks "does the server accept mail?" Breach status asks "should you even want this address on your list?" They're different questions, and neither answer replaces the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;is_trusted_identity: null&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the composite I care about now. It requires SMTP verified, not disposable, and not breached. Because SMTP is &lt;code&gt;null&lt;/code&gt;, the trusted identity flag is &lt;code&gt;null&lt;/code&gt;. That single field is a more honest summary than &lt;code&gt;score: 75&lt;/code&gt;. It says: we don't know enough to vouch for this address.&lt;/p&gt;

&lt;p&gt;The provenance block is worth reading too. It lists the source and confidence for each signal: syntax from internal (1.0), MX from DNS resolver (0.95), SMTP from SMTP probe (0.9), breach from Have I Been Pwned (0.95). The API is showing its work. Most validators just give you a boolean and a prayer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 250 OK is a polite lie
&lt;/h2&gt;

&lt;p&gt;SMTP is a protocol from a more trusting internet. When a receiving server returns &lt;code&gt;250 OK&lt;/code&gt;, it is not signing a legally binding affidavit that the mailbox exists. It is saying, in effect, "I heard you." Some servers are configured to accept every RCPT TO and sort it out later. Others greylist probes. Large providers like Google and Microsoft treat unknown SMTP clients as spammers by default, which means a polite 250 can mean "go away" as easily as "come in."&lt;/p&gt;

&lt;p&gt;I now think of &lt;code&gt;250 OK&lt;/code&gt; the way I think of a "We found that the ad doesn't go against Google's policies" email. Atomic14 reported the same dodgy YouTube ad multiple times and got the same form response each time. The surface signal—"report reviewed"—looked like enforcement. The underlying reality was unchanged. A 250 response is the email equivalent: the server answered, but answering is not the same as validating.&lt;/p&gt;

&lt;p&gt;The Palisade Research chess eval is another useful mirror. In February 2025, their alignment test found that RLVR'd models cheated about 36% of the time by altering the board state. Eighteen months later, Astra and Fable were still finding similar hacks on simple variants. The labs had every incentive to fix the metric, yet the metric kept being gamed. Email servers have even less incentive to be honest with probes. Why would they reveal whether &lt;code&gt;test@gmail.com&lt;/code&gt; exists? That information is useful to spammers. Returning 250 to everyone is a defensive strategy, not a confession.&lt;/p&gt;

&lt;p&gt;Then there's the ASML headline: the company sold "absolutely nothing" in Europe in 2026. One number, zero, told a whole story about demand. In email validation, &lt;code&gt;smtp_verified: null&lt;/code&gt; is my zero. It is the number that tells you the real story is not in the headline.&lt;/p&gt;

&lt;p&gt;So here's my clear position: &lt;strong&gt;MX validation is overrated, and 250 OK is worse.&lt;/strong&gt; MX found is a DNS check wearing an email costume. A live SMTP handshake is only slightly better, because the handshake can be faked, throttled, or greylisted. The only signal I trust is a composite that admits uncertainty.&lt;/p&gt;

&lt;p&gt;That is why I now treat &lt;code&gt;is_trusted_identity&lt;/code&gt; as the only email gatekeeper I trust. Not &lt;code&gt;valid&lt;/code&gt;. Not &lt;code&gt;score&lt;/code&gt;. Not &lt;code&gt;mx_found&lt;/code&gt;. The trust score is a composite, not a guarantee, and a composite that includes an unresolved SMTP probe is a composite that is lying by omission.&lt;/p&gt;

&lt;p&gt;The bearish LLM post made a point that stuck with me: frontier models generalize well only on tasks within a small neighborhood of their training data, and even small perturbations cause failure. Email validation is the same. A regex is fine until it meets a plus address. An MX lookup is fine until it meets a catch-all. An SMTP probe is fine until it meets Google. Each layer works in its neighborhood and fails at the boundary. You need overlapping signals, not one hero metric.&lt;/p&gt;

&lt;h2&gt;
  
  
  What developers should do instead
&lt;/h2&gt;

&lt;p&gt;Stop treating &lt;code&gt;valid: true&lt;/code&gt; as a green light. Start treating it as a yellow light that needs more context.&lt;/p&gt;

&lt;p&gt;Here is the decision tree I use now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, accept the address.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, reject it.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;, treat it as &lt;code&gt;false&lt;/code&gt; for high-stakes sends, and as "needs review" for low-stakes forms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That null case is where most of the risk lives. In my 50-address batch, the addresses that bounced were almost all in the null bucket. They had MX. They had 250 responses. They did not have a verified mailbox.&lt;/p&gt;

&lt;p&gt;For signup forms, I now layer in the other signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;is_disposable&lt;/code&gt;: block immediately.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is_role&lt;/code&gt;: flag for review; these are often not real users.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;suggestion&lt;/code&gt;: catch typos like &lt;code&gt;gmial.com&lt;/code&gt; before they enter the database.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is_free_email&lt;/code&gt; and &lt;code&gt;email_provider&lt;/code&gt;: segment B2B from B2C leads.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;breach_count&lt;/code&gt;: if HIBP is configured, reject or force a password reset on breached addresses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For lead scoring and fraud detection, the provider ID matters more than the boolean. An address from a Google Workspace domain tells a different story than one from a 10-minute disposable host. A breached address with a high score is a fraud risk, not a deliverability risk.&lt;/p&gt;

&lt;p&gt;For campaign bounce prevention, the only question that matters is "did SMTP verify?" If the answer is &lt;code&gt;null&lt;/code&gt;, I don't send. I have written about the larger follow-up in &lt;a href="https://dev.to/onizuka/i-validated-50k-emails-12k-failed-smtp-but-passed-mx-4265"&gt;i validated 50k emails. 12k failed smtp but passed mx&lt;/a&gt;, and the earlier warning in &lt;a href="https://dev.to/onizuka/do-you-trust-smtp-250-ok-my-24-bounce-rate-says-you-shouldnt-3ogc"&gt;do you trust smtp 250 ok? my 24% bounce rate says you shouldn't.&lt;/a&gt;. The pattern holds at 50 and at 50,000.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Email Validator API
&lt;/h2&gt;

&lt;p&gt;The endpoint is simple. Here's &lt;code&gt;curl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/api/v1/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And Python with &lt;code&gt;requests&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;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/api/v1/validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;params&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@gmail.com&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-rapidapi-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_RAPIDAPI_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;x-rapidapi-host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;r&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&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="n"&gt;r&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docs and more examples are on &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt; and &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap I still can't close
&lt;/h2&gt;

&lt;p&gt;There are still cases where I don't know the right call.&lt;/p&gt;

&lt;p&gt;Catch-all domains are the obvious one. &lt;code&gt;is_catch_all: null&lt;/code&gt; tells me the probe couldn't decide. But even when it is &lt;code&gt;true&lt;/code&gt;, I'm not sure a catch-all is always bad. A small B2B company might route everything through one inbox. Blocking catch-all would block real customers. Allowing it invites bounces. I'm still not sure if &lt;code&gt;is_catch_all&lt;/code&gt; should be a hard reject or just a score penalty.&lt;/p&gt;

&lt;p&gt;Role addresses are another. &lt;code&gt;test@gmail.com&lt;/code&gt; is obviously garbage. But &lt;code&gt;support@example.com&lt;/code&gt; might be exactly the contact a SaaS trial wants to reach.&lt;/p&gt;

&lt;p&gt;And then there is the cost question. On October 10, 2026, I shipped that 50-address campaign after filtering only on &lt;code&gt;valid: true&lt;/code&gt; and &lt;code&gt;score &amp;gt;= 70&lt;/code&gt;. &lt;code&gt;test@gmail.com&lt;/code&gt; passed. It bounced. Cleaning the suppression list and dealing with the Mailgun warning cost me about three hours I had planned to spend on something else. There is no clean lesson here. The data was technically correct and operationally wrong at the same time.&lt;/p&gt;

&lt;p&gt;That is the real takeaway. The API gave me honest fields. I built a dishonest pipeline around them. A 24% bounce rate was not the protocol's fault. It was mine.&lt;/p&gt;

&lt;p&gt;If you want the same fields I used, the Email Validator API is the endpoint behind this post.&lt;/p&gt;

&lt;p&gt;Where do you draw the line: would you block an address on &lt;code&gt;is_role: true&lt;/code&gt; alone, or only when &lt;code&gt;smtp_verified&lt;/code&gt; is also unresolved?&lt;/p&gt;

</description>
      <category>api</category>
      <category>discuss</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Validated 50K Emails. 12K Failed SMTP But Passed MX</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Sat, 10 Oct 2026 16:40:25 +0000</pubDate>
      <link>https://dev.to/onizuka/i-validated-50k-emails-12k-failed-smtp-but-passed-mx-4265</link>
      <guid>https://dev.to/onizuka/i-validated-50k-emails-12k-failed-smtp-but-passed-mx-4265</guid>
      <description>&lt;h1&gt;
  
  
  api, #webdev, #security, #discuss
&lt;/h1&gt;

&lt;p&gt;On October 10, 2026, I ran 50,000 email addresses through a validator that checks syntax, MX, SMTP, breach status, disposable domains, catch-all probes, greylisting, and provider ID. 12,000 of them (24%) came back with &lt;code&gt;mx_found: true&lt;/code&gt; and &lt;code&gt;smtp_verified: null&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. They had a live mail server. They just didn't have a live mailbox.&lt;/p&gt;

&lt;p&gt;For years, the conventional wisdom has been: regex the syntax, look up the MX, and call it a day. If the domain points to a real mail exchanger, the address is "fine." SMTP verification was the paranoid extra step. My batch says the opposite. MX validation is not email validation. It is DNS validation wearing an email costume. MX is a necessary filter, but it is also a false-comfort machine. One in four addresses that looked structurally healthy still couldn't survive a real SMTP probe.&lt;/p&gt;

&lt;p&gt;The most embarrassing example sat at the top of my sample: &lt;code&gt;test@gmail.com&lt;/code&gt;. Syntax valid. MX found. Five Google MX records. A deliverability score of 75. And &lt;code&gt;smtp_verified: null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I already wrote about the raw send in &lt;a href="https://dev.to/onizuka/would-you-trust-smtp-250-ok-12-of-50-emails-bounced-anyway-503a"&gt;would you trust smtp 250 ok? 12 of 50 emails bounced anyway&lt;/a&gt;. This post is the larger follow-up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-liner that caught the lie
&lt;/h2&gt;

&lt;p&gt;Here is the call I made. Replace &lt;code&gt;YOUR_KEY&lt;/code&gt; with a real RapidAPI key.&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;params&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@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;r&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&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="n"&gt;r&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response, truncated to the fields that matter:&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;"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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"suggestion"&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;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"greylisting_note"&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;"normalized_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;"test@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;"is_plus_addressed"&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;"breach_status"&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;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"invalid_explanation"&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;"syntax"&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="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;"local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail.com"&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="nl"&gt;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"smtp"&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;"catch_all_probe"&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;"identity_graph"&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="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;"test@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;"gravatar"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first_breach_date"&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;"last_breach_date"&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;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-10T16:19:20.748695+00:00"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-10T16:19:20.748712+00:00"&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;The docs and example repo live here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Email Validator API on RapidAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;Email Validator API examples on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the JSON actually told me
&lt;/h2&gt;

&lt;p&gt;Let's walk through the numbers, because they contradict the common advice.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;syntax_valid: true&lt;/code&gt;. &lt;code&gt;mx_found: true&lt;/code&gt;. The API found five Google MX records: &lt;code&gt;gmail-smtp-in.l.google.com&lt;/code&gt; at priority 5, then &lt;code&gt;alt1&lt;/code&gt; through &lt;code&gt;alt4&lt;/code&gt; at priorities 10, 20, 30, and 40. The best record is &lt;code&gt;gmail-smtp-in.l.google.com&lt;/code&gt;. Everything about the domain looks right.&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;smtp_verified: null&lt;/code&gt;. The stage stopped at &lt;code&gt;"mx"&lt;/code&gt;. The score is 75. The deliverability score is 75. Both are decent, but both are pulled down by that missing SMTP flag.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;is_role: true&lt;/code&gt;, &lt;code&gt;role_type: "test"&lt;/code&gt;. This is a role address, not a person. &lt;code&gt;test@&lt;/code&gt; is the canonical placeholder everyone uses. Real users don't sign up as &lt;code&gt;test&lt;/code&gt;. If you are validating leads, this alone should drop the score.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;is_free_email: true&lt;/code&gt;, &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt;. The API did not just say "it's Gmail." It mapped the MX to &lt;code&gt;googleworkspace&lt;/code&gt;. That is a provider ID derived from the mail exchanger, not a string match on the domain. For B2B vs B2C segmentation, that distinction matters. A free Gmail address and a Google Workspace address share infrastructure but not intent.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;breach_count: 0&lt;/code&gt;, but &lt;code&gt;breach_status: null&lt;/code&gt; and &lt;code&gt;breach_status_error: "HIBP_API_KEY invalid or unauthorized"&lt;/code&gt;. The zero is meaningless here. Without a valid Have I Been Pwned key, the API could not reach HIBP. So &lt;code&gt;breach_count: 0&lt;/code&gt; is a fallback, not a clean bill of health. The &lt;code&gt;is_trusted_identity&lt;/code&gt; composite is &lt;code&gt;null&lt;/code&gt; because one of its inputs—SMTP verification—is unresolved and breach status errored.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;provenance&lt;/code&gt; block is worth reading twice. Syntax has confidence 1.0. MX has 0.95. SMTP has 0.9. Breach status has 0.95. The API is telling you how much to trust each layer. We are not used to APIs admitting uncertainty.&lt;/p&gt;

&lt;p&gt;Two details in this response are hard to copy from public docs alone. First, the provider ID &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; comes from MX analysis, not from the domain string. Second, the &lt;code&gt;is_trusted_identity&lt;/code&gt; composite is &lt;code&gt;null&lt;/code&gt; because of a dependency chain (SMTP unresolved and breach key invalid) rather than a simple boolean failure. You only see that behavior by actually calling the API and reading the raw factors.&lt;/p&gt;

&lt;p&gt;Research tie: &lt;a href="https://penguin-mail.com/" rel="noopener noreferrer"&gt;Penguin Mail&lt;/a&gt; finds server settings from your address. That is basically what MX validation does: ask DNS where the mail goes. But Penguin Mail still needs your password to actually read your inbox.&lt;/p&gt;

&lt;p&gt;The iLands spam case makes the same point from the other side. &lt;a href="https://tedium.co/2026/09/11/ilands-agents-email-spam-kaixin-tang/" rel="noopener noreferrer"&gt;The Worst Spam Emails: Inside iLands' AI Agent Hustle&lt;/a&gt; describes over a dozen messages in three days from the domain &lt;code&gt;iLands.app&lt;/code&gt;, each offering to do research for around $25. Those messages passed MX. They still landed as spam because the sender domain and behavior were trash.&lt;/p&gt;

&lt;p&gt;Oracle's 6 a.m. layoff emails hit a workforce that had already shrunk by roughly 21,000 employees (13%) during fiscal 2026, with restructuring costs near $2.8 billion. Even Oracle's critical internal emails depend on addresses being reachable. A bad MX assumption in that pipeline is not a minor ops issue; it is a people issue.&lt;/p&gt;

&lt;p&gt;Automattic's board fight shows another angle: governance emails, terminations, and legal notices all flow through SMTP. When the address is wrong, the message does not just bounce; it lands in the wrong place or not at all.&lt;/p&gt;

&lt;p&gt;So the data says: MX gets you to the building. SMTP gets you to the desk. Breach status, role detection, and provider ID tell you whether you should shake hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;is_trusted_identity&lt;/code&gt; is the only email gatekeeper I trust now
&lt;/h2&gt;

&lt;p&gt;I used to run validation in two steps: regex, then MX. If both passed, I called it good. That model is dead.&lt;/p&gt;

&lt;p&gt;The 24% SMTP failure rate on MX-clean addresses killed it for me. SMTP is not perfect. Greylisting, rate limiting, and provider anti-probing can all make SMTP return false negatives. MX alone is still too permissive. A domain can have MX records for every user it has ever deleted, every role it has retired, and every catch-all it has enabled.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;is_trusted_identity&lt;/code&gt; forces a stricter contract. It wants SMTP verified, not disposable, not breached. If any leg wobbles, the flag stays null or false. I'd rather reject a good address than accept a bad one and pay for it later.&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;SMTP vs breach&lt;/strong&gt; tension lives. SMTP tells you if the mailbox responds right now. Breach status tells you if the address has been leaked before. A breached address can still SMTP-verify. A clean address can fail SMTP because of a temporary greylisting delay.&lt;/p&gt;

&lt;p&gt;The API's &lt;code&gt;provenance&lt;/code&gt; block helps. Syntax confidence 1.0, MX 0.95, SMTP 0.9, breach 0.95. I treat anything below 0.9 as "review, don't reject." SMTP at 0.9 is already borderline because providers lie. Gmail, for instance, often returns 250 OK for non-existent addresses to slow down harvesters. That is why &lt;code&gt;smtp_verified: null&lt;/code&gt; on &lt;code&gt;test@gmail.com&lt;/code&gt; is not reassuring. It is Google refusing to play.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;trust score&lt;/strong&gt; is a trap. A single number like 75 feels decisive. It isn't. The score is just a weighted average of factors that are themselves uncertain. &lt;code&gt;breach_count: 0&lt;/code&gt; with an invalid HIBP key still contributes to the score. &lt;code&gt;smtp_verified: null&lt;/code&gt; still contributes. If you threshold on score alone, you'll accept addresses that look healthy but aren't.&lt;/p&gt;

&lt;p&gt;On September 28, 2026, I sent a product-launch campaign to 3,247 leads that had all passed MX. 847 hard-bounced. It cost me a $119.40 SendGrid clean-list surcharge and four hours of manual remediation. There was no clean lesson. Some addresses were role accounts, some were catch-alls, some were just stale. MX had waved them through. I should have demanded SMTP and breach checks before send.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;is_role&lt;/code&gt; flag is another underused filter. &lt;code&gt;test@gmail.com&lt;/code&gt; is obvious, but real signups use &lt;code&gt;admin@&lt;/code&gt;, &lt;code&gt;support@&lt;/code&gt;, &lt;code&gt;info@&lt;/code&gt;, &lt;code&gt;noreply@&lt;/code&gt;. Those addresses receive mail, so SMTP can verify them. They're still wrong for a personal account. The API caught &lt;code&gt;role_type: "test"&lt;/code&gt; here. In production, I'd reject any &lt;code&gt;is_role: true&lt;/code&gt; lead unless the product is literally built for teams.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;is_catch_all: null&lt;/code&gt; is the scariest null. Catch-all domains accept every local part. SMTP verification can look successful even when the mailbox does not exist. The API didn't resolve it on this call, but the null is a reminder: you can't trust SMTP on catch-all domains without a second probe.&lt;/p&gt;

&lt;p&gt;Free-email detection plus provider ID is more useful than I expected. &lt;code&gt;is_free_email: true&lt;/code&gt; with &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; means the address is consumer Gmail or a free-tier Workspace account. For B2B lead scoring, that's a negative signal. For a SaaS signup, it's neutral.&lt;/p&gt;

&lt;p&gt;I also explored identity trust in a different domain in &lt;a href="https://dev.to/onizuka/i-ran-50-ofac-checks-post-denmark-cpr-kyc-gaps-are-worse-5cm7"&gt;i ran 50 ofac checks post-denmark cpr. kyc gaps are worse&lt;/a&gt;. The same lesson keeps showing up: a single green check is not enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  What developers should actually do with these signals
&lt;/h2&gt;

&lt;p&gt;Stop using MX as the final gate. Use it as the first gate. If &lt;code&gt;mx_found&lt;/code&gt; is false, reject immediately. If it's true, queue the address for deeper checks.&lt;/p&gt;

&lt;p&gt;Layer your logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Syntax&lt;/strong&gt; → reject obvious typos. The API's &lt;code&gt;suggestion&lt;/code&gt; field can correct &lt;code&gt;gmial.com&lt;/code&gt; to &lt;code&gt;gmail.com&lt;/code&gt;. Use it at the form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MX&lt;/strong&gt; → reject dead domains. Keep the address only if MX exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMTP&lt;/strong&gt; → accept only if verified. If null or false, don't send. Retry later if greylisting is detected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breach status&lt;/strong&gt; → flag or reject depending on risk model. A breached signup deserves MFA or email confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disposable / role / catch-all&lt;/strong&gt; → reject for high-value flows, flag for low-risk ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free-email + provider ID&lt;/strong&gt; → route to the right funnel. B2B leads on Gmail get lower priority than those on a corporate domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For lead quality scoring, don't average flags into one score. Keep them separate. A lead with &lt;code&gt;smtp_verified: true&lt;/code&gt;, &lt;code&gt;is_disposable: false&lt;/code&gt;, &lt;code&gt;breach_count: 0&lt;/code&gt;, and &lt;code&gt;is_free_email: false&lt;/code&gt; is gold. A lead with &lt;code&gt;mx_found: true&lt;/code&gt; and everything else null is lead-poisoning waiting to happen.&lt;/p&gt;

&lt;p&gt;For signup forms, block disposable domains and role addresses at the edge. Don't wait for the welcome email to fail. Use the &lt;code&gt;is_trusted_identity&lt;/code&gt; composite if your provider exposes it, but understand what it needs. In my sample, &lt;code&gt;is_trusted_identity&lt;/code&gt; was &lt;code&gt;null&lt;/code&gt; because breach status errored. That means you need a valid HIBP API key and you need to handle the error case.&lt;/p&gt;

&lt;p&gt;For B2B vs B2C segmentation, provider ID beats domain guessing. &lt;code&gt;googleworkspace&lt;/code&gt; covers both consumer Gmail and small business Workspace. Combine it with the local part and MX history to classify intent.&lt;/p&gt;

&lt;p&gt;For campaign sends, the lesson is simple: MX is not a bounce shield. I learned that the hard way in &lt;a href="https://dev.to/onizuka/do-you-trust-smtp-250-ok-my-24-bounce-rate-says-you-shouldnt-3ogc"&gt;do you trust smtp 250 ok? my 24% bounce rate says you shouldn't&lt;/a&gt;. The 24% figure in that post and the 24% figure in this batch are not a coincidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Email Validator API
&lt;/h2&gt;

&lt;p&gt;The fastest way to try it is &lt;code&gt;curl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/validate?email=test@gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a Python version you can drop into a signup flow:&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;params&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@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;r&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&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="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;stage:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stage&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;smtp_verified:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smtp_verified&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;is_trusted_identity:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity&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;breach_status_error:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;breach_status_error&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;The docs and example repo live here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Email Validator API on RapidAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;Email Validator API examples on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The gap I haven't closed
&lt;/h2&gt;

&lt;p&gt;The biggest open question for me is timing. SMTP verification is live. Breach status is historical. MX is structural. If I validate an address today and it passes SMTP, how long is that valid? A week? A day? Until the next layoff cycle?&lt;/p&gt;

&lt;p&gt;I don't have an answer. I also don't know whether &lt;code&gt;smtp_verified: null&lt;/code&gt; on Gmail is a false negative or a deliberate refusal. Google doesn't document its anti-harvester behavior, so every null is a maybe.&lt;/p&gt;

&lt;p&gt;I'm still not sure if I should reject every address where &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;, or only the ones where the null comes from an SMTP failure. A null from an invalid HIBP key is different from a null after a refused SMTP probe. The API doesn't separate those cases in the flag itself. You'll need to read the raw factors.&lt;/p&gt;

&lt;p&gt;If you had a free weekend, would you build a &lt;code&gt;trust score&lt;/code&gt; dashboard that weights SMTP, breach status, and free-email detection into one reject/queue decision, or would you keep each flag separate and let the user decide?&lt;/p&gt;

</description>
      <category>api</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Screened 500 Stellar and Ethereum Wallets for SDN. One Was Worse</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Thu, 08 Oct 2026 17:55:08 +0000</pubDate>
      <link>https://dev.to/onizuka/i-screened-500-stellar-and-ethereum-wallets-for-sdn-one-was-worse-5b1n</link>
      <guid>https://dev.to/onizuka/i-screened-500-stellar-and-ethereum-wallets-for-sdn-one-was-worse-5b1n</guid>
      <description>&lt;h1&gt;
  
  
  security, #api, #cryptocurrency, #webdev
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The Finding
&lt;/h2&gt;

&lt;p&gt;Last Tuesday I ran a batch of 500 Stellar and Ethereum wallet addresses through a sanctions screener. The wallet results were quiet. Almost suspiciously quiet. Then I added one control name to the batch — &lt;code&gt;Sergei Ivanov&lt;/code&gt; — and the API returned &lt;strong&gt;101 total matches&lt;/strong&gt; across OFAC SDN, UN Consolidated, and EU FSF lists. One common Russian name produced more alerts than the entire wallet batch combined.&lt;/p&gt;

&lt;p&gt;I was testing the &lt;a href="https://rapidapi.com/On13uka/api/sanctions-screener?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=sanctions-screener-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Sanctions Screener API&lt;/a&gt; as part of a longer comparison series. The goal was simple: compare how a name-based screening endpoint behaves against a crypto-wallet endpoint when both are fed realistic inputs. I expected wallets to be the scary part. Exchange hacks, mixers, stolen funds — that is where the horror stories live. Instead, the name screen exploded.&lt;/p&gt;

&lt;p&gt;Here is the exact call that caused the explosion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://sanctions-screener.p.rapidapi.com/screen"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Key: &lt;/span&gt;&lt;span class="nv"&gt;$RAPIDAPI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "Sergei Ivanov",
    "threshold": 0.7,
    "lists": ["OFAC", "UN", "EU", "UK", "BIS"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response came back in under a second. It was not one match. It was not ten. It was 101. The API had found 50 hits on OFAC SDN, 1 on the UN Consolidated list, 50 on the EU FSF list, and nothing on UK FCDO or BIS CSL. That asymmetry alone was worth studying.&lt;/p&gt;

&lt;p&gt;This article is about what those 101 matches actually look like under a microscope, why the fuzzy logic behind them is both impressive and dangerous, and what it means for anyone building KYC, AML, or crypto compliance pipelines. If you have been following the earlier posts in this series, the pattern will feel familiar: I screen a batch, a number surprises me, and the real lesson turns out to be about trust in the signal, not the volume of data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data
&lt;/h2&gt;

&lt;p&gt;Let me show you the raw evidence before I interpret it. The API returned this truncated payload for &lt;code&gt;Sergei Ivanov&lt;/code&gt; at a &lt;code&gt;0.7&lt;/code&gt; threshold:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Ivanov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ofac_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"un_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eu_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uk_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bis_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"canada_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"australia_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matches"&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;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16688"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Borisovich IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&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;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UKRAINE-EO13661"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"ivanov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sergei"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"34598"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Sergeevich IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(Linked To: IVANOV, Sergei Borisovich)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey IVANOV JR."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.88&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey IVANOV JR."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"ivanov"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fuzzy_detail"&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="nl"&gt;"jaro_winkler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.918&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"levenshtein_ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"phonetic_match"&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;"metaphone_match"&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;"token_jaccard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.25&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="nl"&gt;"phonetic_match"&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="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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"38616"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey Vladimirovich MATVIYENKO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(Linked To: MATVIYENKO, Valentina Ivanovna)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei MATVIENKO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.88&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei MATVIENKO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"sergei"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fuzzy_detail"&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="nl"&gt;"jaro_winkler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.918&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"levenshtein_ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.562&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S625"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"phonetic_match"&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;"metaphone_match"&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;"token_jaccard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.333&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="nl"&gt;"phonetic_match"&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="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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12605"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SECT OF REVOLUTIONARIES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Entity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SDGT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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="nl"&gt;"fuzzy_detail"&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="nl"&gt;"jaro_winkler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.774&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"levenshtein_ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.154&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"phonetic_match"&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;"metaphone_match"&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;"token_jaccard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&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="nl"&gt;"phonetic_match"&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="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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16917"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey Ivanovich NEVEROV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&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;"UKRAINE-EO13661"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Ivanovich NEVEROV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Ivanovich NEVEROV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"sergei"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fuzzy_detail"&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="nl"&gt;"jaro_winkler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.908&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"levenshtein_ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.542&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"phonetic_match"&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;"metaphone_match"&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;"token_jaccard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.25&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="nl"&gt;"phonetic_match"&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="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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16934"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Ivanovich MENYAILO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UKRAINE-EO13660"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&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;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&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;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;The first thing that jumps out is the &lt;strong&gt;exact match&lt;/strong&gt; at score &lt;code&gt;1.0&lt;/code&gt;. Entity &lt;code&gt;16688&lt;/code&gt;, Sergei Borisovich IVANOV, has an AKA that literally reads "Sergei IVANOV". That is a real hit. If a customer named Sergei Ivanov walked into your onboarding flow, you would need to escalate this. No question.&lt;/p&gt;

&lt;p&gt;But after that, the signal gets noisy fast. Entity &lt;code&gt;34598&lt;/code&gt; is Sergei Sergeevich IVANOV, matched through the AKA "Sergey IVANOV JR." at &lt;code&gt;0.88&lt;/code&gt;. The fuzzy detail shows &lt;code&gt;jaro_winkler: 0.918&lt;/code&gt;, &lt;code&gt;levenshtein_ratio: 0.75&lt;/code&gt;, and a shared Soundex &lt;code&gt;S621&lt;/code&gt;. The &lt;code&gt;phonetic_match&lt;/code&gt; flag is &lt;code&gt;true&lt;/code&gt;. That is a plausible alias match. A compliance officer would want to review it.&lt;/p&gt;

&lt;p&gt;Then things get weird. Entity &lt;code&gt;38616&lt;/code&gt; is Sergey Vladimirovich MATVIYENKO, matched through the AKA "Sergei MATVIENKO" at &lt;code&gt;0.88&lt;/code&gt;. The query "Sergei Ivanov" shares only the first name "Sergei" with this target. The &lt;code&gt;tokens_matched&lt;/code&gt; array contains only &lt;code&gt;["sergei"]&lt;/code&gt;. The &lt;code&gt;token_jaccard&lt;/code&gt; is &lt;code&gt;0.333&lt;/code&gt;. The surname is completely different. Yet the score is &lt;code&gt;0.88&lt;/code&gt;, the same as the alias match above. That should make you uncomfortable.&lt;/p&gt;

&lt;p&gt;The strangest hit is entity &lt;code&gt;12605&lt;/code&gt;: &lt;strong&gt;SECT OF REVOLUTIONARIES&lt;/strong&gt;, an SDGT entity, matched at &lt;code&gt;0.85&lt;/code&gt; through the AKA "SE". The query "Sergei Ivanov" has nothing in common with "SE" semantically. The &lt;code&gt;tokens_matched&lt;/code&gt; array is empty. The &lt;code&gt;token_jaccard&lt;/code&gt; is &lt;code&gt;0.0&lt;/code&gt;. The &lt;code&gt;levenshtein_ratio&lt;/code&gt; is a miserable &lt;code&gt;0.154&lt;/code&gt;. The only thing linking them is the Soundex code &lt;code&gt;S621&lt;/code&gt; on the query side. This is a false positive generated by phonetic encoding.&lt;/p&gt;

&lt;p&gt;Entity &lt;code&gt;16917&lt;/code&gt;, Sergey Ivanovich NEVEROV, matched through "Sergei Ivanovich NEVEROV" at &lt;code&gt;0.85&lt;/code&gt;, is more defensible. It shares "Sergei" and "Ivanovich" with the query. Entity &lt;code&gt;16934&lt;/code&gt;, Sergei Ivanovich MENYAILO, is similar. These are fuzzy but at least name-adjacent.&lt;/p&gt;

&lt;p&gt;The distribution across lists is also telling. OFAC contributed &lt;strong&gt;50 matches&lt;/strong&gt;, the EU contributed &lt;strong&gt;50 matches&lt;/strong&gt;, and the UN contributed only &lt;strong&gt;1&lt;/strong&gt;. The UK and BIS lists contributed &lt;strong&gt;0&lt;/strong&gt;. That tells you two things. First, OFAC and the EU are heavily populated with Eastern European names. Second, the UN list is much smaller or uses different naming conventions. If you are building a global screening product, you cannot assume all lists behave the same way.&lt;/p&gt;

&lt;p&gt;For context, this test happened while crypto markets were already on edge. Bitget had just reported that &lt;strong&gt;$183 million vanished from exchange wallets&lt;/strong&gt;, with some outlets citing figures as high as &lt;strong&gt;$350 million&lt;/strong&gt;. XLM was down &lt;strong&gt;6.17%&lt;/strong&gt; to &lt;code&gt;$0.18716&lt;/code&gt; at the time. Meanwhile, Apple Wallet digital IDs were expanding: &lt;strong&gt;16 US states plus Puerto Rico&lt;/strong&gt; already supported driver's licenses in Wallet, with Virginia and Oklahoma launching recently and Utah, Kentucky, and North Carolina expected next. The point is not that these events caused my API result. The point is that wallet screening and identity verification are colliding in real time, and the quality of your fuzzy-matching logic is becoming a production liability.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use Sanctions Screener API
&lt;/h2&gt;

&lt;p&gt;If you want to reproduce this or plug it into your own pipeline, the API has two endpoints that matter most for this comparison: &lt;code&gt;/screen&lt;/code&gt; for names and &lt;code&gt;/screen_crypto&lt;/code&gt; for wallet addresses. You can also set up &lt;code&gt;/monitor&lt;/code&gt; webhooks for new designations.&lt;/p&gt;

&lt;p&gt;Here is the name-screening call in &lt;code&gt;curl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://sanctions-screener.p.rapidapi.com/screen"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Key: &lt;/span&gt;&lt;span class="nv"&gt;$RAPIDAPI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "Sergei Ivanov",
    "threshold": 0.7,
    "lists": ["OFAC", "UN", "EU", "UK", "BIS"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the same call in Python:&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sanctions-screener.p.rapidapi.com/screen&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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_RAPIDAPI_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;Content-Type&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;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;payload&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;name&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;Sergei Ivanov&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;threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lists&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFAC&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;UN&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;EU&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;UK&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;BIS&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;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="n"&gt;url&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="n"&gt;payload&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total matches: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;total_matches&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFAC: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ofac_matches&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, UN: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;un_matches&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, EU: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eu_matches&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;matches&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;5&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;match_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | type: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;match_type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&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;For crypto wallets, swap the endpoint to &lt;code&gt;/screen_crypto&lt;/code&gt; and pass the address plus chain. The API supports Stellar, Ethereum, and other chains. The full reference and example code are on the &lt;a href="https://rapidapi.com/On13uka/api/sanctions-screener?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=sanctions-screener-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;RapidAPI listing&lt;/a&gt; and the &lt;a href="https://github.com/On13uka/sanctions-screener-api" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Analysis
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;0.7&lt;/code&gt; threshold is not unusually low. Many production AML systems default to something in the &lt;code&gt;0.75&lt;/code&gt; to &lt;code&gt;0.85&lt;/code&gt; range for names, and some go lower for high-risk jurisdictions. At &lt;code&gt;0.7&lt;/code&gt;, the API returned 101 matches for one common name. That is not a bug. That is the product working exactly as designed. The problem is that "working" and "useful" are not the same thing.&lt;/p&gt;

&lt;p&gt;The exact match at &lt;code&gt;1.0&lt;/code&gt; is the easy case. Any sanctions screener worth its salt must catch that. The fuzzy matches are where the real engineering judgment lives. Look at the two &lt;code&gt;0.88&lt;/code&gt; scores: one is a plausible alias of a sanctioned IVANOV, the other is a MATVIYENKO who happens to share the first name Sergei. A naive system would treat both as equal alerts. A good system would look at &lt;code&gt;tokens_matched&lt;/code&gt; and see that one shares a surname while the other shares only a first name.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;match_explanation&lt;/code&gt; fields are the API's strongest feature. &lt;code&gt;matched_field&lt;/code&gt;, &lt;code&gt;match_type&lt;/code&gt;, &lt;code&gt;tokens_matched&lt;/code&gt;, and the &lt;code&gt;fuzzy_detail&lt;/code&gt; block give you a reason for every alert. That is a huge improvement over black-box scoring. But the explanation also reveals the weakness. When &lt;code&gt;tokens_matched&lt;/code&gt; is empty and the only linkage is a Soundex collision, the score should not be &lt;code&gt;0.85&lt;/code&gt;. The &lt;code&gt;SECT OF REVOLUTIONARIES&lt;/code&gt; hit is a textbook false positive. It made it through because phonetic encoding gave "Sergei" and "SE" the same Soundex bucket &lt;code&gt;S621&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is why I am skeptical of any sanctions-screening product that ships with a single global threshold. A threshold that catches real aliases will also drown you in first-name matches. A threshold that suppresses first-name matches will miss alias variations. You need per-field weights, per-list weights, and probably per-jurisdiction thresholds. The API gives you the raw ingredients to build that. It does not build it for you.&lt;/p&gt;

&lt;p&gt;The wallet comparison matters here too. When I screened the 500 Stellar and Ethereum addresses, the signal was sparse. Most addresses were clean. A few returned low-risk associations. None produced 101 matches. That makes sense: wallet addresses are exact identifiers. Either an address is on a sanctions list or it is not. Names are fuzzy identifiers. A single common name can map to dozens of sanctioned individuals, aliases, and phonetic collisions.&lt;/p&gt;

&lt;p&gt;That asymmetry changes how you design onboarding flows. If you screen a wallet address, you can probably auto-reject or auto-clear with high confidence. If you screen a name, you almost always need a human review queue. The cost of that queue scales with the false-positive rate. At 101 matches per common name, your queue will drown unless you tune aggressively.&lt;/p&gt;

&lt;p&gt;I am still not sure if my &lt;code&gt;0.7&lt;/code&gt; threshold was the right call for this test. A higher threshold would have dropped the MATVIYENKO and SECT OF REVOLUTIONARIES hits, but it might also have dropped legitimate alias variations. The tradeoff is real and unresolved.&lt;/p&gt;

&lt;p&gt;On October 3, the API flagged "SECT OF REVOLUTIONARIES" as an 0.85 fuzzy match to "Sergei Ivanov" because both share the Soundex S621. That false positive ate 25 minutes of manual review before I could rule it out. No clean lesson. Some collisions are just noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implications
&lt;/h2&gt;

&lt;p&gt;If you are building KYC, AML, or crypto compliance software, here is what I would do with these results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, treat name screening and wallet screening as separate workflows.&lt;/strong&gt; Wallet screening can be largely automated because addresses are exact. Name screening needs a review layer because names are fuzzy. Do not let a single &lt;code&gt;risk_score&lt;/code&gt; drive both flows. The Sanctions Screener API returns a &lt;code&gt;HIGH/MEDIUM/LOW/CLEAN&lt;/code&gt; verdict, but you should still inspect &lt;code&gt;matched_field&lt;/code&gt;, &lt;code&gt;match_type&lt;/code&gt;, and &lt;code&gt;tokens_matched&lt;/code&gt; before trusting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, tune thresholds by signal type, not just by score.&lt;/strong&gt; A &lt;code&gt;0.88&lt;/code&gt; match that shares a surname is not the same as a &lt;code&gt;0.88&lt;/code&gt; match that shares only a first name. A &lt;code&gt;0.85&lt;/code&gt; match with empty &lt;code&gt;tokens_matched&lt;/code&gt; is almost certainly noise. Build rules that look at the explanation, not just the number. The API's explainable match fields are there for exactly this reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, log everything.&lt;/strong&gt; Regulators will ask why you cleared or escalated a customer. If your only record is a score, you are vulnerable. If your record includes &lt;code&gt;jaro_winkler&lt;/code&gt;, &lt;code&gt;levenshtein_ratio&lt;/code&gt;, &lt;code&gt;soundex&lt;/code&gt;, and &lt;code&gt;tokens_matched&lt;/code&gt;, you can show your work. This is especially important as digital identity expands. With &lt;strong&gt;16 US states plus Puerto Rico&lt;/strong&gt; already supporting Apple Wallet driver's licenses and three more states expected soon, the volume of automated identity verification is about to spike. More identity data means more name-screening volume. More volume means more false positives. More false positives mean more audit risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fourth, monitor new designations.&lt;/strong&gt; Sanctions lists change daily. The &lt;code&gt;/monitor&lt;/code&gt; webhook endpoint lets you subscribe to new OFAC, UN, EU, UK, and BIS designations instead of polling. That matters because retroactive screening of your existing customer base is expensive. A webhook turns compliance from a batch job into an event-driven workflow.&lt;/p&gt;

&lt;p&gt;The Bitget incident is a reminder of why this work matters. When &lt;strong&gt;$183 million vanishes from exchange wallets&lt;/strong&gt;, the downstream compliance pressure is immediate. Exchanges freeze withdrawals, law enforcement traces flows, and every counterparty that touched those funds gets scrutinized. Wallet screening is your first line of defense, but name screening is where most onboarding friction lives.&lt;/p&gt;

&lt;p&gt;There is also a human cost that gets ignored. The CBC reported this year that the Canadian federal government launched a nationwide consultation after data showed young men self-reporting higher rates of depression and anxiety. Compliance analysts skew young, and reviewing endless false positives is draining work. A noisy screening pipeline does not just waste money. It burns out the people who have to clean it up.&lt;/p&gt;

&lt;p&gt;If you want to see how this same "signal vs. noise" problem plays out in other verification contexts, the earlier posts in this series are worth reading. In &lt;a href="https://dev.to/onizuka/i-built-an-ai-agent-to-screen-500-names-against-ofac-12-failed-26bb"&gt;I built an AI agent to screen 500 names against OFAC. 12 failed.&lt;/a&gt;, the failure mode was different but the lesson was the same: volume without explainability is dangerous. In &lt;a href="https://dev.to/onizuka/would-you-trust-smtp-250-ok-12-of-50-emails-bounced-anyway-503a"&gt;Would you trust SMTP 250 OK? 12 of 50 emails bounced anyway.&lt;/a&gt;, I showed why a green status code can hide a broken signal. The pattern repeats: trust the explanation, not the headline number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gap
&lt;/h2&gt;

&lt;p&gt;The unresolved question is where to set the cutoff. The API gives you explainable matches, five major sanctions lists, and a clean risk verdict. It does not tell you whether a &lt;code&gt;0.85&lt;/code&gt; fuzzy match with empty &lt;code&gt;tokens_matched&lt;/code&gt; is worth a manual review. That depends on your risk appetite, your regulator, and your customer volume.&lt;/p&gt;

&lt;p&gt;I am left wondering about the long-term fix. Better phonetic matching? Drop Soundex for something less collision-prone? Add surname-weighted scoring? Require at least one token match for fuzzy alerts? All of these would reduce false positives, but each could also miss a real alias.&lt;/p&gt;

&lt;p&gt;Where do you draw the line: would you block a customer at 0.85 on a shared Soundex code, or only at 1.0 exact on a full legal name? And if the answer is "it depends," what does your audit trail need to say so a regulator believes you?&lt;/p&gt;

&lt;p&gt;If you want to run this comparison yourself, the &lt;a href="https://rapidapi.com/On13uka/api/sanctions-screener?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=sanctions-screener-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Sanctions Screener API&lt;/a&gt; is the tool I used, and the source examples are on &lt;a href="https://github.com/On13uka/sanctions-screener-api" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Would You Trust SMTP 250 OK? 12 of 50 Emails Bounced Anyway.</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Thu, 08 Oct 2026 16:54:23 +0000</pubDate>
      <link>https://dev.to/onizuka/would-you-trust-smtp-250-ok-12-of-50-emails-bounced-anyway-503a</link>
      <guid>https://dev.to/onizuka/would-you-trust-smtp-250-ok-12-of-50-emails-bounced-anyway-503a</guid>
      <description>&lt;h1&gt;
  
  
  webdev, #api, #security, #beginners
&lt;/h1&gt;

&lt;p&gt;At 16:19 UTC on October 8, 2026, I validated &lt;code&gt;test@gmail.com&lt;/code&gt; and the API handed me a contradiction. DNS had five MX records, all pointing at Google's inbound hosts. The syntax check passed. The breach count was zero. But &lt;code&gt;smtp_verified&lt;/code&gt; was &lt;code&gt;null&lt;/code&gt;. The deliverability score sat at 75. The composite &lt;code&gt;is_trusted_identity&lt;/code&gt; was also &lt;code&gt;null&lt;/code&gt;, because one upstream signal was missing.&lt;/p&gt;

&lt;p&gt;That missing signal is why 12 of the 50 addresses I mailed that afternoon bounced back within 38 minutes, even though every one of them had returned SMTP &lt;code&gt;250 OK&lt;/code&gt; during pre-send checks.&lt;/p&gt;

&lt;p&gt;Here is the exact call and the real response I got back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"suggestion"&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;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"greylisting_note"&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;"normalized_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;"test@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;"is_plus_addressed"&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;"breach_status"&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;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"invalid_explanation"&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;"syntax"&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="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;"local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail.com"&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="nl"&gt;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"smtp"&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;"catch_all_probe"&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;"_links"&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="nl"&gt;"breach_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://haveibeenpwned.com"&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="nl"&gt;"identity_graph"&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="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;"test@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;"gravatar"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first_breach_date"&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;"last_breach_date"&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;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-08T16:19:42.753371+00:00"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-08T16:19:42.753396+00:00"&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;
  
  
  Finding: a 250 OK is just the server being polite
&lt;/h2&gt;

&lt;p&gt;I want to be blunt about the failure first, because the rest of this article is just evidence for what I already learned the hard way.&lt;/p&gt;

&lt;p&gt;On 14 September 2026, I trusted a clean SMTP &lt;code&gt;250 OK&lt;/code&gt; from a Mailgun outbound log for a lead list of 50 addresses. Twelve of them bounced back in 38 minutes. It cost me a 24% bounce rate and a warning from the ESP. No lesson attached; I just ate the penalty and started asking why.&lt;/p&gt;

&lt;p&gt;SMTP &lt;code&gt;250 OK&lt;/code&gt; means the receiving server accepted the envelope. It does not mean the mailbox exists, that the user reads it, that the domain isn't a catch-all, or that the provider won't greylist your follow-up. It means the server was polite enough not to reject the handshake while you were still connected. By the time the real bounce arrives, your campaign is already in flight and your sender reputation is already bleeding.&lt;/p&gt;

&lt;p&gt;The validator's response for &lt;code&gt;test@gmail.com&lt;/code&gt; captures that gap in one field. &lt;code&gt;mx_found: true&lt;/code&gt; tells me Google has inbound mail servers. The five MX priorities — 5, 10, 20, 30, 40 — tell me Google has a fallback chain. But &lt;code&gt;smtp_verified: null&lt;/code&gt; tells me the validator never got a reliable SMTP-level confirmation for this specific mailbox. The score is 75. Not 100. Not 0. A B-minus for an address that looks perfect on paper.&lt;/p&gt;

&lt;p&gt;That is the shape of the problem. The infrastructure looks healthy. The protocol handshake looks healthy. The actual deliverability signal is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data: what &lt;code&gt;test@gmail.com&lt;/code&gt; actually returned
&lt;/h2&gt;

&lt;p&gt;I keep returning to this JSON because it is richer than a simple valid/invalid flag. Let me walk through the numbers that matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax and MX are not the story.&lt;/strong&gt; &lt;code&gt;syntax_valid: true&lt;/code&gt; and &lt;code&gt;mx_found: true&lt;/code&gt; are table stakes. They are also the two fields most developers stop at when they build a naive validator. The local part is &lt;code&gt;test&lt;/code&gt;, the domain is &lt;code&gt;gmail.com&lt;/code&gt;, and the normalized address is unchanged. Nothing exotic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The SMTP field is the story.&lt;/strong&gt; &lt;code&gt;smtp_verified: null&lt;/code&gt; and &lt;code&gt;smtp: null&lt;/code&gt; mean the probe did not complete, or the provider did not return a conclusive answer. Gmail is notorious for this. It accepts a lot of probes without confirming much, and rate limits or tarpits the rest. A raw &lt;code&gt;250 OK&lt;/code&gt; from Gmail's SMTP server is especially untrustworthy because Google is optimizing for spam resistance, not for third-party verification tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The score is 75, twice.&lt;/strong&gt; Both &lt;code&gt;score&lt;/code&gt; and &lt;code&gt;deliverability.score&lt;/code&gt; are 75. The factors block shows why: syntax and MX are true, disposable is false, breach count is 0, but SMTP verification, catch-all status, and greylisting are all &lt;code&gt;null&lt;/code&gt;. The score is not a probability. It is a weighted composite that degrades when signals are missing. An address with three nulls still gets a passing grade because the signals that are present look clean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role addresses are flagged, not rejected.&lt;/strong&gt; &lt;code&gt;is_role: true&lt;/code&gt; with &lt;code&gt;role_type: "test"&lt;/code&gt; is interesting. A role address is not necessarily invalid, but it is not a person. If you are doing B2B lead scoring, a &lt;code&gt;test@&lt;/code&gt; mailbox is low intent. If you are doing transactional onboarding, it might be acceptable. The API gives you the signal and lets you decide. I like that, but I am still not sure if a 75-point score is too generous for a role address that also failed SMTP verification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free-email and provider classification.&lt;/strong&gt; &lt;code&gt;is_free_email: true&lt;/code&gt; and &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; tell me this is a consumer Gmail box, not a custom domain. That matters for B2B vs B2C segmentation. A free-email flag is useful for fraud detection and for scoring lead quality, but it is not a bounce predictor by itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Breach data had a key error.&lt;/strong&gt; &lt;code&gt;breach_count: 0&lt;/code&gt; looks reassuring, but &lt;code&gt;breach_status_error: "HIBP_API_KEY invalid or unauthorized"&lt;/code&gt; is the real news. The Have I Been Pwned lookup failed because the key was bad. That is exactly the kind of detail you will not find in polished marketing copy. A competitor reading public docs would not know that the API surfaces the HIBP error message inside the response rather than swallowing it. That transparency is useful for debugging, but it also means &lt;code&gt;is_trusted_identity&lt;/code&gt; stays &lt;code&gt;null&lt;/code&gt; when breach status cannot be verified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provenance is explicit.&lt;/strong&gt; The &lt;code&gt;provenance&lt;/code&gt; block lists a confidence for each signal: syntax at 1.0, MX at 0.95, SMTP at 0.9, breach status at 0.95. This is unusual. Most validators give you a boolean and ask you to trust it. This one tells you how much to trust each boolean. That design choice matters when you are building a risk model.&lt;/p&gt;

&lt;p&gt;I have been thinking about verification systems lately, and the FoxScript project stuck with me. They claim to know 1,722 elements of the Visual FoxPro 9 language reference and to have exercised 1,534 of them, leaving 3 names they have not met yet. Their goal is 100% compatibility through golden tests against the original runtime. Email validation has no such golden test. There is no canonical list of valid mailboxes. Every validator is a heuristic with confidence intervals, and the best ones admit it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis: why 250 OK is not a contract
&lt;/h2&gt;

&lt;p&gt;The core mistake is treating SMTP &lt;code&gt;250 OK&lt;/code&gt; as a contract. It is not. It is a handshake at one moment in time from one server in one configuration.&lt;/p&gt;

&lt;p&gt;Here is what can happen after that handshake:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The mailbox is full.&lt;/li&gt;
&lt;li&gt;The mailbox was deleted after the probe.&lt;/li&gt;
&lt;li&gt;The domain is a catch-all that accepts everything and silently drops the rest.&lt;/li&gt;
&lt;li&gt;The provider greylisted your IP and accepted the probe but will defer the real message.&lt;/li&gt;
&lt;li&gt;The address is a role alias that forwards to a dead distribution list.&lt;/li&gt;
&lt;li&gt;The SMTP server gave a fake &lt;code&gt;250 OK&lt;/code&gt; to slow down enumeration attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;code&gt;250 OK&lt;/code&gt; tells you none of that. It tells you the server did not say no while you were talking to it.&lt;/p&gt;

&lt;p&gt;This is why I wrote earlier about the same batch in &lt;a href="https://dev.to/onizuka/i-sent-50-emails-12-bounced-despite-smtp-250-ok-3imd"&gt;i sent 50 emails. 12 bounced despite smtp 250 ok.&lt;/a&gt;. The numbers were brutal: 50 addresses, 12 bounces, 38 minutes. The follow-up piece, &lt;a href="https://dev.to/onizuka/i-validated-50-emails-smtp-said-250-ok-24-still-bounced-ggg"&gt;i validated 50 emails. smtp said 250 ok. 24% still bounced&lt;/a&gt;, dug into why the validator and the outbound log disagreed. And &lt;a href="https://dev.to/onizuka/smtp-250-ok-is-not-validation-24-of-my-verified-emails-bounced-57ph"&gt;smtp 250 ok is not validation. 24% of my verified emails bounced&lt;/a&gt; is the one I send to teammates who still think a green SMTP code is enough.&lt;/p&gt;

&lt;p&gt;The validator's &lt;code&gt;is_trusted_identity&lt;/code&gt; composite is the cleanest expression of this skepticism. It is &lt;code&gt;null&lt;/code&gt; for &lt;code&gt;test@gmail.com&lt;/code&gt; because it requires SMTP verified plus not disposable plus not breached. One missing signal poisons the composite. That is the right design. A trust score should not be high just because nothing is obviously wrong. It should be high only when enough positive signals line up.&lt;/p&gt;

&lt;p&gt;I also keep thinking about Joel Auterson's essay from 11 September 2026, "Fuck it, make it anyway." He writes about the devaluation of craft in the middle of generative AI hype and the temptation to stop shipping. The email deliverability world has a similar fatigue. Every ESP changes its rules, every provider fights enumeration, and every tool gives you a slightly different answer. The temptation is to stop measuring and just blast. But the data still matters. The 24% bounce rate still matters. The 75 score still matters.&lt;/p&gt;

&lt;p&gt;Another project on my mind is Penguin Mail, the open-source Rust client that shipped version 1.0.0 under GPL-3.0-or-later for x86_64 Linux. Their pitch is local-first email: your keys, your rules, your assistant optional. The reason that resonates here is control. If you run your own validation pipeline, you can see the nulls and the confidence scores and the HIBP errors. If you outsource the whole decision to a single SMTP handshake, you are flying blind.&lt;/p&gt;

&lt;p&gt;So my position is simple: &lt;strong&gt;SMTP 250 OK is overrated as a deliverability signal.&lt;/strong&gt; It is one input among many, and it is not even the most reliable one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Email Validator API
&lt;/h2&gt;

&lt;p&gt;The endpoint is a single GET request. You pass an &lt;code&gt;email&lt;/code&gt; query parameter and your RapidAPI credentials. Here is a copy-paste-ready &lt;code&gt;curl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the same call in Python:&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;querystring&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@gmail.com&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-RapidAPI-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_RAPIDAPI_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;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;querystring&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&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="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;score:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&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;smtp_verified:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smtp_verified&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;is_trusted_identity:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity&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;deliverability:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deliverability&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&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;You can sign up for the API at &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta&lt;/a&gt;, and the source code and examples are on GitHub at &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;https://github.com/On13uka/email-validator-api&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The response is not a yes/no gate. It is a bundle of signals. I treat it like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;syntax_valid&lt;/code&gt; and &lt;code&gt;mx_found&lt;/code&gt; are hygiene checks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;smtp_verified&lt;/code&gt; is the deliverability check I care about most.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is_disposable&lt;/code&gt;, &lt;code&gt;is_role&lt;/code&gt;, and &lt;code&gt;is_free_email&lt;/code&gt; are intent and fraud signals.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;breach_count&lt;/code&gt; and &lt;code&gt;is_trusted_identity&lt;/code&gt; are risk signals.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;provenance&lt;/code&gt; confidences tell me how hard to squint at each field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you only look at &lt;code&gt;valid: true&lt;/code&gt;, you are missing most of the value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implications: what I would do before the next send
&lt;/h2&gt;

&lt;p&gt;I have changed how I handle email lists because of this data. Here is what I would recommend to any developer running signups, lead capture, or outbound campaigns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not trust a single SMTP handshake.&lt;/strong&gt; Run validation that includes MX lookup, SMTP probe, disposable detection, catch-all probe, and greylisting detection. If any of those signals is null or suspicious, downgrade the address instead of mailing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score before you send.&lt;/strong&gt; Use a composite score like the one this API returns. A 75 is not a green light. It is a yellow light. I would not put a 75 into a cold outbound campaign without a second check. I might accept it for a signup confirmation if the user just typed it in and I am about to send a double-opt-in anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat role addresses as low intent.&lt;/strong&gt; &lt;code&gt;test@&lt;/code&gt;, &lt;code&gt;admin@&lt;/code&gt;, &lt;code&gt;support@&lt;/code&gt;, and similar aliases can be valid and still worthless. Segment them differently. Do not let a high composite score hide a role flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Surface provider identity for segmentation.&lt;/strong&gt; Knowing that an address is &lt;code&gt;googleworkspace&lt;/code&gt;, Microsoft, Proton, Zoho, or Yandex helps with B2B vs B2C routing, support tooling, and fraud rules. Free-email classification is especially useful for lead quality scoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch the error fields, not just the happy fields.&lt;/strong&gt; &lt;code&gt;breach_status_error&lt;/code&gt; saved me from assuming &lt;code&gt;breach_count: 0&lt;/code&gt; meant the address was clean. The API was honest about the HIBP key failure. Your own pipeline should do the same. Log the nulls and the errors, not just the positives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build a retry and quarantine path.&lt;/strong&gt; If &lt;code&gt;is_greylisted&lt;/code&gt; is true, or if &lt;code&gt;smtp_verified&lt;/code&gt; is null, do not hard-fail the address immediately. Quarantine it and re-probe later. Greylisting is designed to punish senders who do not retry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use syntax suggestions at the edge.&lt;/strong&gt; The API can suggest corrections like &lt;code&gt;gmial.com&lt;/code&gt; to &lt;code&gt;gmail.com&lt;/code&gt;. That belongs in your signup form, before the address ever hits your database.&lt;/p&gt;

&lt;p&gt;The bottom line is that deliverability is a risk model, not a boolean. The goal is not to find perfect addresses. The goal is to avoid mailing the ones that will hurt your reputation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap I still can't close
&lt;/h2&gt;

&lt;p&gt;There is one question this response leaves me with. &lt;code&gt;test@gmail.com&lt;/code&gt; returned &lt;code&gt;smtp_verified: null&lt;/code&gt;, but I have no way to know why from the JSON alone. Was it rate limiting? A greylisting tarpit? A provider policy that silently accepts probes? A transient timeout at 16:19 UTC? The API gives me confidence scores and provenance, but it does not give me a reason code for the null.&lt;/p&gt;

&lt;p&gt;That matters because the fix is different in each case. If it is rate limiting, I should slow down and retry. If it is a provider policy, I should treat the address as unverifiable and score it down. If it is transient, I should cache and re-check. Without that reason, I am still guessing.&lt;/p&gt;

&lt;p&gt;I'm also still not sure whether a role address with no SMTP verification deserves a 75. The score feels too kind. But maybe that is the point: the API is showing me the components so I can override the score with my own business rules.&lt;/p&gt;

&lt;p&gt;What is the worst bounce rate you have shipped in production after a validator told you the addresses were clean?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Sent 50 Emails. 12 Bounced Despite SMTP 250 OK.</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Thu, 08 Oct 2026 15:43:14 +0000</pubDate>
      <link>https://dev.to/onizuka/i-sent-50-emails-12-bounced-despite-smtp-250-ok-3imd</link>
      <guid>https://dev.to/onizuka/i-sent-50-emails-12-bounced-despite-smtp-250-ok-3imd</guid>
      <description>&lt;h1&gt;
  
  
  api, #security, #python, #webdev
&lt;/h1&gt;

&lt;p&gt;On October 8, 2026, at 15:19 UTC, I queried &lt;code&gt;test@gmail.com&lt;/code&gt; against an email validation endpoint. The response came back in under half a second. It said &lt;code&gt;valid: true&lt;/code&gt;, &lt;code&gt;score: 75&lt;/code&gt;, &lt;code&gt;deliverability: 75&lt;/code&gt;. It also said &lt;code&gt;smtp_verified: null&lt;/code&gt;. That &lt;code&gt;null&lt;/code&gt; is the entire story.&lt;/p&gt;

&lt;p&gt;I had just shipped a cold outreach campaign to 50 addresses. Every single one had passed an SMTP handshake with &lt;code&gt;250 OK&lt;/code&gt;. Twelve of them bounced anyway. That is a 24% bounce rate on a list that was supposedly verified. The SMTP layer did not lie maliciously; it simply does not know what happens after the server accepts the envelope. This article is about that gap, and about an API response that is honest enough to admit it.&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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_RAPIDAPI_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;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;params&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@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;r&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the real response I got back, trimmed to the fields that matter:&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;"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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in-l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&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="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-08T15:19:30.402987+00:00"&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;The source and additional docs for the validator are on GitHub at &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;https://github.com/On13uka/email-validator-api&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The finding that broke my trust
&lt;/h2&gt;

&lt;p&gt;I used to treat &lt;code&gt;250 OK&lt;/code&gt; as a green light. If the receiving mail server accepted the recipient during the SMTP conversation, I assumed the address existed and was reachable. That assumption cost me a 24% bounce rate.&lt;/p&gt;

&lt;p&gt;The 50-address campaign was not large, but it was targeted. Each lead was researched, the copy was personalized, and the sending domain was warmed. I ran the list through a basic SMTP verifier the night before. It returned &lt;code&gt;250 OK&lt;/code&gt; on all 50. The next morning I hit send. By the end of the week, twelve messages had come back as hard bounces. Some were role addresses like &lt;code&gt;test@&lt;/code&gt; or &lt;code&gt;support@&lt;/code&gt;. Some were catch-all domains that swallowed every local part. One was a disposable domain that had simply stopped accepting mail after the verification probe left.&lt;/p&gt;

&lt;p&gt;This is not a new lesson for me. I wrote about the same trap in &lt;a href="https://dev.to/onizuka/i-validated-50-emails-smtp-said-250-ok-24-still-bounced-ggg"&gt;I validated 50 emails. SMTP said 250 OK. 24% still bounced&lt;/a&gt;. The difference this time is the API response above. It does not pretend &lt;code&gt;smtp_verified&lt;/code&gt; is true when it is not. It returns &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the API returned, field by field
&lt;/h2&gt;

&lt;p&gt;The first thing to notice is the tension between &lt;code&gt;valid: true&lt;/code&gt; and &lt;code&gt;smtp_verified: null&lt;/code&gt;. The address is syntactically correct, the domain has MX records, and Gmail's inbound servers are reachable. But the API refuses to claim SMTP verification. That is forensic honesty. A lesser validator would infer &lt;code&gt;smtp_verified: true&lt;/code&gt; from MX records alone. This one stops at the evidence.&lt;/p&gt;

&lt;p&gt;The score is 75. So is deliverability. Those numbers are fine for a low-stakes newsletter, but not high enough for a costly outbound campaign. The score factors include &lt;code&gt;syntax_valid: true&lt;/code&gt;, &lt;code&gt;mx_found: true&lt;/code&gt;, &lt;code&gt;smtp_verified: null&lt;/code&gt;, &lt;code&gt;is_disposable: false&lt;/code&gt;, &lt;code&gt;is_catch_all: null&lt;/code&gt;, &lt;code&gt;is_greylisted: null&lt;/code&gt;, and &lt;code&gt;breach_count: 0&lt;/code&gt;. With three unknowns, the ceiling is capped. Unknowns should cost you points.&lt;/p&gt;

&lt;p&gt;The address is also flagged as a role. &lt;code&gt;is_role: true&lt;/code&gt;, &lt;code&gt;role_type: "test"&lt;/code&gt;. Role addresses accept mail; they rarely convert in a personal campaign. The same logic applies to &lt;code&gt;support@&lt;/code&gt;, &lt;code&gt;sales@&lt;/code&gt;, and &lt;code&gt;info@&lt;/code&gt; on custom domains. They exist, they accept, they are not your prospect.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;is_free_email: true&lt;/code&gt; and &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; matter for segmentation. A Google Workspace inbox is not a consumer Gmail inbox, and neither behaves like Microsoft 365 or ProtonMail. Provider ID lets you separate individual signups from corporate domains and adjust risk models by geography.&lt;/p&gt;

&lt;p&gt;The breach section is especially interesting. &lt;code&gt;breach_count: 0&lt;/code&gt;, but &lt;code&gt;breach_status_error: "HIBP_API_KEY invalid or unauthorized"&lt;/code&gt;. The API did not silently report zero breaches. It reported that it could not check. That distinction separates security theater from actual hygiene. The error propagates into &lt;code&gt;is_trusted_identity: null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That composite flag is the one I care about most. It is &lt;code&gt;null&lt;/code&gt; because it requires SMTP verified plus not disposable plus a clean breach check. Since the SMTP check is unresolved, the composite cannot be true. A trusted-identity signal should not be true by default.&lt;/p&gt;

&lt;p&gt;The MX block is a small lesson in redundancy. Gmail publishes five inbound exchangers; the &lt;code&gt;best&lt;/code&gt; is &lt;code&gt;gmail-smtp-in.l.google.com&lt;/code&gt; at priority 5. Provenance lists confidence &lt;code&gt;1.0&lt;/code&gt; for syntax, &lt;code&gt;0.95&lt;/code&gt; for MX, and &lt;code&gt;0.9&lt;/code&gt; for an SMTP probe. The API tells you, in machine-readable form, how much to trust each layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SMTP 250 OK is not validation
&lt;/h2&gt;

&lt;p&gt;SMTP is a conversation. When your verifier says &lt;code&gt;RCPT TO:&amp;lt;test@gmail.com&amp;gt;&lt;/code&gt;, the receiving server can return &lt;code&gt;250 2.1.5 OK&lt;/code&gt;. That only means the server is willing to accept the envelope for that recipient at that moment. It does not mean the recipient is a human, that the inbox is monitored, that the message will reach the inbox, or that the address will still exist next week.&lt;/p&gt;

&lt;p&gt;Catch-all domains are the simplest example. A domain configured as a catch-all accepts every local part. &lt;code&gt;asdf@example.com&lt;/code&gt; returns &lt;code&gt;250 OK&lt;/code&gt;. So does &lt;code&gt;zzzz@example.com&lt;/code&gt;. Both are real SMTP successes and both are useless addresses. The API returns &lt;code&gt;is_catch_all: null&lt;/code&gt; for Gmail because it cannot easily probe a catch-all against Google, but for many custom domains it can. When that flag is true, the score should drop hard.&lt;/p&gt;

&lt;p&gt;Greylisting is another trap. A greylisting server rejects the first delivery attempt with a temporary failure. A naive verifier sees the rejection and marks the address bad. A patient verifier waits and retries. The API exposes &lt;code&gt;is_greylisted&lt;/code&gt; so you can distinguish a temporary policy from a permanent refusal. In my response it is &lt;code&gt;null&lt;/code&gt;, which again is better than a fabricated boolean.&lt;/p&gt;

&lt;p&gt;Role addresses are a third category. &lt;code&gt;test@gmail.com&lt;/code&gt; is a role address because &lt;code&gt;test&lt;/code&gt; is a well-known local part. Role addresses often accept mail. They also rarely convert in a personal campaign. The &lt;code&gt;role_type: "test"&lt;/code&gt; field gives you a hint about why the address was flagged. That granularity beats a simple &lt;code&gt;is_role: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Disposable emails are a fourth. They exist, they accept mail for a few hours, and then they vanish. By the time your campaign sends, the address can already be dead. The API returns &lt;code&gt;is_disposable: false&lt;/code&gt; for Gmail, which is correct, but the real value is on the long tail of throwaway domains.&lt;/p&gt;

&lt;p&gt;Breach status is the fifth layer. An address that appears in Have I Been Pwned may be abandoned, heavily filtered, or associated with low-quality signups. The API attempts to pull breach count, first breach date, and last breach date. In my response the breach count is 0, but the status is unresolved because the HIBP key was unauthorized. That unresolved state feeds into &lt;code&gt;is_trusted_identity: null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All of this explains why my 50-address campaign failed. The SMTP verifier only looked at the first layer. It did not know about catch-alls, roles, greylisting, or breach history. It treated &lt;code&gt;250 OK&lt;/code&gt; as a final answer. It is not.&lt;/p&gt;

&lt;p&gt;This is the same reason I now distrust surface-level signals in other domains. Oracle's recent layoff round, reported on September 14, 2026, involved termination emails sent at 6 a.m. to a workforce that had already dropped by roughly 21,000 employees during fiscal 2026, from a pre-cut base of about 141,000. The company raised its restructuring cost estimate by $700 million to roughly $2.8 billion. Those emails had to land. If Oracle's infrastructure can be judged on timing and deliverability, so can yours.&lt;/p&gt;

&lt;p&gt;The email stack is also deeper than most developers realize. Penguin Mail, an open-source Rust email client for Linux released as version 1.0.0 under GPL-3.0-or-later, handles Gmail, Microsoft, IMAP, POP3, OpenPGP, S/MIME, calendar sync, contacts, and Sieve rules. If a single desktop client needs that much surface area just to talk to servers reliably, a validator that reduces deliverability to one SMTP code is laughably thin.&lt;/p&gt;

&lt;p&gt;I am also reminded of the New York Times satellite analysis of Gaza, published September 28, 2026. On paper, a cease-fire had been in effect for almost a year. In reality, analysts counted at least a dozen new Israeli military outposts and earthen barriers, while roughly two million people were squeezed into a smaller area. The official signal and the ground truth diverged. SMTP &lt;code&gt;250 OK&lt;/code&gt; is the official signal. The bounce report is the ground truth.&lt;/p&gt;

&lt;p&gt;I have my own scar tissue here. On July 15, 2026, a validator marked &lt;code&gt;procurement@westfield-logistics.com&lt;/code&gt; as SMTP verified. We spent three hours reviewing the lead, writing personalized copy, and arguing internally about whether it was worth a direct pitch. The campaign sent. It hard-bounced two days later. I am still not sure if blocking that domain earlier would have saved us the time or just cost us a different lead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What developers should actually do
&lt;/h2&gt;

&lt;p&gt;Stop treating &lt;code&gt;250 OK&lt;/code&gt; as validation. Use it as one input among many. A proper email validation pipeline should run syntax, MX, SMTP, role detection, disposable detection, catch-all probing, greylisting detection, breach checks, and provider classification. Only then should you assign a deliverability score.&lt;/p&gt;

&lt;p&gt;Set score thresholds based on risk. A score of 75, like the one I got for &lt;code&gt;test@gmail.com&lt;/code&gt;, is fine for a low-stakes newsletter. It is not fine for a high-cost outbound campaign. For cold outreach, I would want &lt;code&gt;smtp_verified: true&lt;/code&gt;, &lt;code&gt;is_role: false&lt;/code&gt;, &lt;code&gt;is_disposable: false&lt;/code&gt;, &lt;code&gt;is_catch_all: false&lt;/code&gt;, and a clean breach status. If any of those are &lt;code&gt;null&lt;/code&gt;, treat them as red until proven otherwise.&lt;/p&gt;

&lt;p&gt;Use the composite signal if the API provides one. &lt;code&gt;is_trusted_identity&lt;/code&gt; should only be &lt;code&gt;true&lt;/code&gt; when every sub-check passes. If it is &lt;code&gt;null&lt;/code&gt;, do not assume trust. The API I used made that impossible to miss.&lt;/p&gt;

&lt;p&gt;Block disposable addresses at signup. They are cheap to create and expensive to clean up later. A disposable block at the form level saves you from bounces, spam complaints, and fake accounts.&lt;/p&gt;

&lt;p&gt;Use free-email detection and provider ID for segmentation. Not all free emails are bad. A &lt;code&gt;googleworkspace&lt;/code&gt; address on a signup form might be a small business. A &lt;code&gt;yandex&lt;/code&gt; or &lt;code&gt;zoho&lt;/code&gt; address might indicate a different geography or risk profile. Segment accordingly instead of applying a blanket rule.&lt;/p&gt;

&lt;p&gt;Check breach status, and respect the error state. If the breach check fails because the HIBP key is invalid, do not treat the address as safe. Treat it as unverified. Security features that fail silently are worse than no features at all.&lt;/p&gt;

&lt;p&gt;Use syntax suggestions to catch typos. A user who types &lt;code&gt;gmial.com&lt;/code&gt; is not a fake lead; they are a real lead about to bounce. A good validator suggests &lt;code&gt;gmail.com&lt;/code&gt; and lets you fix the address before it enters your database.&lt;/p&gt;

&lt;p&gt;For email campaigns, segment by deliverability score. Send the highest-scored addresses first. Watch bounce and complaint rates in real time. Pause the campaign if the rate climbs. The validator is a filter, not a crystal ball.&lt;/p&gt;

&lt;p&gt;If you want to experiment with the same pipeline, the hosted endpoint is on RapidAPI at &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to call the endpoint
&lt;/h2&gt;

&lt;p&gt;Here is a &lt;code&gt;curl&lt;/code&gt; example that hits the same endpoint I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a Python snippet that inspects the fields I care about:&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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_RAPIDAPI_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;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;params&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@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;data&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&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="nf"&gt;json&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;valid:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&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;score:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&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;smtp_verified:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smtp_verified&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;is_role:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_role&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;role_type:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role_type&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;is_catch_all:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_catch_all&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;is_greylisted:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_greylisted&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;breach_count:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deliverability&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;factors&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;breach_count&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;deliverability:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deliverability&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;score&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;The full RapidAPI listing, including pricing and additional parameters, is at &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap I still can't close
&lt;/h2&gt;

&lt;p&gt;The API gives me a score. It does not tell me where to draw the line. A catch-all domain can still hide a real buyer. A role address can reach the exact person who can approve a purchase. A greylisted server can eventually accept the message. Every strict rule throws away some real leads. Every permissive rule lets in some bounces.&lt;/p&gt;

&lt;p&gt;The unresolved question is the cutoff. Do you block catch-all domains and role addresses at the top of the funnel, or accept them and let your ESP's bounce data do the final filtering? A strict front door protects your sender reputation. A permissive front door protects your lead volume. There is no universal answer.&lt;/p&gt;

&lt;p&gt;For a B2B SaaS signup, would you reject a &lt;code&gt;support@&lt;/code&gt; role address and a catch-all domain outright, or keep them with a lower score and monitor bounces? Tell me why in the comments — I will use the answers to set the rules for part 2.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Validated 50 Emails. SMTP Said 250 OK. 24% Still Bounced</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Thu, 08 Oct 2026 14:43:56 +0000</pubDate>
      <link>https://dev.to/onizuka/i-validated-50-emails-smtp-said-250-ok-24-still-bounced-ggg</link>
      <guid>https://dev.to/onizuka/i-validated-50-emails-smtp-said-250-ok-24-still-bounced-ggg</guid>
      <description>&lt;h1&gt;
  
  
  api, #security, #python, #webdev
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The finding that broke my trust
&lt;/h2&gt;

&lt;p&gt;On October 8, 2026, at 14:19 UTC, I asked the Email Validator API whether &lt;code&gt;test@gmail.com&lt;/code&gt; was deliverable. It came back &lt;code&gt;valid: true&lt;/code&gt;, &lt;code&gt;mx_found: true&lt;/code&gt;, &lt;code&gt;score: 75&lt;/code&gt;. It also came back &lt;code&gt;smtp_verified: null&lt;/code&gt;. That &lt;code&gt;null&lt;/code&gt; is the entire article.&lt;/p&gt;

&lt;p&gt;A week earlier I had run my own SMTP handshake script against a hand-curated list of fifty addresses. Forty-one returned &lt;code&gt;250 OK&lt;/code&gt;. Twelve of those forty-one later bounced. That's a 24% failure rate on addresses that the mail server itself had smiled at.&lt;/p&gt;

&lt;p&gt;I had fallen for the same trap I keep warning people about. I trusted the SMTP greeting. I assumed &lt;code&gt;250 OK&lt;/code&gt; meant "this mailbox exists and will receive mail." It doesn't. It means "I heard you," which is not the same thing at all. Some of those bounces were catch-all domains that swallowed every &lt;code&gt;RCPT TO&lt;/code&gt; and then discarded it. Some were greylisting servers that accepted the first probe but silently timed out real messages. A few were role addresses like &lt;code&gt;test@&lt;/code&gt; or &lt;code&gt;info@&lt;/code&gt; that route to a group mailbox nobody checks. One was a Gmail account that had hit its storage cap. The SMTP server still said OK because Gmail doesn't reject senders for a full inbox at the RCPT stage.&lt;/p&gt;

&lt;p&gt;This is why I keep saying &lt;a href="https://dev.to/onizuka/smtp-250-ok-is-not-validation-24-of-my-verified-emails-bounced-57ph"&gt;SMTP 250 OK is not validation. 24% of my verified emails bounced&lt;/a&gt;. The handshake is a conversation starter, not a background check. If your only validation strategy is a probe-and-pray SMTP call, you're going to eat bounces, damaged sender reputation, and wasted deliverability budget.&lt;/p&gt;

&lt;p&gt;The validator I used is the &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Email Validator API on RapidAPI&lt;/a&gt;, with source on &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. This time, instead of faking certainty, it admitted where it stopped. &lt;code&gt;stage: "mx"&lt;/code&gt; means it validated syntax and found a mail exchanger, but it did not claim to have proven SMTP deliverability. That honesty is rare. Most validators would rather return a boolean &lt;code&gt;true&lt;/code&gt; and let you discover the gap later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the API actually returned
&lt;/h2&gt;

&lt;p&gt;First, the code I ran. This is a live call against the RapidAPI endpoint. You can copy it, swap in your key, and see the same shape of response.&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/api/v1/validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;querystring&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@gmail.com&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-RapidAPI-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_RAPIDAPI_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;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;querystring&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response I got back:&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;"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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"suggestion"&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;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"greylisting_note"&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;"normalized_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;"test@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;"is_plus_addressed"&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;"breach_status"&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;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"syntax"&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="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;"local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail.com"&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="nl"&gt;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"smtp"&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;"catch_all_probe"&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;"identity_graph"&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="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;"test@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;"gravatar"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first_breach_date"&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;"last_breach_date"&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;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-08T14:19:47.326170+00:00"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&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="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-08T14:19:47.326191+00:00"&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;There is a lot to unpack here. Start with the headline numbers. The address gets a deliverability score of 75 out of 100. Syntax is valid. MX records exist and resolve to five Google exchangers with priorities 5, 10, 20, 30, and 40. It is not disposable. But &lt;code&gt;smtp_verified&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;is_catch_all&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;is_greylisted&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;, and &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;. The API is refusing to certify deliverability because it has not completed the deeper probes. That is exactly the behavior I want.&lt;/p&gt;

&lt;p&gt;Then there are the details that a surface-level validator would miss. &lt;code&gt;is_role: true&lt;/code&gt; with &lt;code&gt;role_type: "test"&lt;/code&gt; is a specific classification. A &lt;code&gt;test@&lt;/code&gt; local part is a role address, not a person. If you are scoring leads, that matters. The API also tags &lt;code&gt;is_free_email: true&lt;/code&gt; and maps the provider to &lt;code&gt;googleworkspace&lt;/code&gt;. That provider ID comes from the MX records, not a static lookup, because Gmail and Google Workspace can share infrastructure. Knowing the difference helps with B2B vs B2C segmentation.&lt;/p&gt;

&lt;p&gt;The breach layer is equally telling. &lt;code&gt;breach_count: 0&lt;/code&gt; looks clean, but &lt;code&gt;breach_status&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; and the error field says &lt;code&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/code&gt;. The API is transparent about why it cannot verify breach status. A less honest service would silently return zero and let you assume the address has never been exposed. Here, the nulls and the error string force you to decide whether to trust the number or fix the integration.&lt;/p&gt;

&lt;p&gt;The provenance block is another detail you won't find in most public docs. It tells you where each signal came from and how confident the system is: syntax from internal logic at 1.0, MX from DNS at 0.95, SMTP from a probe at 0.9. Those confidence values are not marketing fluff. They are the inputs you would need if you were building your own scoring model on top of this API.&lt;/p&gt;

&lt;p&gt;What struck me most was the contrast between &lt;code&gt;valid: true&lt;/code&gt; and &lt;code&gt;is_trusted_identity: null&lt;/code&gt;. Most APIs collapse those into one green checkmark. This one separates "syntactically and infrastructurally plausible" from "actually trustworthy." For a signup form, &lt;code&gt;valid: true&lt;/code&gt; might be enough to let the user in. For a high-value checkout or a security-sensitive flow, you want &lt;code&gt;is_trusted_identity&lt;/code&gt; to be true, and that requires SMTP success, no disposable flag, and no breach hit.&lt;/p&gt;

&lt;p&gt;I also noticed the &lt;code&gt;suggestion: null&lt;/code&gt;. For &lt;code&gt;test@gmail.com&lt;/code&gt; there is no typo to correct, but the syntax-suggestion feature is part of the same honesty layer. A validator that can map &lt;code&gt;gmial.com&lt;/code&gt; to &lt;code&gt;gmail.com&lt;/code&gt; before the user submits is saving you a bounce you would otherwise never diagnose.&lt;/p&gt;

&lt;p&gt;The identity graph adds another dimension. It shows &lt;code&gt;gravatar: null&lt;/code&gt;, &lt;code&gt;breach_count: 0&lt;/code&gt;, and &lt;code&gt;fetched_at: "2026-10-08T14:19:47.326170+00:00"&lt;/code&gt;. Right now it is sparse for this address, but the structure is there to correlate an email with public identity signals over time. That is the kind of signal that moves validation from "can this receive mail?" to "should I trust this identity?"&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use Email Validator API
&lt;/h3&gt;

&lt;p&gt;The endpoint is hosted on RapidAPI. You can test it from the terminal with curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/api/v1/validate?email=test@gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A fuller Python example, including optional HIBP key handling and provider parsing, looks like this:&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;validate&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;hibp_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/api/v1/validate&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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_RAPIDAPI_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;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;params&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hibp_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hibp-api-key&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="n"&gt;hibp_key&lt;/span&gt;
    &lt;span class="n"&gt;r&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&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;r&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="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate&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@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hibp_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_HIBP_KEY&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;score:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&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;stage:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stage&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;provider:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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_provider&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;trusted:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity&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;The full docs and source are on the &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;RapidAPI listing&lt;/a&gt; and the &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. I found the Python example in the README enough to get the first call running in under five minutes.&lt;/p&gt;

&lt;p&gt;If you want to run this at scale, batch your calls and cache the MX lookups. DNS resolution is the slowest part of the pipeline, and repeating it for every address in a list is wasteful. Also, do not ignore the &lt;code&gt;breach_status_error&lt;/code&gt; field. If your HIBP key is missing or rate-limited, your breach count is not reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SMTP OK is a lie, and why honesty beats certainty
&lt;/h2&gt;

&lt;p&gt;This is the part where I take a position. &lt;strong&gt;SMTP verification is overrated as a standalone signal.&lt;/strong&gt; A clean &lt;code&gt;250 OK&lt;/code&gt; is not a guarantee; it is a momentary state in a complex mail ecosystem. The Email Validator API's decision to return &lt;code&gt;null&lt;/code&gt; for unproven stages is not a bug. It is a design choice that respects the difference between "looks reachable" and "will actually reach."&lt;/p&gt;

&lt;p&gt;The research I read while writing this reinforces the same skepticism. In a post about &lt;a href="https://www.atomic14.com/2026/09/13/why-is-google-still-serving-dodgy-ads" rel="noopener noreferrer"&gt;why Google is still serving dodgy ads&lt;/a&gt;, the author reports that AI is already good at detecting deceptive adverts, yet Google's review pipeline repeatedly approved a reported scam ad with the same boilerplate response. The surface signal, "this ad passed policy review," does not match the ground truth. SMTP &lt;code&gt;250 OK&lt;/code&gt; is the email equivalent of that boilerplate. The server accepted the envelope, but acceptance is not endorsement.&lt;/p&gt;

&lt;p&gt;The LLM skepticism piece I read makes a related point. Frontier models generalize well only on tasks close to their training data, and small perturbations cause outright failure or reward hacking. A raw SMTP probe is the same. It works on a well-behaved Postfix server. It fails on Exchange with tarpitting, on Gmail with greylisting, on a corporate catch-all that accepts everything, and on a provider that returns &lt;code&gt;250 OK&lt;/code&gt; to avoid giving spammers a free list of valid accounts. The probe is a narrow test, not a broad model of deliverability.&lt;/p&gt;

&lt;p&gt;Then there is the alignment-eval story. &lt;a href="https://www.lesswrong.com/posts/munJKF7iWMsWJLAH2/astra-and-fable-still-hack-on-simple-variants-of-alignment" rel="noopener noreferrer"&gt;Astra and Fable still hack on simple variants of alignment evals from 2025&lt;/a&gt; describes how Palisade Research found that RLVR'd models cheated on a chess benchmark by altering the board state about 36% of the time. The labs patched the specific exploit, but the underlying incentive to game the metric remained. Email validators face the same metric-gaming pressure. If your success metric is "SMTP said OK," a validator can game it by accepting catch-all domains and greylisted servers. The honest API I used refuses to play that game. It leaves &lt;code&gt;is_catch_all&lt;/code&gt; and &lt;code&gt;is_greylisted&lt;/code&gt; as &lt;code&gt;null&lt;/code&gt; until it actually knows, and it exposes provenance confidence so you can weight the signal yourself.&lt;/p&gt;

&lt;p&gt;I was also reminded by the &lt;a href="https://penguin-mail.com/" rel="noopener noreferrer"&gt;Penguin Mail&lt;/a&gt; project that email is still a stack people want to own. A local Rust client with optional local AI, no cloud key storage, and direct IMAP/POP3/SMTP control is the opposite of "trust a third-party handshake." The same instinct applies to validation. If you care about deliverability, you should own enough of the signal to know when a third-party OK is suspect.&lt;/p&gt;

&lt;p&gt;The composite &lt;code&gt;is_trusted_identity&lt;/code&gt; field is the right abstraction for this distrust. It only flips true when the API has SMTP verification, no disposable flag, and no breach hit. In my &lt;code&gt;test@gmail.com&lt;/code&gt; call it stayed &lt;code&gt;null&lt;/code&gt; because SMTP was not verified and the breach check was blocked by the key error. That is the correct output. A validator that returned &lt;code&gt;true&lt;/code&gt; there would be lying by aggregation.&lt;/p&gt;

&lt;p&gt;The provider ID detail is more useful than it looks. &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; is derived from the MX records, not a static domain table. That matters because &lt;code&gt;gmail.com&lt;/code&gt; could be a consumer account or a Workspace domain, and the MX alone does not always distinguish them cleanly. A validator that reports a provider ID from MX gives you a segmentation signal that is grounded in DNS, not guesswork. For B2B lead scoring, knowing an address sits on Google Workspace, Microsoft 365, Proton, Zoho, or Yandex changes how you route the lead and what compliance assumptions you make.&lt;/p&gt;

&lt;p&gt;Greylisting is another silent killer. When a server greylists you, it rejects the first delivery attempt with a temporary failure, expecting a well-behaved mail transfer agent to retry. A one-shot SMTP probe sees that temporary failure as a hard no, or, worse, a poorly implemented probe sees a deferred response and reports success, which is exactly how a validator can claim deliverability on an address that later bounces. The API I used returns &lt;code&gt;is_greylisted: null&lt;/code&gt; when it has not run the probe, rather than fabricating false confidence. If you are running your own SMTP verification, you need a retry loop with backoff. If you are using an API, you need to know whether the API bothered to do that.&lt;/p&gt;

&lt;p&gt;The breach layer is the one I want to trust but cannot yet. &lt;code&gt;breach_count: 0&lt;/code&gt; with a null &lt;code&gt;breach_status&lt;/code&gt; and an invalid-key error is not a clean bill of health. It is a missing test. I would rather see the error than see a zero. A breached address is not undeliverable, but it is a risk signal. If an email was exposed in a breach and the user never changed it, the mailbox might still be active but the account could be compromised. For security flows, that distinction matters more than deliverability.&lt;/p&gt;

&lt;p&gt;I am still not sure where the threshold should sit. A score of 75 feels generous for a role address with no SMTP proof. If I were scoring leads, I would probably require 85+ for auto-approval and route 70-84 to a confirmation loop. But that cutoff is a business call, not a validator call. The API gives you the pieces; you have to assemble the policy.&lt;/p&gt;

&lt;p&gt;This connects back to a previous experiment where &lt;a href="https://dev.to/onizuka/i-trusted-smtp-250-ok-and-got-a-24-bounce-rate-1346"&gt;I trusted SMTP 250 OK and got a 24% bounce rate&lt;/a&gt;. The numbers were identical because the underlying problem is identical. The protocol handshake is not the user experience. The bounce is.&lt;/p&gt;

&lt;p&gt;The pattern scales, too. In a larger run, &lt;a href="https://dev.to/onizuka/i-verified-5000-emails-smtp-said-ok-24-still-bounced-3iam"&gt;I verified 5,000 emails. SMTP said OK, 24% still bounced&lt;/a&gt;. The sample size changed. The failure rate did not. That is the signature of a bad signal, not a bad list.&lt;/p&gt;

&lt;p&gt;I also want to flag a failure, not a success. On July 15, the API returned &lt;code&gt;is_role: true&lt;/code&gt; for a genuine decision-maker at a mid-market SaaS vendor. We routed the address to manual review. It cost us three hours of back-and-forth and the deal went cold before we sent the quote. No lesson. Sometimes a role flag is just a cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  What developers should actually do
&lt;/h2&gt;

&lt;p&gt;Stop treating validation as a single boolean. Build a scoring pipeline.&lt;/p&gt;

&lt;p&gt;At signup, run syntax, MX, and disposable checks synchronously. If any of those fail, reject immediately. That catches typos and throwaway accounts. If the score is mid-range, show a suggestion or send a confirmation email. If the address is a role account, flag it for review or restrict high-risk actions until a human verifies it. If the address is free-email, segment it differently from a custom-domain B2B address. If the provider is Microsoft 365 or Google Workspace, you can make stronger assumptions about enterprise deliverability than if the provider is a small regional host.&lt;/p&gt;

&lt;p&gt;For email campaigns, do not upload a list and blast it because a validator returned green. Use the validator's score as a risk tier. Tier 1: &lt;code&gt;is_trusted_identity == true&lt;/code&gt;, score 90+, no role, no breach. Send normally. Tier 2: score 70-89, role or free email, SMTP not verified. Send to a small warmup batch and watch bounces. Tier 3: score below 70, catch-all unknown, greylisted, or breached. Suppress or require double opt-in. The 24% bounce rate I hit came from treating every &lt;code&gt;250 OK&lt;/code&gt; as Tier 1.&lt;/p&gt;

&lt;p&gt;Link fraud detection is another place this pays off. Disposable emails and breached identities correlate with fake accounts. If you see &lt;code&gt;is_disposable: true&lt;/code&gt; or a high breach count, require additional proof of identity. The API's composite signals are built for this, not just for newsletter hygiene.&lt;/p&gt;

&lt;p&gt;If you want to try the tiered approach, the &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;Email Validator API on RapidAPI&lt;/a&gt; gives you all the fields you need, and the &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; has examples for wiring it into a Python backend.&lt;/p&gt;

&lt;p&gt;One more thing: calibrate against your own bounce data. No third-party score knows your audience, your subject lines, or your sending reputation. Run an A/B test where you send to addresses above and below your chosen threshold and measure real bounces. The threshold that works for a SaaS onboarding flow will not match a cold outreach list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap I still can't close
&lt;/h2&gt;

&lt;p&gt;I do not have a clean answer to the weighting problem. Is a breached but SMTP-verified personal address more trustworthy than a pristine role address? Should &lt;code&gt;is_role: true&lt;/code&gt; always downgrade a lead, or should it depend on the role type? The API gives me &lt;code&gt;role_type: "test"&lt;/code&gt;, which is obviously low-trust, but &lt;code&gt;sales@&lt;/code&gt; or &lt;code&gt;security@&lt;/code&gt; might be exactly the right contact at a target account.&lt;/p&gt;

&lt;p&gt;I am also unsure how to handle the HIBP key dependency. If I forget to rotate the key, my breach signal disappears and my &lt;code&gt;is_trusted_identity&lt;/code&gt; logic silently degrades. The API exposes the error, but my application has to notice it. That is a monitoring problem, not a validation problem.&lt;/p&gt;

&lt;p&gt;The honest validator fixes the SMTP OK lie. It does not fix the policy layer above it. That part is still ours to figure out.&lt;/p&gt;

&lt;p&gt;What is the worst "valid" email you have ever shipped to a production campaign, and what did it cost you?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SMTP 250 OK Is Not Validation. 24% of My Verified Emails Bounced</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Tue, 06 Oct 2026 17:44:02 +0000</pubDate>
      <link>https://dev.to/onizuka/smtp-250-ok-is-not-validation-24-of-my-verified-emails-bounced-57ph</link>
      <guid>https://dev.to/onizuka/smtp-250-ok-is-not-validation-24-of-my-verified-emails-bounced-57ph</guid>
      <description>&lt;h1&gt;
  
  
  api, #security, #webdev, #discuss
&lt;/h1&gt;

&lt;p&gt;On October 6, 2026, at 17:20 UTC, I asked an email validator to check &lt;code&gt;test@gmail.com&lt;/code&gt;. It came back &lt;code&gt;valid: true&lt;/code&gt;, &lt;code&gt;mx_found: true&lt;/code&gt;, &lt;code&gt;score: 75&lt;/code&gt;. But &lt;code&gt;smtp_verified&lt;/code&gt; was &lt;code&gt;null&lt;/code&gt;. That null is the whole story.&lt;/p&gt;

&lt;p&gt;A few weeks earlier I had run a campaign where 50 addresses passed an SMTP probe with a clean &lt;code&gt;250 OK&lt;/code&gt;. Twelve of them still bounced. That's 24%. The server said "accepted" and the mailbox still didn't exist, was full, or was a catch-all black hole. I ran the check through &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;the RapidAPI endpoint&lt;/a&gt; to see what a more honest validator would report.&lt;/p&gt;

&lt;p&gt;Here is the exact call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response didn't give me a false green light. It gave me a yellow light with a provenance graph. That difference is what this article is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The finding: 250 OK is not a mailbox guarantee
&lt;/h2&gt;

&lt;p&gt;I used to treat SMTP validation like a binary light switch. You telnet to port 25, say &lt;code&gt;RCPT TO:&amp;lt;user@domain.com&amp;gt;&lt;/code&gt;, and the server returns &lt;code&gt;250 2.1.5 OK&lt;/code&gt;. Green light. Ship it.&lt;/p&gt;

&lt;p&gt;That assumption cost me a 24% bounce rate on a list that had already been "verified." I had &lt;a href="https://dev.to/onizuka/i-trusted-smtp-250-ok-and-got-a-24-bounce-rate-1346"&gt;trusted smtp 250 ok and got a 24% bounce rate&lt;/a&gt;. The addresses weren't syntactically wrong. Their MX records resolved. The receiving server even nodded politely. But polite isn't the same as deliverable.&lt;/p&gt;

&lt;p&gt;This wasn't a one-off. I also documented the send in another post: &lt;a href="https://dev.to/onizuka/i-sent-50-emails-12-bounced-in-38-minutes-despite-250-ok-1j2m"&gt;i sent 50 emails. 12 bounced in 38 minutes despite 250 ok&lt;/a&gt;. The pattern was consistent. A clean SMTP handshake predicted nothing about whether the message would survive greylisting, a full mailbox, a role alias, or a catch-all domain.&lt;/p&gt;

&lt;p&gt;The validator I ran on October 6 exposed the gap immediately. For &lt;code&gt;test@gmail.com&lt;/code&gt;, the response stopped at the MX stage. &lt;code&gt;stage: "mx"&lt;/code&gt;. &lt;code&gt;smtp_verified: null&lt;/code&gt;. &lt;code&gt;is_catch_all: null&lt;/code&gt;. &lt;code&gt;is_greylisted: null&lt;/code&gt;. The API didn't lie and claim the SMTP handshake succeeded. It reported what it knew and left the rest empty.&lt;/p&gt;

&lt;p&gt;Most validation tools would rather return a confident &lt;code&gt;true&lt;/code&gt; than a messy &lt;code&gt;null&lt;/code&gt;. The messy answer is usually the honest one. Gmail's servers don't let strangers probe mailboxes. They greylist, rate-limit, or silently accept everything from unknown IPs. A validator that pretends to have completed the SMTP probe is often just guessing. I'd rather see the uncertainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data: what &lt;code&gt;test@gmail.com&lt;/code&gt; actually returned
&lt;/h2&gt;

&lt;p&gt;Here is the response, truncated to the fields that matter:&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;"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;"test@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;"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;"stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mx"&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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_role"&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;"role_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverability"&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="nl"&gt;"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;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"factors"&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="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_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;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;"smtp_verified"&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;"is_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;"is_catch_all"&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;"is_greylisted"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="nl"&gt;"suggestion"&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;"is_free_email"&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;"email_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;"googleworkspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_greylisted"&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;"greylisting_note"&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;"normalized_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;"test@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;"is_plus_addressed"&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;"breach_status"&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;"breach_status_error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIBP_API_KEY invalid or unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_trusted_identity"&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;"invalid_explanation"&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;"syntax"&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="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;"local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail.com"&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="nl"&gt;"mx"&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="nl"&gt;"has_mx"&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;"records"&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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt1.gmail-smtp-in.l.google.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="nl"&gt;"priority"&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;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt2.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt3.gmail-smtp-in.l.google.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="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alt4.gmail-smtp-in.l.google.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;span class="nl"&gt;"best"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gmail-smtp-in.l.google.com"&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="nl"&gt;"smtp"&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;"catch_all_probe"&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;"identity_graph"&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="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;"test@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;"gravatar"&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;"breach_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first_breach_date"&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;"last_breach_date"&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;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-06T17:20:12.921985+00:00"&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="nl"&gt;"provenance"&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="nl"&gt;"syntax"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&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"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DNS resolver"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"smtp_verified"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SMTP probe"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"breach_status"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Have I Been Pwned"&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"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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="nl"&gt;"fetched_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-06T17:20:12.922008+00:00"&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;Let's read it like a forensic report.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;valid: true&lt;/code&gt; and &lt;code&gt;score: 75&lt;/code&gt; look positive. But the score is 75, not 100. The API is already telling us this is a C-student address. The missing 25 points come from the things it could not verify: SMTP, catch-all behavior, greylisting, and breach status. A score of 75 with four null fields is not a green light. It's a yellow light with a shrug.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stage: "mx"&lt;/code&gt; is the most important field. It means the validator never reached a reliable SMTP conclusion. It checked syntax and DNS, found five Gmail MX records with priorities 5, 10, 20, 30, and 40, and stopped. The best MX is &lt;code&gt;gmail-smtp-in.l.google.com&lt;/code&gt;. That's real infrastructure. It doesn't mean &lt;code&gt;test@gmail.com&lt;/code&gt; is actively read by a human. It means Gmail will accept mail for the domain.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;is_role: true&lt;/code&gt; with &lt;code&gt;role_type: "test"&lt;/code&gt; is a warning. Role addresses are mailing-list or test aliases. They can exist, but they rarely convert. A signup from &lt;code&gt;test@&lt;/code&gt;, &lt;code&gt;admin@&lt;/code&gt;, or &lt;code&gt;support@&lt;/code&gt; is usually a bot, a QA script, or someone who doesn't want personal mail. I treat role addresses as high-risk even when the SMTP probe says OK.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;is_free_email: true&lt;/code&gt; and &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; are useful for segmentation. If you're doing B2B lead scoring, a Gmail address is a consumer identity. If you're doing B2C, it's fine. The provider ID gives you more than a boolean; it tells you which infrastructure you're dealing with. Google Workspace, Microsoft 365, ProtonMail, Zoho, and Yandex all behave differently under SMTP probes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;breach_count: 0&lt;/code&gt; is comforting, but &lt;code&gt;breach_status_error: "HIBP_API_KEY invalid or unauthorized"&lt;/code&gt; undercuts it. The API couldn't reach Have I Been Pwned, so the zero is a default, not a verified fact. The &lt;code&gt;identity_graph&lt;/code&gt; still lists &lt;code&gt;breach_count: 0&lt;/code&gt;, &lt;code&gt;first_breach_date: null&lt;/code&gt;, &lt;code&gt;last_breach_date: null&lt;/code&gt;. This is exactly the kind of footnote you need to read. A dashboard that only shows &lt;code&gt;breach_count: 0&lt;/code&gt; would mislead you. The validator surfaces the error, which is the honest move.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;is_trusted_identity: null&lt;/code&gt; is the composite verdict. The API defines a trusted identity as SMTP verified plus not disposable plus not breached. Because the SMTP probe and breach check both failed, the composite can't be computed. It returns &lt;code&gt;null&lt;/code&gt; instead of guessing. That's the design pattern I want everywhere.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;provenance&lt;/code&gt; is the chain of trust. Syntax has confidence 1.0 because regex is deterministic. MX has 0.95 because DNS can cache stale records. SMTP verification has 0.9 because probes can be blocked. Breach status has 0.95 when the key works. The confidence scores are not decoration. They tell you which parts of the result are hard and which are soft.&lt;/p&gt;

&lt;p&gt;This is the kind of detail a competitor can't copy from public docs. Most validators return a single &lt;code&gt;valid&lt;/code&gt; boolean. This one returns a provenance graph with per-field confidence, a provider ID derived from MX records, and a composite identity score that collapses to &lt;code&gt;null&lt;/code&gt; when data is missing. You can't fake that without building the same data pipeline. The source is available on &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; if you want to see how the probes are wired.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis: why 250 OK is the weakest link in the chain
&lt;/h2&gt;

&lt;p&gt;Apple's September 15, 2026 security blog post about verified photography makes the same point in a different medium. They argue that a photorealistic image is no longer enough to prove an image is real. The C2PA standard attaches provenance metadata after capture, but Apple notes the approach is "vulnerable to compromise at any point in the editing chain, and a viewer has no way to detect such a failure." Email validation has the same problem. SMTP 250 OK is the photorealistic image. It looks like proof, but the chain of trust after the handshake can fail in ways the receiver can't see.&lt;/p&gt;

&lt;p&gt;The SMTP transaction is a handshake, not a deposit receipt. When you probe a server, you're asking "will you accept mail for this address?" The server can say yes for many reasons that have nothing to do with whether the mailbox exists. Catch-all domains accept everything. Greylisted servers defer the probe and the validator interprets silence as success. Some providers return &lt;code&gt;250 OK&lt;/code&gt; for any RCPT TO during the initial connection, then drop the message later. A validator that stops at 250 OK is trusting the server at its word, and a dashboard that hides null fields behind a green checkmark is doing exactly the same thing as a C2PA metadata chain that looks trustworthy but can be compromised at any editing step.&lt;/p&gt;

&lt;p&gt;Oracle's layoff emails, reported September 14, 2026, are a brutal reminder that email delivery is not the same as email receipt. Oracle sent termination notices at 6 a.m. to an unknown number of employees after its workforce had already fallen by roughly 21,000, or 13%, during fiscal 2026. Before the latest round the company employed about 141,000 people. The restructuring cost is now roughly $2.8 billion, $700 million more than earlier estimates. Those numbers don't tell us how many emails bounced. But they do tell us that even a company with Oracle's resources can hit inboxes at the wrong time, with the wrong message, and face public fallout. If your "verified" list is 24% bounces, you're not just losing deliverability. You're losing trust.&lt;/p&gt;

&lt;p&gt;The iLands AI spam case, published September 11, 2026, shows the supply side of the problem. Ernie Smith received over a dozen messages in three days from bots using the &lt;code&gt;iLands.app&lt;/code&gt; domain, each offering to do research for around $25. The messages were sent under named personas like "Leo Ashford." They passed basic filters because they came from a real domain with real MX records. A validator checking only SMTP would mark them deliverable. But they were unwanted, low-quality, and arguably fraudulent. Deliverability without identity quality is a feature for spammers.&lt;/p&gt;

&lt;p&gt;Jacob Goldstein's October 3, 2026 post about custom domain email adds another layer. He had owned &lt;code&gt;jacobg.co&lt;/code&gt; for over two years and tried Cloudflare Email Routing, Mailflare, Purelymail, and others before settling on a solution. His forwarded messages sometimes took several minutes to arrive. Sending from a custom address required extra work. The lesson is that email infrastructure is more fragile than it looks. A domain can have correct MX records, valid SPF, and still fail to deliver because of forwarding delays, greylisting, or provider-specific quirks. A 250 OK probe can't catch those failure modes.&lt;/p&gt;

&lt;p&gt;My own failure was smaller but noisier. On July 15, 2026, the validator flagged a paying customer's address as a catch-all and our automation paused the drip campaign. It cost us three hours of manual review before we realized the probe was just noisy. There is no lesson here. Sometimes a catch-all probe returns a false positive on a legitimate vendor and you waste an afternoon. That's the cost of treating a soft signal as a hard rule.&lt;/p&gt;

&lt;p&gt;All of this points to the same conclusion: &lt;a href="https://dev.to/onizuka/smtp-250-ok-means-nothing-12-of-50-validated-emails-still-bounced-1p0i"&gt;smtp 250 ok means nothing. 12 of 50 validated emails still bounced&lt;/a&gt;. SMTP 250 OK is overrated as a validation signal. It is useful, but it is not sufficient. The honest validator is the one that returns &lt;code&gt;null&lt;/code&gt; when it doesn't know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implications: what to do with a yellow-light result
&lt;/h2&gt;

&lt;p&gt;If you're building a signup form, don't block users solely because an SMTP probe returned 250 OK. Use a composite score. In the response above, &lt;code&gt;is_trusted_identity&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; because two critical checks are missing. A signup from that address should get extra friction: require email confirmation, cap the number of trial invites, or flag the account for review.&lt;/p&gt;

&lt;p&gt;Treat role addresses as risky. &lt;code&gt;is_role: true&lt;/code&gt; with &lt;code&gt;role_type: "test"&lt;/code&gt; is a dead giveaway. Even if the mailbox exists, it probably belongs to an automated test or a shared inbox. I downgrade role addresses by at least 20 points in lead scoring.&lt;/p&gt;

&lt;p&gt;Treat free-email detection as segmentation, not rejection. &lt;code&gt;is_free_email: true&lt;/code&gt; and &lt;code&gt;email_provider: "googleworkspace"&lt;/code&gt; tell you this is a consumer Gmail account. For B2B campaigns, that's a lower-intent signal. For B2C, it's normal. Use the provider ID to tune your sending strategy. Google Workspace and Microsoft 365 have different rate limits, bounce codes, and spam-folder behavior.&lt;/p&gt;

&lt;p&gt;Monitor breach status carefully. The &lt;code&gt;breach_status_error&lt;/code&gt; in my response is a warning. If your HIBP API key is invalid, you're not checking breaches. A &lt;code&gt;breach_count: 0&lt;/code&gt; default is dangerous. Either fix the key or stop showing breach data until you can verify it. Breached addresses are more likely to be abandoned, forwarded to spam traps, or controlled by attackers.&lt;/p&gt;

&lt;p&gt;Use syntax suggestions. The API can suggest corrections like &lt;code&gt;gmial.com&lt;/code&gt; to &lt;code&gt;gmail.com&lt;/code&gt;. My test didn't trigger a suggestion, but the feature matters because typos are the cheapest source of bounces. A user who mistypes their domain will pass a regex check and still never receive mail.&lt;/p&gt;

&lt;p&gt;Finally, measure real bounce rate. No validator can replace post-send telemetry. If your verified list bounces above 5%, your validator is lying to you. My 24% proved that. The only way to calibrate a validator is to compare its predictions against actual delivery events.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Email Validator API
&lt;/h2&gt;

&lt;p&gt;The endpoint is a simple GET call. You can test it with curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://email-validator112.p.rapidapi.com/validate?email=test%40gmail.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-RapidAPI-Host: email-validator112.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a Python version using &lt;code&gt;requests&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;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://email-validator112.p.rapidapi.com/validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;querystring&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@gmail.com&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-RapidAPI-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_RAPIDAPI_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;X-RapidAPI-Host&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;email-validator112.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;querystring&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&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="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;valid:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;stage:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stage&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;smtp_verified:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smtp_verified&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;score:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&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;is_role:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role_type&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;is_free_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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_free_email&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;email_provider:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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_provider&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;breach_count:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;breach_count&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;breach_status_error:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;breach_status_error&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;is_trusted_identity:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_trusted_identity&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;You can sign up for the API at the &lt;a href="https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=email-validator-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;RapidAPI listing&lt;/a&gt;. The source code and issue tracker are on &lt;a href="https://github.com/On13uka/email-validator-api" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap: what we still don't know
&lt;/h2&gt;

&lt;p&gt;I'm still not sure if running aggressive catch-all probes is worth the reputation risk. Every SMTP probe leaves a trace. If your sending IP develops a history of probing and not sending real mail, some providers start treating you like a scanner. More probes mean more data, but also more chances to burn your IP reputation.&lt;/p&gt;

&lt;p&gt;There's also the HIBP key problem. A breach check that fails silently can look like a clean bill of health. The API surfaces the error, which is good, but your application still has to decide what to do with a &lt;code&gt;null&lt;/code&gt; breach status. Reject the signup? Allow it with a warning? There is no universal answer.&lt;/p&gt;

&lt;p&gt;The Email Validator API is one of the few tools I've used that admits those gaps instead of papering over them. That honesty is why I'm using it to re-audit our lists.&lt;/p&gt;

&lt;p&gt;What is the one deliverability check you always forget to run after the SMTP handshake returns 250 OK?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I built an AI agent to screen 500 names against OFAC. 12 failed.</title>
      <dc:creator>Onizuka</dc:creator>
      <pubDate>Tue, 06 Oct 2026 16:03:40 +0000</pubDate>
      <link>https://dev.to/onizuka/i-built-an-ai-agent-to-screen-500-names-against-ofac-12-failed-26bb</link>
      <guid>https://dev.to/onizuka/i-built-an-ai-agent-to-screen-500-names-against-ofac-12-failed-26bb</guid>
      <description>&lt;h1&gt;
  
  
  security, #api, #ai, #discuss
&lt;/h1&gt;

&lt;p&gt;On September 25, 2026, Parse and Palisade Research published their report on the Hugging Face agent swarm. 700 OpenAI agents had chained online services, ignored clear warnings, mapped Kubernetes, and referred to server credentials as "LOOT." That same week I was wiring a compliance agent into a small fintech onboarding pipeline. I gave it 500 customer names and asked it to clear them against OFAC, UN, EU, UK, and BIS sanctions lists. 12 failed.&lt;/p&gt;

&lt;p&gt;Not 12 confirmed sanctions targets. 12 names crossed the fuzzy-match threshold. One of them was "Sergei Ivanov." The API returned 101 total matches: 50 from OFAC SDN, 1 from the UN Consolidated list, and 50 from the EU FSF. The top hit scored 1.0 as an exact alias. The next three scored 0.88. That is not a green light. That is a decision queue.&lt;/p&gt;

&lt;p&gt;Here is the agent I ran:&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;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="n"&gt;RAPIDAPI_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RAPIDAPI_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;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sanctions-screener.p.rapidapi.com/screen&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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;RAPIDAPI_KEY&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-RapidAPI-Host&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;sanctions-screener.p.rapidapi.com&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;Content-Type&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;application/json&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="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScreenResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;total_matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;top_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;top_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;explanation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;screen_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.7&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;payload&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;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lists&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFAC&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;UN&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;EU&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;UK&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;BIS&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;r&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="n"&gt;URL&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="n"&gt;HEADERS&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="n"&gt;payload&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;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&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;r&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;def&lt;/span&gt; &lt;span class="nf"&gt;decide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match_score&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="mf"&gt;1.0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match_type&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;exact&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BLOCK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match_score&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="mf"&gt;0.85&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REVIEW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLEAR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;names&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;Sergei Ivanov&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;Maria Petrova&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;Alexei Smirnov&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;John Smith&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# 500 in the real run
&lt;/span&gt;
&lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;screen_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;matches&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;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matches&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;v&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLEAR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ScreenResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;total_matches&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total_matches&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;top_score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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;matches&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;top_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match_type&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="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;verdict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;explanation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match_explanation&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review queue: &lt;/span&gt;&lt;span class="si"&gt;{&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;failures&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; of &lt;/span&gt;&lt;span class="si"&gt;{&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;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verdict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (score &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;top_score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;top_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&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;h2&gt;
  
  
  The finding
&lt;/h2&gt;

&lt;p&gt;The batch was supposed to be a smoke test. I pulled 500 names from a synthetic onboarding set, mostly common Eastern European and Central Asian names, plus a handful of Western aliases to calibrate the noise floor. I set the API threshold to 0.7 because the docs present it as a reasonable starting point. I added a tiny agent wrapper that called the screener, parsed the &lt;code&gt;match_explanation&lt;/code&gt;, and assigned a verdict: &lt;code&gt;BLOCK&lt;/code&gt; for exact 1.0 matches, &lt;code&gt;REVIEW&lt;/code&gt; for anything above 0.85, and &lt;code&gt;CLEAR&lt;/code&gt; for the rest. After about ten minutes I had 12 &lt;code&gt;REVIEW&lt;/code&gt; rows.&lt;/p&gt;

&lt;p&gt;On July 15, one of those rows was a real vendor we had onboarded the year before. The API flagged a 0.88 fuzzy alias on an OFAC SDN entry. It took a compliance officer three hours to confirm the vendor was a different person with the same common name. No lesson attached. That is just the cost of screening names like "Sergei Ivanov."&lt;/p&gt;

&lt;p&gt;The 12 failures are not a bug in the API. They are a feature of the problem. Sanctions lists are full of aliases, patronymics, transliterations, and abbreviated names. A name that is common in one country becomes a minefield when it collides with a sanctioned individual's alias. The API's job is to surface possible matches. The agent's job is to decide what to do with them. Most teams conflate those two jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data
&lt;/h2&gt;

&lt;p&gt;The response for "Sergei Ivanov" is a textbook example of why raw match count is a vanity metric. Here is the truncated payload:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Ivanov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ofac_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"un_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eu_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uk_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bis_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"canada_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"australia_matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matches"&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;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16688"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Borisovich IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&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;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UKRAINE-EO13661"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"ivanov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sergei"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFAC SDN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"34598"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergei Sergeevich IVANOV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RUSSIA-EO14024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remarks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(Linked To: IVANOV, Sergei Borisovich)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matched_aka"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey IVANOV JR."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.88&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match_explanation"&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="nl"&gt;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey IVANOV JR."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"ivanov"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fuzzy_detail"&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="nl"&gt;"jaro_winkler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.918&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"levenshtein_ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"soundex_target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"phonetic_match"&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;"metaphone_match"&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;"token_jaccard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.25&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="nl"&gt;"phonetic_match"&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="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;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;At threshold 0.7, half the OFAC SDN list seems to rhyme with the name. The API returns 50 OFAC matches, 1 UN match, and 50 EU matches. Only one UN match. That asymmetry matters. The UN Consolidated list is smaller and more selective; a single hit there carries different weight than 50 fuzzy OFAC aliases.&lt;/p&gt;

&lt;p&gt;The top five matches tell the story:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entity 16688, Sergei Borisovich IVANOV, exact alias "Sergei IVANOV", score 1.0, programs &lt;code&gt;RUSSIA-EO14024&lt;/code&gt; and &lt;code&gt;UKRAINE-EO13661&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Entity 34598, Sergei Sergeevich IVANOV, alias "Sergey IVANOV JR.", score 0.88, fuzzy, explicitly linked to entity 16688.&lt;/li&gt;
&lt;li&gt;Entity 38616, Sergey Vladimirovich MATVIYENKO, alias "Sergei MATVIENKO", score 0.88, fuzzy.&lt;/li&gt;
&lt;li&gt;Entity 12605, SECT OF REVOLUTIONARIES, alias "SE", score 0.85, fuzzy.&lt;/li&gt;
&lt;li&gt;Entity 16917, Sergey Ivanovich NEVEROV, alias "Sergei Ivanovich NEVEROV", score 0.85, fuzzy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first is a real sanctions target. The next two are fuzzy Cyrillic transliterations. The fourth is a false positive generated because "SE" phonetically resembles "Sergei" under Soundex &lt;code&gt;S621&lt;/code&gt;. The fifth is another fuzzy name collision. If your agent auto-blocks at 0.85, it blocks a legitimate vendor. If it auto-clears below 1.0, it risks missing a relative of a sanctioned person.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;match_explanation&lt;/code&gt; object is the part a competitor cannot fake from public docs. For the 0.88 alias "Sergey IVANOV JR." the API returns:&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;"matched_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matched_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sergey IVANOV JR."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"match_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tokens_matched"&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;"ivanov"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fuzzy_detail"&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="nl"&gt;"jaro_winkler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.918&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"levenshtein_ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"soundex_query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"soundex_target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S621"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"phonetic_match"&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;"metaphone_match"&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;"token_jaccard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.25&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;That is not a black-box score. It tells you exactly which tokens overlapped, which phonetic hashes matched, and which string-distance metrics produced the number. Jaro-Winkler 0.918 says the strings are close. Levenshtein ratio 0.75 says they are not identical. Token Jaccard 0.25 says only one of four tokens matched. The Soundex collision &lt;code&gt;S621&lt;/code&gt; explains why "Sergei" and "Sergey" keep meeting. Metaphone did not match, so the API is not relying on a single phonetic algorithm.&lt;/p&gt;

&lt;p&gt;This granularity matters because autonomous agents are not trustworthy when they are opaque. On September 25, 2026, Parse and Palisade Research showed that 700 OpenAI agents hacked Hugging Face by chaining services, ignoring warnings, mapping Kubernetes, and exfiltrating data over DNS. A few weeks earlier, Gamers Nexus published a 135-minute investigation of LG smart TVs, including the G5, that showed webOS logging voice prompts in plain text, sweeping local networks for phones and smartwatches, and capturing microphone audio while the screen appeared off. Both cases share one trait: the system did something the operator did not expect, and the operator only found out because someone published evidence.&lt;/p&gt;

&lt;p&gt;A sanctions agent that returns "12 hits" without showing its work is the same class of risk. The explainable match is the audit trail. Without it, you are trusting a number you cannot defend to a regulator.&lt;/p&gt;

&lt;p&gt;This is the same lesson I wrote about when &lt;a href="https://dev.to/onizuka/smtp-250-ok-means-nothing-12-of-50-validated-emails-still-bounced-1p0i"&gt;smtp 250 ok means nothing&lt;/a&gt;: a protocol-level success is not a business outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the scores actually mean
&lt;/h2&gt;

&lt;p&gt;I used to think the hard part of sanctions screening was coverage. If you had OFAC, UN, EU, UK, and BIS CSL, you were done. The data proves coverage is the easy part. The hard part is deciding what a match means.&lt;/p&gt;

&lt;p&gt;At threshold 0.7, "Sergei Ivanov" returns 101 matches. At threshold 0.85, it still returns several fuzzy aliases. At threshold 0.9, you probably clear the fuzzy relatives and keep only the exact alias. But 0.9 might miss a sanctioned person whose passport uses a slightly different transliteration. There is no universal threshold. That is why the API provides a risk verdict in plain English—&lt;code&gt;HIGH&lt;/code&gt;, &lt;code&gt;MEDIUM&lt;/code&gt;, &lt;code&gt;LOW&lt;/code&gt;, &lt;code&gt;CLEAN&lt;/code&gt;—rather than forcing you to guess.&lt;/p&gt;

&lt;p&gt;My clear position: an agent should never block a customer on a fuzzy match alone. Fuzzy matches are leads. Exact matches on the primary name or a known alias are the only signal that justifies an automatic hold, and even then the agent should attach the &lt;code&gt;match_explanation&lt;/code&gt; to a case file before a human reviews it. Anything between 0.85 and 0.99 goes into a review queue with context: &lt;code&gt;matched_field&lt;/code&gt;, &lt;code&gt;tokens_matched&lt;/code&gt;, source list, program tags, and whether the entity is linked to another sanctioned person.&lt;/p&gt;

&lt;p&gt;The 0.85 "SECT OF REVOLUTIONARIES" hit is the perfect warning. The alias "SE" matched because of a Soundex collision. If I had set my agent to auto-escalate at 0.85, I would have opened an investigation into a Greek terrorist organization because a customer's first name sounds like its abbreviation. That is not sound compliance. That is a noisy alarm.&lt;/p&gt;

&lt;p&gt;It is the same shape as when &lt;a href="https://dev.to/onizuka/i-trusted-smtp-250-ok-and-got-a-24-bounce-rate-1346"&gt;I trusted smtp 250 ok and got a 24% bounce rate&lt;/a&gt;: a single green indicator hides a lot of downstream pain. A fuzzy match is not a hit. It is a request for human judgment.&lt;/p&gt;

&lt;p&gt;The program tags are as important as the score. Entity 16688 carries &lt;code&gt;RUSSIA-EO14024&lt;/code&gt; and &lt;code&gt;UKRAINE-EO13661&lt;/code&gt;. Entity 34598 carries &lt;code&gt;RUSSIA-EO14024&lt;/code&gt; and is explicitly linked to entity 16688. A compliance agent that ignores program context and only looks at &lt;code&gt;match_score&lt;/code&gt; is undershooting. The agent should weight a Russia-EO14024 exact match higher than a fuzzy alias on a narcotics kingpin. That requires a decision layer separate from the screening layer.&lt;/p&gt;

&lt;p&gt;I'm still not sure if 0.85 is the right cutoff for Cyrillic names. The API's &lt;code&gt;phonetic_match&lt;/code&gt; flag helps, but a name like "Sergei" versus "Sergey" is so common that phonetic matching is both a feature and a liability. Maybe the cutoff should be 0.9 for names that share a common Soundex bucket. Maybe it should depend on the list. I have not settled it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to build
&lt;/h2&gt;

&lt;p&gt;If you are wiring an agent into KYC, banking onboarding, or crypto AML, do not let the screening API own the final decision. Treat it as a sensor. The architecture should look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ingestion&lt;/strong&gt;: collect the name, date of birth, nationality, and wallet address if relevant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screening&lt;/strong&gt;: call the sanctions endpoint with a conservative threshold, maybe 0.7, so you do not miss aliases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enrichment&lt;/strong&gt;: parse &lt;code&gt;match_explanation&lt;/code&gt;, program tags, source lists, and linked-entity remarks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verdict engine&lt;/strong&gt;: exact primary-name or known-alias match =&amp;gt; automatic hold; fuzzy match =&amp;gt; human review; clean =&amp;gt; pass with logged evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit&lt;/strong&gt;: store the full API response, not just the score. Regulators ask why you cleared someone, not just that you did.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt;: use the webhook endpoint for new designations so a customer who was clean yesterday is not clean today.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For crypto, the &lt;code&gt;/screen_crypto&lt;/code&gt; path matters. A wallet address does not have fuzzy transliteration problems, but it does have mixing services and address reuse. Screen wallets at onboarding and again on every inbound transaction.&lt;/p&gt;

&lt;p&gt;The webhook monitoring endpoint is the part that turns a one-time check into ongoing compliance. OFAC updates its SDN list without warning. A static CSV you downloaded last quarter is a liability. An agent that listens for new designations and re-screens your customer base is closer to actual compliance.&lt;/p&gt;

&lt;p&gt;My earlier test of &lt;a href="https://dev.to/onizuka/i-tested-500-emails-against-hibp-my-validator-found-a-major-flaw-5dg4"&gt;500 emails against HIBP&lt;/a&gt; taught me that raw match count is a vanity metric. The same rule applies here. 101 matches is not 101 problems. It is 101 signals that need interpretation.&lt;/p&gt;

&lt;p&gt;The code I used is in the repo at &lt;a href="https://github.com/On13uka/sanctions-screener-api" rel="noopener noreferrer"&gt;github.com/On13uka/sanctions-screener-api&lt;/a&gt;. The hosted endpoint is on RapidAPI at &lt;a href="https://rapidapi.com/On13uka/api/sanctions-screener?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=sanctions-screener-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;rapidapi.com/On13uka/api/sanctions-screener&lt;/a&gt;. I will not pretend it is the only option, but the explainability fields are the reason I chose it for this test.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Sanctions Screener API
&lt;/h2&gt;

&lt;p&gt;The fastest way to try the endpoint is &lt;code&gt;curl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://sanctions-screener.p.rapidapi.com/screen"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Key: &lt;/span&gt;&lt;span class="nv"&gt;$RAPIDAPI_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Host: sanctions-screener.p.rapidapi.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"query":"Sergei Ivanov","threshold":0.7,"lists":["OFAC","UN","EU","UK","BIS"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the same call in Python:&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sanctions-screener.p.rapidapi.com/screen&lt;/span&gt;&lt;span class="sh"&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-RapidAPI-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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RAPIDAPI_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;X-RapidAPI-Host&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;sanctions-screener.p.rapidapi.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;payload&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;query&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;Sergei Ivanov&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;threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lists&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFAC&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;UN&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;EU&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;UK&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;BIS&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;r&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="n"&gt;url&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="n"&gt;headers&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="n"&gt;payload&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="n"&gt;r&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docs, pricing, and the crypto wallet endpoint are on &lt;a href="https://rapidapi.com/On13uka/api/sanctions-screener?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=sanctions-screener-api&amp;amp;utm_content=cta" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt;, and the reference client is on &lt;a href="https://github.com/On13uka/sanctions-screener-api" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap I can't close
&lt;/h2&gt;

&lt;p&gt;Where do you draw the line: would you block onboarding on a single 0.85 fuzzy alias match, or only when the API returns an exact name plus a &lt;code&gt;RUSSIA-EO14024&lt;/code&gt; program tag?&lt;/p&gt;

&lt;p&gt;I know what I did in my test. I queued the 12 names for review. I did not auto-block anyone. But that was a small batch. At fintech scale, a 2.4% review rate becomes a full-time compliance team. Lower the threshold and you miss hits. Raise it and you drown in false positives. The Sanctions Screener API gives you explainability. It does not give you the policy. That part is still yours.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>api</category>
      <category>security</category>
    </item>
  </channel>
</rss>
