<?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>Forem: APIVerve</title>
    <description>The latest articles on Forem by APIVerve (@apiverve).</description>
    <link>https://forem.com/apiverve</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1903396%2Ffb21c9e6-1502-4695-90f9-61bb48e43a2b.png</url>
      <title>Forem: APIVerve</title>
      <link>https://forem.com/apiverve</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/apiverve"/>
    <language>en</language>
    <item>
      <title>7 SSL Certificate Problems That Kill Trust</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 16 Mar 2026 16:29:30 +0000</pubDate>
      <link>https://forem.com/apiverve/7-ssl-certificate-problems-that-kill-trust-n4</link>
      <guid>https://forem.com/apiverve/7-ssl-certificate-problems-that-kill-trust-n4</guid>
      <description>&lt;p&gt;Nothing tanks user trust faster than a browser security warning. That red padlock, the "Your connection is not private" interstitial—most visitors won't click through it. They'll leave. And they probably won't come back.&lt;/p&gt;

&lt;p&gt;SSL certificates are one of those things that work invisibly until they don't. And when they fail, they fail loudly. Here are seven problems that catch teams off guard, and how to spot them before your users do.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Certificate Expired
&lt;/h2&gt;

&lt;p&gt;This is the most common SSL failure, and the most preventable. Every SSL certificate has an expiration date. When that date passes, browsers immediately distrust the certificate and show a full-page warning.&lt;/p&gt;

&lt;p&gt;It happens to everyone. Even massive companies have let certificates expire on production systems. The problem is that certificate renewal is easy to forget when it only happens once a year (or once every 90 days with Let's Encrypt). The person who set it up might have left the company. The auto-renewal might have failed silently. The credit card on file—or even the &lt;a href="https://apiverve.com/marketplace/domainexpiration?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=ssl-certificate-problems-checklist" rel="noopener noreferrer"&gt;domain registration itself&lt;/a&gt;—might have expired.&lt;/p&gt;

&lt;p&gt;The fix is monitoring. Use an &lt;a href="https://apiverve.com/marketplace/sslchecker?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=ssl-certificate-problems-checklist" rel="noopener noreferrer"&gt;SSL checker&lt;/a&gt; to check your certificates regularly and alert when expiration is approaching. Most teams set alerts at 30 days, 14 days, and 7 days before expiration. That gives you three chances to notice and act before anything breaks.&lt;/p&gt;

&lt;p&gt;An SSL checker returns the &lt;code&gt;valid_from&lt;/code&gt; and &lt;code&gt;valid_to&lt;/code&gt; dates. Subtract today from &lt;code&gt;valid_to&lt;/code&gt; and you know exactly how many days remain. If the number gets below your threshold, somebody needs to renew.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Domain Doesn't Match
&lt;/h2&gt;

&lt;p&gt;An SSL certificate is issued for specific domain names. A certificate for &lt;code&gt;example.com&lt;/code&gt; doesn't cover &lt;code&gt;app.example.com&lt;/code&gt; unless it's a wildcard certificate (&lt;code&gt;*.example.com&lt;/code&gt;). And a wildcard for &lt;code&gt;*.example.com&lt;/code&gt; doesn't cover &lt;code&gt;example.com&lt;/code&gt; itself—that's a separate entry.&lt;/p&gt;

&lt;p&gt;This mismatch happens most often during infrastructure changes. You spin up a new subdomain, point it to a server that already has an SSL certificate, and assume it's covered. It isn't. The browser checks whether the certificate's subject (or subject alternative names) includes the domain being accessed. If not, warning page. A quick &lt;a href="https://apiverve.com/marketplace/dnslookup?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=ssl-certificate-problems-checklist" rel="noopener noreferrer"&gt;DNS lookup&lt;/a&gt; can confirm which server a subdomain actually points to before you assume certificate coverage.&lt;/p&gt;

&lt;p&gt;Checking the &lt;code&gt;subject.CN&lt;/code&gt; (Common Name) and &lt;code&gt;subjectaltname&lt;/code&gt; fields from a certificate reveals exactly which domains it covers. Before launching a new subdomain, verify the certificate includes it. Before migrating a domain to new infrastructure, confirm the new server's certificate matches.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Weak Encryption
&lt;/h2&gt;

&lt;p&gt;Not all SSL certificates are equally strong. The encryption strength depends on the key size and the cipher suites the server supports.&lt;/p&gt;

&lt;p&gt;A 2048-bit RSA key is the current minimum for browser trust. Older certificates with 1024-bit keys are considered insecure. Even certificates that are technically valid might use deprecated cipher suites that modern scanners flag as weak.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;bits&lt;/code&gt; field from an SSL check tells you the key size immediately. If you see 1024 or lower, that certificate needs replacement regardless of its expiration date. Modern certificates should use 2048-bit RSA at minimum, with 4096-bit as the recommended standard for anything handling sensitive data.&lt;/p&gt;

&lt;p&gt;This matters beyond security. Google uses HTTPS as a ranking signal, and strong SSL configuration is part of that assessment. Weak encryption can quietly hurt your search rankings.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Certificate Chain Issues
&lt;/h2&gt;

&lt;p&gt;SSL certificates don't exist in isolation. They're part of a chain: your certificate, signed by an intermediate certificate, signed by a root certificate that browsers trust. If any link in this chain is missing or broken, validation fails.&lt;/p&gt;

&lt;p&gt;The most common chain issue is a missing intermediate certificate. Your certificate is valid. The root certificate is trusted. But the server doesn't serve the intermediate certificate that connects the two. Some browsers can fill in the gap by fetching the intermediate themselves. Others can't.&lt;/p&gt;

&lt;p&gt;This creates the maddening situation where your site works in Chrome but not in Firefox, or works on desktop but not on mobile. Inconsistent failures across browsers almost always point to a chain problem.&lt;/p&gt;

&lt;p&gt;An SSL check that includes CA issuer information reveals whether the chain is complete. If the check succeeds but specific browsers fail, look at the chain configuration on your server.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Mixed Content
&lt;/h2&gt;

&lt;p&gt;You have a valid SSL certificate. Your site loads over HTTPS. But some resources on the page—images, scripts, stylesheets—load over plain HTTP. This is mixed content, and browsers handle it harshly.&lt;/p&gt;

&lt;p&gt;Active mixed content (scripts, iframes) gets blocked entirely. Passive mixed content (images, audio) might load but triggers warnings. Either way, that green padlock disappears, replaced by a warning icon that tells users something is wrong.&lt;/p&gt;

&lt;p&gt;SSL certificate checks won't catch this directly—it's a content problem, not a certificate problem. But it ends up on this list because developers blame the certificate first, every single time. If your certificate checks clean but users report security warnings, audit your pages for HTTP resource references.&lt;/p&gt;

&lt;p&gt;The fix is usually straightforward: change &lt;code&gt;http://&lt;/code&gt; to &lt;code&gt;https://&lt;/code&gt; in your resource URLs, or better yet, use protocol-relative URLs (&lt;code&gt;//&lt;/code&gt;) that inherit the page's protocol.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The Wrong Certificate Type
&lt;/h2&gt;

&lt;p&gt;Different certificate types provide different levels of validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domain Validation (DV)&lt;/strong&gt; certificates confirm you control the domain. They're automated, free (via Let's Encrypt), and sufficient for most applications. The certificate shows the domain name but no organization information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Organization Validation (OV)&lt;/strong&gt; certificates verify the organization behind the domain. The CA checks that the company exists and controls the domain. The certificate includes the organization name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extended Validation (EV)&lt;/strong&gt; certificates involve the most rigorous verification. The CA verifies legal existence, physical address, and operational presence. These used to show a green bar in browsers (most no longer do), but they still convey maximum trust for high-stakes applications.&lt;/p&gt;

&lt;p&gt;The issue isn't that one type is "wrong"—it's using the wrong type for your context. An e-commerce site processing payments should probably have OV or EV, not just DV. A personal blog? DV is more than enough. Financial institutions often require EV for regulatory compliance.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;issuer&lt;/code&gt; and &lt;code&gt;subject&lt;/code&gt; fields reveal the certificate type. DV certificates show minimal subject information. OV and EV certificates include organization details—country, state, company name.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Certificate Revocation
&lt;/h2&gt;

&lt;p&gt;Certificates can be revoked before they expire. This happens when private keys are compromised, the CA made an error, or the domain ownership changed. Revoked certificates should not be trusted, even if they haven't expired.&lt;/p&gt;

&lt;p&gt;Revocation checking is, frankly, one of SSL's worst-designed aspects. Two mechanisms exist: CRL (Certificate Revocation Lists) and OCSP (Online Certificate Status Protocol). Both have reliability issues. CRLs can be large and slow to download. OCSP responders can be unreachable.&lt;/p&gt;

&lt;p&gt;OCSP stapling improves this by having the server attach a recent OCSP response to the SSL handshake. Clients don't need to contact the OCSP responder separately. If your server supports OCSP stapling, enable it.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;infoAccess&lt;/code&gt; field from a certificate check shows the OCSP URI—where revocation status can be verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating the Checklist
&lt;/h2&gt;

&lt;p&gt;Manually checking certificates doesn't scale. If you manage more than a handful of domains, automate these checks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/sslchecker?domain=yourdomain.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.valid_from → certificate start date&lt;/span&gt;
&lt;span class="c1"&gt;// data.valid_to → certificate expiration date&lt;/span&gt;
&lt;span class="c1"&gt;// data.bits → key strength (2048, 4096, etc.)&lt;/span&gt;
&lt;span class="c1"&gt;// data.issuer.O → certificate authority name&lt;/span&gt;
&lt;span class="c1"&gt;// data.subject.CN → domain the cert covers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this daily against every domain you manage. Alert on certificates expiring within 30 days. Flag any certificate with key strength below 2048 bits. Log changes to detect unexpected certificate swaps.&lt;/p&gt;

&lt;p&gt;Most SSL failures are preventable. They happen because nobody was watching. A daily automated check turns "surprise outage" into "routine maintenance."&lt;/p&gt;




&lt;p&gt;Monitor your SSL certificates with the &lt;a href="https://apiverve.com/marketplace/sslchecker?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=ssl-certificate-problems-checklist" rel="noopener noreferrer"&gt;SSL Checker API&lt;/a&gt;. Verify TLS configuration with the &lt;a href="https://apiverve.com/marketplace/tlscheck?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=ssl-certificate-problems-checklist" rel="noopener noreferrer"&gt;TLS Checker API&lt;/a&gt;. Track domain expirations with the &lt;a href="https://apiverve.com/marketplace/domainexpiration?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=ssl-certificate-problems-checklist" rel="noopener noreferrer"&gt;Domain Expiration API&lt;/a&gt;. Stay ahead of security issues.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/ssl-certificate-problems-checklist" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ssl</category>
      <category>security</category>
      <category>devops</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Take Your App Multilingual (No Rewrite)</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 16 Mar 2026 16:19:50 +0000</pubDate>
      <link>https://forem.com/apiverve/take-your-app-multilingual-no-rewrite-3fh3</link>
      <guid>https://forem.com/apiverve/take-your-app-multilingual-no-rewrite-3fh3</guid>
      <description>&lt;p&gt;"We should support Spanish" is a sentence that strikes fear into engineering teams. Not because translation is conceptually hard, but because retroactively adding language support to an app built entirely in English feels like it requires rearchitecting everything.&lt;/p&gt;

&lt;p&gt;It doesn't. At least, not all at once.&lt;/p&gt;

&lt;p&gt;There's a pragmatic middle ground between "English only" and "fully localized in 40 languages." Most teams never find it because the discussion jumps straight from "we need Spanish" to "okay, let's implement i18n across the entire codebase." That's a months-long project. There are faster ways to start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start With the Content, Not the UI
&lt;/h2&gt;

&lt;p&gt;The typical internationalization approach starts with the UI framework. Extract every string into a translation file. Set up locale routing. Configure date and number formatting. This is the right end state, but it's the wrong starting point for most teams.&lt;/p&gt;

&lt;p&gt;Start with user-facing content instead. The parts of your application where language actually matters to the user experience. Product descriptions. Help articles. Email notifications. Error messages. Onboarding flows.&lt;/p&gt;

&lt;p&gt;These are the places where a non-English speaker hits a wall. They might tolerate an English navigation bar—button labels are often recognizable across languages. But a help article that explains a critical feature? An error message that tells them what went wrong? Those need to be in their language.&lt;/p&gt;

&lt;p&gt;Translating content is a much smaller, more contained project than internationalizing an entire UI framework. And it delivers most of the user value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Machine Translation Has Gotten Good
&lt;/h2&gt;

&lt;p&gt;Five years ago, machine translation was a punchline. Google Translate could turn a French restaurant menu into surrealist poetry. Professional human translators were the only serious option.&lt;/p&gt;

&lt;p&gt;That's changed. Modern machine translation handles most European and major Asian languages well. It's not perfect—it struggles with idioms, cultural references, and highly technical jargon. But for straightforward content like product descriptions, support articles, and transactional emails, the quality is genuinely usable.&lt;/p&gt;

&lt;p&gt;The key is knowing where machine translation works and where it doesn't.&lt;/p&gt;

&lt;p&gt;It works well for: factual content, technical documentation, transactional messages (order confirmations, shipping notifications), knowledge base articles, and structured data with predictable patterns.&lt;/p&gt;

&lt;p&gt;It works poorly for: marketing copy that relies on wordplay, legal documents where precision is critical, creative content that depends on cultural context, and anything where tone is as important as meaning.&lt;/p&gt;

&lt;p&gt;For the first category, machine translation gets you 80-90% of the way there. For the second, you still need human translators—but that's a much smaller volume of content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detect Before You Translate
&lt;/h2&gt;

&lt;p&gt;Before you translate anything, you need to know what language your users actually want. Guessing wrong is worse than not translating at all. There are several signals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser language headers&lt;/strong&gt; are the most reliable automatic signal. Every HTTP request includes an &lt;code&gt;Accept-Language&lt;/code&gt; header that lists the user's preferred languages. If the header says &lt;code&gt;es-MX,es;q=0.9,en;q=0.8&lt;/code&gt;, the user prefers Mexican Spanish, then general Spanish, then English.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Account settings&lt;/strong&gt; let users explicitly choose. This is the gold standard—direct user preference. But it only works for logged-in users who've configured their settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content language detection&lt;/strong&gt; handles user-generated input. When a user submits a support ticket or fills out a form, detecting the language of their text tells you how to respond. If they wrote in Portuguese, respond in Portuguese.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Geographic signals&lt;/strong&gt; are a rough proxy. A user in Japan probably prefers Japanese. But this fails for expatriates, travelers, and multilingual countries like Switzerland or India. Don't force language based on location—use it as a default that users can override.&lt;/p&gt;

&lt;p&gt;The pragmatic approach layers these signals: use account settings if available, fall back to browser headers, and detect content language for user-generated text.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Translate First
&lt;/h2&gt;

&lt;p&gt;You can't translate everything at once. So what earns priority?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onboarding flows&lt;/strong&gt; are the highest priority. A user who can't understand the signup process won't become a user. If your signup, login, and initial setup are translated, you capture users who would otherwise bounce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transactional emails&lt;/strong&gt; come next. Order confirmations, password resets, billing notifications—these are messages users need to understand to use your service. A password reset email in a language you don't read is useless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error messages&lt;/strong&gt; might be third. Think about how frustrating it is to get an error you &lt;em&gt;can&lt;/em&gt; read. Now imagine it's in a language you don't understand. If a payment fails and the error message is in English, a Spanish-speaking user has no idea what to do next. Translated errors reduce support ticket volume significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Help documentation&lt;/strong&gt; and support articles address self-service. If users can find answers in their language, they don't need to contact support. This scales better than hiring multilingual support staff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Marketing pages&lt;/strong&gt; are last in priority but first in visibility. Your homepage and pricing page are what prospective users see. But prospects who don't read English probably found you through localized search results, which means they need localized landing pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Translation API
&lt;/h2&gt;

&lt;p&gt;Translating content programmatically keeps the process automated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/translator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your payment was processed successfully.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.translatedText → "Su pago fue procesado exitosamente."&lt;/span&gt;
&lt;span class="c1"&gt;// data.sourceLang → "en"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;source&lt;/code&gt; parameter is optional. When omitted, the API auto-detects the source language. This is useful when processing user-generated content where you don't know what language they wrote in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing Translations
&lt;/h2&gt;

&lt;p&gt;Where do translated strings live? This depends on your architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alongside the original content&lt;/strong&gt; is simplest. If your product descriptions are in a database, add a &lt;code&gt;locale&lt;/code&gt; column and store translated versions as separate rows. Same content ID, different locale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In translation files&lt;/strong&gt; works for UI strings. JSON files keyed by locale (&lt;code&gt;en.json&lt;/code&gt;, &lt;code&gt;es.json&lt;/code&gt;, &lt;code&gt;fr.json&lt;/code&gt;) are the standard i18n approach. Most frontend frameworks have built-in support for this pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generated on the fly&lt;/strong&gt; works for content that changes frequently or where the translation doesn't need to be perfect. Real-time translation of support chat messages, for example. The user sees the translation immediately, even if it's not hand-polished.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cached after first translation&lt;/strong&gt; balances cost and freshness. Translate content the first time it's requested in a given language, cache the result, and serve from cache thereafter. This limits API calls while keeping translations available.&lt;/p&gt;

&lt;p&gt;The right approach often combines these. Static UI strings in translation files. Database content with locale columns. Real-time translation for chat and user-generated content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Languages to Start With
&lt;/h2&gt;

&lt;p&gt;Choosing which languages to add first is a business decision, not a technical one.&lt;/p&gt;

&lt;p&gt;Check your analytics. Where are your existing users? If 15% of your traffic comes from Spanish-speaking countries, Spanish is the obvious first language. If you have growing traffic from Brazil, Portuguese.&lt;/p&gt;

&lt;p&gt;Consider your market strategy. Expanding into a specific region? Translate for that region's languages before you launch there, not after.&lt;/p&gt;

&lt;p&gt;Think about language reach. Spanish covers 20+ countries. French covers parts of Europe, Africa, and the Americas. Mandarin covers the world's largest population. Arabic covers a wide geographic band. These high-reach languages provide the most return per translation investment.&lt;/p&gt;

&lt;p&gt;Also consider language similarity. If you've already translated to Spanish, Portuguese is a smaller incremental effort—the languages share significant vocabulary and structure. This is one of those rare cases where the second step is genuinely easier than the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Translation Quality Over Time
&lt;/h2&gt;

&lt;p&gt;Machine translation is a starting point, not a final state. Over time, improve translations using feedback.&lt;/p&gt;

&lt;p&gt;Let users flag bad translations. A small "Suggest better translation" link gives bilingual users a way to contribute. Crowdsourced corrections improve quality organically.&lt;/p&gt;

&lt;p&gt;Review high-traffic pages with human translators. Your homepage, pricing page, and checkout flow deserve professional translation. The cost is modest and the impact on conversion is measurable.&lt;/p&gt;

&lt;p&gt;Track support tickets by language. If Spanish-speaking users consistently contact support about a specific feature, the translated documentation for that feature probably needs work.&lt;/p&gt;

&lt;p&gt;Translation quality is a spectrum, not a binary. Shipping an 80% translation today beats shipping a perfect one six months from now. The users you'd lose in those six months aren't coming back.&lt;/p&gt;




&lt;p&gt;Translate content with the &lt;a href="https://apiverve.com/marketplace/translator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=going-multilingual-without-rebuilding" rel="noopener noreferrer"&gt;Translation API&lt;/a&gt;. Detect languages with the &lt;a href="https://apiverve.com/marketplace/languagedetector?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=going-multilingual-without-rebuilding" rel="noopener noreferrer"&gt;Language Detection API&lt;/a&gt;. Reach a global audience without starting from scratch.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/going-multilingual-without-rebuilding" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>internationalization</category>
      <category>translation</category>
      <category>localization</category>
      <category>growth</category>
    </item>
    <item>
      <title>Extract Clean Content from Any Webpage</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 16 Mar 2026 16:16:49 +0000</pubDate>
      <link>https://forem.com/apiverve/extract-clean-content-from-any-webpage-34km</link>
      <guid>https://forem.com/apiverve/extract-clean-content-from-any-webpage-34km</guid>
      <description>&lt;p&gt;Open the source of any modern webpage. What you'll find is thousands of lines of HTML—navigation bars, footers, sidebars, ad containers, tracking scripts, cookie banners, newsletter popups, social sharing widgets. Buried somewhere in that noise is the actual content. The article. The documentation. The information someone actually came to read.&lt;/p&gt;

&lt;p&gt;Extracting that content cleanly is a problem developers run into constantly. Whether you're building a read-later app, migrating content between platforms, feeding text to an AI model, or archiving information for research, you need the content without the cruft.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Raw HTML Is Useless
&lt;/h2&gt;

&lt;p&gt;You might think fetching a webpage and stripping the tags gives you the content. Try it on any real webpage and you'll understand why that doesn't work.&lt;/p&gt;

&lt;p&gt;A typical blog post lives inside a &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; tag, but it's surrounded by dozens of &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements containing things that aren't the content. The navigation has text. The footer has text. The sidebar has text. Related article sections have text. Cookie consent banners have text. All of it is valid HTML, and none of it is the article.&lt;/p&gt;

&lt;p&gt;Stripping tags removes the structure but keeps all the noise. You end up with the article text mashed together with navigation links, footer disclaimers, sidebar promotions, and whatever else lives on the page. Completely unusable.&lt;/p&gt;

&lt;p&gt;Proper content extraction requires understanding what the page is about and isolating the primary content from everything else. This is a surprisingly hard problem that's been studied for over a decade, with algorithms like Readability (the one behind Firefox's Reader View) and various boilerplate detection methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown as the Output Format
&lt;/h2&gt;

&lt;p&gt;Why Markdown specifically? Because it preserves structure while being universally portable.&lt;/p&gt;

&lt;p&gt;HTML preserves structure too, but it carries presentation concerns. CSS classes, inline styles, framework-specific attributes—all tied to the original site's design system. Moving HTML between systems means cleaning up all that presentation baggage.&lt;/p&gt;

&lt;p&gt;Plain text loses structure entirely. Headings become regular lines. Links lose their URLs. Lists become indistinguishable from paragraphs. You can't tell where a section begins or what's emphasized.&lt;/p&gt;

&lt;p&gt;Markdown sits in the sweet spot. It preserves headings (&lt;code&gt;##&lt;/code&gt;), links (&lt;code&gt;[text](url)&lt;/code&gt;), lists, bold/italic emphasis, and code blocks. But it carries zero presentation information. It renders cleanly in any Markdown-capable system—GitHub, Notion, static site generators, documentation platforms, or even a plain text editor.&lt;/p&gt;

&lt;p&gt;This makes Markdown the ideal intermediate format. Extract from any source, output Markdown, then render it however you need. Think of it as plain text that didn't skip leg day—just enough structure to be genuinely useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case: Content Migration
&lt;/h2&gt;

&lt;p&gt;Moving content between platforms is one of the most common reasons to extract web content.&lt;/p&gt;

&lt;p&gt;Say you're migrating a blog from WordPress to a static site generator like Astro, Hugo, or Next.js. Your old posts are in WordPress's HTML format, complete with WordPress-specific shortcodes and CSS classes. Your new platform wants Markdown files.&lt;/p&gt;

&lt;p&gt;You could export the WordPress database and parse the HTML. But that gives you WordPress-flavored HTML full of &lt;code&gt;wp-block-*&lt;/code&gt; classes and shortcode syntax that no other platform understands.&lt;/p&gt;

&lt;p&gt;The cleaner approach: point a &lt;a href="https://apiverve.com/marketplace/urltomarkdown?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=extract-clean-content-from-any-webpage" rel="noopener noreferrer"&gt;content extractor&lt;/a&gt; at each published URL and get back clean Markdown. The extractor ignores the WordPress theme, the sidebar widgets, the comment sections. It pulls the article content and converts it to portable Markdown that works anywhere.&lt;/p&gt;

&lt;p&gt;This works for any source platform. Migrating from Medium? From Ghost? From a custom CMS? If the content is accessible via URL, it can be converted to Markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case: AI and LLM Pipelines
&lt;/h2&gt;

&lt;p&gt;Large language models need clean text. Feed them a raw HTML page and they waste context window on navigation menus, script tags, and boilerplate. Feed them extracted Markdown and they get the actual content with structural context preserved.&lt;/p&gt;

&lt;p&gt;This matters enormously for retrieval-augmented generation (RAG) systems. Garbage in, hallucinations out. When your application retrieves web pages to answer user questions, the quality of the retrieved content determines the quality of the answer. If the retrieved "content" is mostly navigation links and cookie banners, the model's response will be confused or hallucinated.&lt;/p&gt;

&lt;p&gt;Markdown extraction cleans the input pipeline. Each URL becomes a clean document with headings, paragraphs, and links—exactly what the model needs to generate useful responses.&lt;/p&gt;

&lt;p&gt;The word count in the response also helps you manage context windows. If the extracted content is 5,000 words and your model context is 8,000 tokens, you know you need to &lt;a href="https://apiverve.com/marketplace/textsummarizer?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=extract-clean-content-from-any-webpage" rel="noopener noreferrer"&gt;summarize&lt;/a&gt; or chunk the content before including it. Without word count metadata, you're guessing at token usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case: Research and Archiving
&lt;/h2&gt;

&lt;p&gt;Web content disappears. Pages get taken down, domains expire, companies shut down, articles get edited. If you need to reference web content later—for research, compliance, or legal purposes—you need to save it.&lt;/p&gt;

&lt;p&gt;Archiving as Markdown has advantages over saving HTML or screenshots. Markdown files are tiny (kilobytes versus megabytes for full HTML with assets). They're searchable with basic text tools. They're version-controllable with Git. They render in any text editor.&lt;/p&gt;

&lt;p&gt;A research workflow might look like: find relevant articles, extract each to Markdown, save with metadata (source URL, extraction date, word count), and organize in a searchable archive. Months later, you can search your archive, find the relevant document, and cite it with the original URL—even if the original has since been taken down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case: Read-Later and Digest Tools
&lt;/h2&gt;

&lt;p&gt;Pocket, Instapaper, and similar read-later tools solve the same core problem: extract the readable content from a webpage and present it cleanly. Building a custom version of this—whether for personal use, a team knowledge base, or a customer-facing feature—requires content extraction.&lt;/p&gt;

&lt;p&gt;Newsletter digests work similarly. Aggregate content from multiple sources, extract the articles, summarize or truncate them, and compile into a single readable email or document.&lt;/p&gt;

&lt;p&gt;The word count from extraction helps with digest curation. If your weekly digest should take 10 minutes to read (roughly 2,500 words), you can select articles that fit within that budget and trim or summarize others.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Good Extraction Looks Like
&lt;/h2&gt;

&lt;p&gt;A well-extracted page preserves several things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headings&lt;/strong&gt; maintain their hierarchy. The article's H1, H2, and H3 structure carries over to Markdown heading levels. This lets you navigate the document, generate a table of contents, or break the content into sections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt; survive as Markdown links with their original URLs. If the article references external sources, those references remain clickable. For content migration, this means your internal links can be updated to new URLs in a search-and-replace pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lists&lt;/strong&gt; stay as lists. Bullet points and numbered lists render as Markdown lists rather than being flattened into paragraphs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code blocks&lt;/strong&gt; are preserved when present. Technical documentation often includes code examples, and losing those to paragraph formatting would destroy the content's utility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Images&lt;/strong&gt; are referenced but not downloaded. The Markdown includes image references with the original URLs. You can download and re-host images separately if needed. This keeps the extraction lightweight and fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Extraction Call
&lt;/h2&gt;

&lt;p&gt;Extracting a webpage to Markdown is a single POST request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/urltomarkdown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/blog/interesting-article&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.title → "The Interesting Article"&lt;/span&gt;
&lt;span class="c1"&gt;// data.markdown → "# The Interesting Article\n\nFirst paragraph..."&lt;/span&gt;
&lt;span class="c1"&gt;// data.wordCount → 2847&lt;/span&gt;
&lt;span class="c1"&gt;// data.linkCount → 14&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes the page title, the full Markdown content, and metadata about what was extracted—word count, image count, and link count. This metadata helps you decide what to do with the content before processing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dealing with Edge Cases
&lt;/h2&gt;

&lt;p&gt;Not every webpage extracts cleanly. Some edge cases to be aware of.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript-rendered content&lt;/strong&gt; is the biggest headache. Single-page applications that render content entirely in JavaScript after page load don't serve readable HTML in the initial response. The extraction sees the JavaScript bundle, not the rendered content. Most traditional websites, blogs, documentation sites, and news outlets serve HTML directly and extract well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paywalled content&lt;/strong&gt; returns only what's publicly visible. If an article shows the first three paragraphs and hides the rest behind a paywall, that's all the extraction will capture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heavy advertising&lt;/strong&gt; can confuse extraction algorithms. Pages where ads are interspersed with content might include some ad text in the output. This is rare with good extraction, but it happens on particularly ad-heavy sites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-article pages&lt;/strong&gt; like homepages, category pages, and search results don't have a single "article" to extract. The extraction might return navigation text or a collection of snippets rather than coherent content. These pages aren't good extraction targets.&lt;/p&gt;

&lt;p&gt;For anything production-facing, validate extraction results. Check that the word count is reasonable (an article with 50 words almost certainly extracted poorly), that the title was found, and that the Markdown contains actual content. If you only need plain text without formatting, a &lt;a href="https://apiverve.com/marketplace/websitetotext?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=extract-clean-content-from-any-webpage" rel="noopener noreferrer"&gt;website-to-text conversion&lt;/a&gt; is a lighter alternative worth considering.&lt;/p&gt;




&lt;p&gt;Extract web content cleanly with the &lt;a href="https://apiverve.com/marketplace/urltomarkdown?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=extract-clean-content-from-any-webpage" rel="noopener noreferrer"&gt;URL to Markdown API&lt;/a&gt;. Get plain text with the &lt;a href="https://apiverve.com/marketplace/websitetotext?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=extract-clean-content-from-any-webpage" rel="noopener noreferrer"&gt;Website to Text API&lt;/a&gt;. Scrape links with the &lt;a href="https://apiverve.com/marketplace/linkscraper?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=extract-clean-content-from-any-webpage" rel="noopener noreferrer"&gt;Link Scraper API&lt;/a&gt;. Build content pipelines that work with any source.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/extract-clean-content-from-any-webpage" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>contentextraction</category>
      <category>markdown</category>
      <category>dataprocessing</category>
    </item>
    <item>
      <title>Enrich Your CRM with Public Company Data</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 16 Mar 2026 16:11:59 +0000</pubDate>
      <link>https://forem.com/apiverve/enrich-your-crm-with-public-company-data-3k6g</link>
      <guid>https://forem.com/apiverve/enrich-your-crm-with-public-company-data-3k6g</guid>
      <description>&lt;p&gt;Sales reps hate data entry. Every minute spent googling a company's address, looking up their ticker symbol, or figuring out what industry they're in is a minute they could spend actually selling.&lt;/p&gt;

&lt;p&gt;CRM records with missing fields are a universal problem. A lead comes in with a company name and an email. That's it. No industry, no address, no exchange information, no SIC code. The rep is supposed to fill all of that in before their next call. They won't. The CRM stays sparse, reporting stays useless, and nobody trusts the data.&lt;/p&gt;

&lt;p&gt;Data enrichment fixes this by pulling company information automatically. When a lead mentions they work at a public company, you can look up everything the SEC knows about that company in real time—and it's a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SEC Already Did the Research
&lt;/h2&gt;

&lt;p&gt;The SEC requires public companies to file detailed information. Every US-listed company has a Central Index Key (CIK), and their filings include corporate metadata that's remarkably useful for sales and business intelligence.&lt;/p&gt;

&lt;p&gt;From a single ticker symbol or company name, you get:&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;legal company name&lt;/strong&gt; as registered with the SEC. This matters more than you'd think. Your lead might say "Google," but the SEC filing says "Alphabet Inc." Contracts, proposals, and invoices need the legal name.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;SIC code and industry description.&lt;/strong&gt; Standard Industrial Classification codes categorize companies by their primary business activity. SIC 3571 is "Electronic Computers." SIC 7372 is "Prepackaged Software." This tells your sales team what vertical the prospect is in without anyone having to research it.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;stock exchange&lt;/strong&gt; where the company trades. NYSE, Nasdaq, or other exchanges. For some sales teams, exchange listing is a proxy for company size and legitimacy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Physical addresses&lt;/strong&gt;—both mailing and business headquarters. Knowing where a company is headquartered helps with territory assignment, timezone awareness, and compliance with regional regulations.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;phone number&lt;/strong&gt; from their SEC filing. It's the corporate number, not a sales line, but it's a verified contact point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State of incorporation.&lt;/strong&gt; Delaware, Nevada, and a few other states are disproportionately popular. This matters for legal teams drafting contracts and for compliance workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fiscal year end.&lt;/strong&gt; If you sell to finance teams, knowing when their fiscal year closes tells you when budget decisions happen. A company with a June fiscal year makes purchasing decisions on a completely different timeline than one ending in December.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Former company names.&lt;/strong&gt; Companies rename. Mergers happen. Your CRM might have the old name. The API tells you what the company used to be called, helping you reconcile records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four Lookup Methods, One Response
&lt;/h2&gt;

&lt;p&gt;Not every lead arrives with the same information. Sometimes you have a ticker symbol. Sometimes just a name. Doesn't matter—the API handles all of these.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ticker lookup&lt;/strong&gt; is the most precise. If someone mentions they work at a company and you know the ticker—AAPL, MSFT, TSLA—you get an exact match. No ambiguity. One ticker, one company.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CIK lookup&lt;/strong&gt; is how regulators reference companies. If you're integrating with other SEC data sources or financial systems, CIK numbers are the canonical identifier. Think of it as the company's "social security number" in the SEC system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Name search&lt;/strong&gt; handles the fuzziest input. A lead says "I work at Apple" and you search for "Apple." You'll get results that match, and you pick the right one. Name search uses prefix matching, so "Micro" returns Microsoft, Micron, and other companies starting with those letters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SIC code search&lt;/strong&gt; flips the whole thing around. Instead of looking up one company, you list all companies in a specific industry. SIC 7372 returns every publicly traded prepackaged software company. Want to build a prospect list for an entire vertical in one call? This is how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enrichment in Practice
&lt;/h2&gt;

&lt;p&gt;The real value of company data isn't in individual lookups—it's in automated enrichment that runs behind the scenes.&lt;/p&gt;

&lt;p&gt;When a new lead enters your CRM, you extract the company name from their email domain or from what they entered in a form. You search the company name against the API. If there's a match, you populate CRM fields automatically.&lt;/p&gt;

&lt;p&gt;This happens without the sales rep doing anything. They open the lead record and the industry, address, phone number, and other fields are already there. The rep can focus on the conversation, not the data entry.&lt;/p&gt;

&lt;p&gt;For existing CRM records, run a batch enrichment process. Export records with missing fields, look up each company, fill in the gaps, and import back. A CRM with 10,000 leads that's 60% complete suddenly becomes 90% complete overnight. That's the kind of improvement that makes a sales manager's week.&lt;/p&gt;

&lt;p&gt;The enrichment also serves as a data quality check. If a lead claims to work at "Apple" but the email domain is a personal Gmail, that's worth flagging. If the company name doesn't match any SEC filing, it's either a private company (useful to know) or possibly fake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Sales Intelligence
&lt;/h2&gt;

&lt;p&gt;Raw company data is nice. Combined company data is where it gets interesting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Industry segmentation&lt;/strong&gt; using SIC codes lets you analyze which industries your product sells best in. If 40% of your closed deals are in SIC codes related to financial services, that tells your marketing team where to focus campaigns. It tells your sales team which leads to prioritize.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Geographic analysis&lt;/strong&gt; using business addresses reveals where your customers cluster. Three enterprise clients in Chicago might justify a local sales presence. A concentration in a particular state might make a regional conference worth attending.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fiscal year timing&lt;/strong&gt; drives sales strategy. Companies with December fiscal year ends make buying decisions in Q3 and Q4. Companies with June year ends buy in Q1 and Q2. If your CRM knows every prospect's fiscal year, your sales team can time outreach to land when budgets are being set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Company name reconciliation&lt;/strong&gt; catches duplicates. If your CRM has records for both "Alphabet Inc" and "Google LLC," the API's former names and ticker data help you recognize they're the same entity. Deduplication improves forecasting accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  One API Call
&lt;/h2&gt;

&lt;p&gt;The technical side is minimal. Here's what a ticker lookup returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/companylookup?ticker=AAPL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.name → "Apple Inc"&lt;/span&gt;
&lt;span class="c1"&gt;// data.sic → "3571"&lt;/span&gt;
&lt;span class="c1"&gt;// data.sicDescription → "Electronic Computers"&lt;/span&gt;
&lt;span class="c1"&gt;// data.exchanges → ["Nasdaq"]&lt;/span&gt;
&lt;span class="c1"&gt;// data.addresses.business.city → "Cupertino"&lt;/span&gt;
&lt;span class="c1"&gt;// data.fiscalYearEnd → "0928"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four lookup methods, same response structure. Pick whichever matches the data you already have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Public Companies
&lt;/h2&gt;

&lt;p&gt;A natural question: what about private companies? The SEC only covers publicly traded companies—roughly 10,000 US entities. If your prospects are primarily startups or small businesses, SEC data won't cover them.&lt;/p&gt;

&lt;p&gt;But for B2B sales teams selling to mid-market and enterprise companies, the coverage is substantial. These are the companies spending significant budgets, and they're almost all publicly listed or subsidiaries of public companies.&lt;/p&gt;

&lt;p&gt;For the private companies in your CRM, the failed lookup itself is useful information. A "not found" is still a data point. Knowing a prospect is private affects how you position pricing, what competitive intel is available, and how the sales cycle might differ.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Freshness
&lt;/h2&gt;

&lt;p&gt;SEC filings update regularly. Companies file quarterly and annually, and the data refreshes accordingly. This means the API reflects current information—current addresses, current phone numbers, current SIC codes.&lt;/p&gt;

&lt;p&gt;Companies occasionally reclassify their industry, change their headquarters, or rename. The API tracks these changes. This is one reason periodic re-enrichment matters. A company that moved headquarters six months ago should have the new address in your CRM.&lt;/p&gt;




&lt;p&gt;Look up any public company with the &lt;a href="https://apiverve.com/marketplace/companylookup?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=crm-data-enrichment-company-lookup" rel="noopener noreferrer"&gt;Company Lookup API&lt;/a&gt;. Search by ticker, name, CIK, or SIC code. Enrich your CRM, build prospect lists, and stop making sales reps do data entry.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/crm-data-enrichment-company-lookup" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sales</category>
      <category>crm</category>
      <category>dataenrichment</category>
      <category>finance</category>
    </item>
    <item>
      <title>Business Day Math Is Harder Than You Think</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 16 Mar 2026 16:08:58 +0000</pubDate>
      <link>https://forem.com/apiverve/business-day-math-is-harder-than-you-think-haa</link>
      <guid>https://forem.com/apiverve/business-day-math-is-harder-than-you-think-haa</guid>
      <description>&lt;p&gt;Quick: how many business days are in March 2026?&lt;/p&gt;

&lt;p&gt;If you answered 22, you skipped weekends but forgot that different countries have different holidays in March. The US doesn't have a federal holiday in March, so 22 is correct there. But India celebrates Holi. Mexico has Benito Juárez's birthday. Japan has Vernal Equinox Day.&lt;/p&gt;

&lt;p&gt;The correct answer depends entirely on where your users are. And that's just the beginning of why business day calculations are more complex than they appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Weekend Problem
&lt;/h2&gt;

&lt;p&gt;"Weekdays are Monday through Friday" is a reasonable assumption if your entire user base is in the Western world. It's wrong for about two billion people.&lt;/p&gt;

&lt;p&gt;In many Middle Eastern countries, the traditional work week runs Sunday through Thursday. Friday and Saturday are the weekend. In some countries, the weekend is Friday and Saturday. Others use Saturday and Sunday. A few have only a single-day weekend.&lt;/p&gt;

&lt;p&gt;Saudi Arabia, the UAE, and several other Gulf states recently shifted to a Saturday-Sunday weekend to align with global financial markets. But the transition wasn't instantaneous—some sectors still observe different schedules. Building "weekend" logic as a hardcoded Saturday-Sunday check will produce wrong results for a meaningful portion of the global workforce.&lt;/p&gt;

&lt;p&gt;The pattern here is the same one that plagues all internationalization: assumptions that feel universal but aren't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Holidays Break Everything
&lt;/h2&gt;

&lt;p&gt;Even once you've sorted out weekends, holidays introduce a whole new layer of complexity.&lt;/p&gt;

&lt;p&gt;National holidays vary by country—obviously. Less obviously, they vary by region within countries. The US has federal holidays, but individual states have additional ones. Germany's holidays differ by Bundesland. Canada's provinces don't all observe the same holidays.&lt;/p&gt;

&lt;p&gt;Some holidays move. Easter is the most famous moveable holiday, but plenty of others shift year to year. Holidays based on lunar calendars (Eid, Chinese New Year, Diwali) fall on different Gregorian calendar dates each year. A holiday calculator that works for 2026 might be wrong for 2027.&lt;/p&gt;

&lt;p&gt;Some holidays have substitute rules. In Japan, if a national holiday falls on a Sunday, the following Monday becomes a holiday. In the UK, bank holidays falling on weekends are "moved" to the next working day. These substitution rules differ by country.&lt;/p&gt;

&lt;p&gt;And then there are the one-time holidays. Royal funerals, national celebrations, election days. These can be declared with short notice, and no algorithm can predict them. The UK had a one-off bank holiday for the Queen's funeral in 2022. Japan had extra holidays for the Emperor's enthronement in 2019.&lt;/p&gt;

&lt;p&gt;Any system that calculates business days needs a holiday database that's actively maintained—not a formula.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters in Software
&lt;/h2&gt;

&lt;p&gt;Business day calculations aren't academic. They affect real money and real deadlines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SLA commitments&lt;/strong&gt; are measured in business days. "We'll respond within 2 business days" means something very different if you count holidays versus if you don't. A support ticket filed on Wednesday before a three-day weekend might not be due until the following Tuesday—but only if your system knows about the holiday.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payment terms&lt;/strong&gt; like "Net 30" sometimes mean 30 business days, sometimes 30 calendar days. The distinction matters for cash flow management. A vendor expecting payment in 30 business days is actually giving you about six weeks of calendar time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shipping estimates&lt;/strong&gt; that say "3-5 business days" need to exclude weekends and holidays. A package shipped on the Friday before a Monday holiday doesn't arrive until at least Thursday—five calendar days but only three business days in transit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Financial settlement&lt;/strong&gt; follows strict business day rules. Stock trades settle on T+1 (one business day after the trade). Bond settlements are T+2. Getting these wrong has regulatory and financial consequences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HR and payroll&lt;/strong&gt; systems calculate everything from PTO accrual to payroll dates based on working days. A payroll that runs on the "last business day of the month" needs to know when that actually is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cross-Border Headache
&lt;/h2&gt;

&lt;p&gt;This is where most teams throw up their hands.&lt;/p&gt;

&lt;p&gt;Consider a contract between a US company and a German supplier. The contract says "delivery within 10 business days." Whose business days? The US celebrates Independence Day on July 4th. Germany doesn't. Germany celebrates Tag der Deutschen Einheit on October 3rd. The US doesn't.&lt;/p&gt;

&lt;p&gt;International businesses generally specify which country's calendar governs. But if your application calculates deadlines automatically, it needs to know which calendar to use for each transaction. A single "business days" calculation might need different holiday calendars depending on which parties are involved.&lt;/p&gt;

&lt;p&gt;Payment processing across borders faces this constantly. A wire transfer between New York and London might be delayed by a US holiday that the UK doesn't observe, or vice versa. Both banking systems need to be open for the transfer to process. Miss this, and money sits in limbo while everyone points fingers at the other side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API Approach
&lt;/h2&gt;

&lt;p&gt;Maintaining your own holiday database is possible but tedious. Holidays change. Countries add holidays, remove them, or change the dates. Regional variations multiply the complexity.&lt;/p&gt;

&lt;p&gt;An API handles this by maintaining the data externally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/workingdays?country=US&amp;amp;year=2026&amp;amp;month=3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.workingDaysCount → 22&lt;/span&gt;
&lt;span class="c1"&gt;// data.nonWorkingDays → [{ date: "2026-03-01", reasons: ["weekend"] }, ...]&lt;/span&gt;
&lt;span class="c1"&gt;// Each non-working day includes the reason: "weekend" or the holiday name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response tells you not just the count but the specific dates and reasons. A weekend is different from a holiday in some business contexts—some contracts count weekends but not holidays, or vice versa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hardcoding holidays.&lt;/strong&gt; Every developer thinks this will be fine. It never stays fine. A list of holidays that was correct when written becomes wrong within a year. If you must maintain a local list, treat it as data that requires annual updates, not code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ignoring regional holidays.&lt;/strong&gt; National holidays are the minimum. If your users are in Bavaria, Bavarian holidays matter. If they're in Quebec, Quebec holidays matter. The difference can be several days per year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming "business day" means the same thing everywhere.&lt;/strong&gt; It doesn't. In some industries and countries, Saturday is a half working day. Some count it as a full working day. Banking, retail, and government often have different definitions even within the same country.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting timezone boundaries.&lt;/strong&gt; When it's already Friday in Tokyo, it's still Thursday in New York. If your deadline is "5 business days from today," the definition of "today" depends on where the user is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not handling the "next business day" edge case.&lt;/strong&gt; What's the next business day after December 31st? Good luck. It depends on the year and country. Some countries have holidays on January 2nd. Some have extended New Year breaks. The answer might be January 3rd, 4th, or even later.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Much Precision Do You Need?
&lt;/h2&gt;

&lt;p&gt;Not every application needs per-country holiday awareness. A project management tool where all users are in one country can use that country's calendar and be fine. A personal todo app might not need business day calculations at all.&lt;/p&gt;

&lt;p&gt;But any application that deals with contractual deadlines, financial settlements, international operations, or SLA tracking needs proper business day logic. The cost of getting it wrong—missed deadlines, SLA violations, incorrect payment dates—exceeds the cost of implementing it right.&lt;/p&gt;

&lt;p&gt;Start with weekends. Add national holidays for your primary country. Then expand to regional holidays and additional countries as your user base grows. Each layer adds accuracy, and the API approach means you don't have to maintain the data yourself.&lt;/p&gt;




&lt;p&gt;Calculate working days accurately with the &lt;a href="https://apiverve.com/marketplace/workingdays?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=business-day-calculations-are-harder-than-you-think" rel="noopener noreferrer"&gt;Working Days API&lt;/a&gt;. Look up holidays worldwide with the &lt;a href="https://apiverve.com/marketplace/worldholidays?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=business-day-calculations-are-harder-than-you-think" rel="noopener noreferrer"&gt;World Holidays API&lt;/a&gt;. Get date math right the first time with the &lt;a href="https://apiverve.com/marketplace/datecalculator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=business-day-calculations-are-harder-than-you-think" rel="noopener noreferrer"&gt;Date Calculator API&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/business-day-calculations-are-harder-than-you-think" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>datecalculations</category>
      <category>businesslogic</category>
      <category>internationalization</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Summarize Text with AI: A Practical Guide</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 09 Mar 2026 19:07:05 +0000</pubDate>
      <link>https://forem.com/apiverve/summarize-text-with-ai-a-practical-guide-p5a</link>
      <guid>https://forem.com/apiverve/summarize-text-with-ai-a-practical-guide-p5a</guid>
      <description>&lt;p&gt;Long content is everywhere. Articles run thousands of words. Customer emails ramble across paragraphs. Research papers span dozens of pages. Support tickets contain three complaints, two tangents, and buried somewhere in the middle, the actual problem.&lt;/p&gt;

&lt;p&gt;Readers skim. Attention is limited. The information exists, but extracting it takes effort that people don't have. This is where AI summarization helps—condensing content to its essential points so readers can understand quickly and decide whether to engage further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extractive vs. Abstractive (and Why It Matters)
&lt;/h2&gt;

&lt;p&gt;Text summarization extracts the most important information from a document and presents it in condensed form. Good summarization preserves meaning while dramatically reducing length. But not all summarization works the same way.&lt;/p&gt;

&lt;p&gt;There are two fundamental approaches. Extractive summarization pulls key sentences directly from the original text and combines them. The summary uses the author's exact words, just fewer of them. Abstractive summarization generates new sentences that capture the meaning, potentially using different wording than the original.&lt;/p&gt;

&lt;p&gt;Most practical summarization systems use extractive methods or hybrid approaches. Extractive summaries are more predictable—you know the output contains actual sentences from the input. Abstractive summaries can be more readable but occasionally introduce errors or hallucinations.&lt;/p&gt;

&lt;p&gt;Here's the catch: the quality of a summary depends heavily on the input. Well-structured documents with clear topic sentences summarize better than rambling, disorganized text. A news article with a clear lead paragraph summarizes easily. A stream-of-consciousness email might not.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Summarization Adds Value
&lt;/h2&gt;

&lt;p&gt;Summarization isn't appropriate everywhere. It works best in specific contexts where readers need quick understanding without full detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content previews.&lt;/strong&gt; Article cards in a news aggregator need short descriptions. Generating these from the full article text ensures accuracy—no human needs to write descriptions for every piece.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search results.&lt;/strong&gt; When users search your content, showing a relevant snippet helps them decide which result to click. Summarization can generate these snippets contextually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Email and notification digests.&lt;/strong&gt; Daily or weekly rollups contain too many items to show in full. Summaries let recipients scan quickly and click into items that interest them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Support ticket triage.&lt;/strong&gt; Support dashboards show ticket lists. Agents need to scan quickly and prioritize. A two-sentence summary of each ticket beats reading every word of every complaint. This one alone can save hours per day for a busy support team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meeting notes.&lt;/strong&gt; Transcript summarization pulls out key decisions and action items. Attendees can confirm accuracy without reviewing the entire recording.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Research and analysis.&lt;/strong&gt; Researchers reading many papers benefit from summaries that help them decide which papers deserve full attention.&lt;/p&gt;

&lt;p&gt;The common thread: situations where understanding the gist matters more than understanding every detail, and where the volume of content exceeds the available attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary Length and Quality
&lt;/h2&gt;

&lt;p&gt;The relationship between summary length and quality isn't linear. Shorter isn't always better.&lt;/p&gt;

&lt;p&gt;Very short summaries (one sentence) capture only the single most important point. They work for headlines and notifications but lose nuance. A complex article reduced to one sentence will necessarily omit important context.&lt;/p&gt;

&lt;p&gt;Medium-length summaries (2-4 sentences) balance brevity with completeness. They can capture the main point plus supporting context. This length works for previews, digests, and triage interfaces.&lt;/p&gt;

&lt;p&gt;Longer summaries (5+ sentences) approach executive summary territory. They preserve more detail and work for situations where readers need substantive understanding without reading the full document.&lt;/p&gt;

&lt;p&gt;The right length depends on context. A content card might need one sentence. A support ticket summary might need three. A research paper abstract might need a full paragraph.&lt;/p&gt;

&lt;p&gt;Most summarization APIs let you specify length—typically as a number of sentences. Experiment with your content to find the length that serves your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Different Content Types
&lt;/h2&gt;

&lt;p&gt;Different content types summarize differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;News articles&lt;/strong&gt; summarize well because journalists write with summarization in mind. The lead paragraph contains the key information. The inverted pyramid structure puts important facts first. Summarization often just extracts this existing structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Academic papers&lt;/strong&gt; have explicit abstracts written by the authors. Summarization adds value for papers without abstracts or for generating even shorter summaries of the abstracts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer feedback&lt;/strong&gt; tends to be less structured. Reviews ramble, combine multiple topics, and vary wildly in length. Summarization helps but may require longer summaries to capture the mix of points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conversational text&lt;/strong&gt; (chat logs, meeting transcripts) is particularly challenging. Conversation meanders. Multiple speakers interleave. Important points aren't always stated explicitly. Summarization works but may miss implicit meaning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical documentation&lt;/strong&gt; summarizes reasonably well if the documentation is well-written. Step-by-step procedures condense to descriptions of what gets accomplished.&lt;/p&gt;

&lt;p&gt;Know your content. Test summarization on representative samples before deploying it widely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aggregating Multiple Summaries
&lt;/h2&gt;

&lt;p&gt;Sometimes you need to summarize not one document but many. Product reviews across hundreds of customers. Support tickets for the past week. Articles from multiple sources on the same topic.&lt;/p&gt;

&lt;p&gt;The naive approach—concatenating everything and summarizing the result—fails at scale. Documents become too long, and summaries lose coherence.&lt;/p&gt;

&lt;p&gt;Better approaches exist. Summarize each document individually, then summarize the summaries. This hierarchical approach handles arbitrary scale while maintaining quality at each level.&lt;/p&gt;

&lt;p&gt;For aggregation to work well, you need volume. Summarizing three reviews produces a thin aggregate. Summarizing three hundred produces genuine insight: "Customers consistently praise battery life and criticize the charging cable."&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining with Other Analysis
&lt;/h2&gt;

&lt;p&gt;Summarization pairs well with other text analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sentiment analysis&lt;/strong&gt; tells you how the content feels—positive, negative, neutral. Combined with summarization, you know both what was said and how it was said. "Customers complain about shipping delays (negative)" is more useful than either piece of information alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Topic extraction&lt;/strong&gt; identifies what the content is about. Combined with summarization, you can organize summaries by topic. Support tickets become "5 tickets about billing issues, 3 about login problems."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Language detection&lt;/strong&gt; identifies what language the content is in. For multilingual applications, you might summarize in the original language or translate before summarizing.&lt;/p&gt;

&lt;p&gt;These combinations create richer understanding than any single analysis provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Considerations
&lt;/h2&gt;

&lt;p&gt;The API call itself is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/textsummarizer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;articleContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.summary contains the condensed text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beyond the API call, consider caching. The same input always produces similar output (with minor variations). Cache summaries alongside their source content to avoid redundant API calls.&lt;/p&gt;

&lt;p&gt;Consider preprocessing. Very long documents might need truncation before summarization. Documents with lots of boilerplate (legal disclaimers, repeated headers) might benefit from cleaning first.&lt;/p&gt;

&lt;p&gt;And don't overlook user expectations. Make it clear when content is summarized rather than original. Users who don't realize they're reading a condensed version will blame you when details are missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality Assessment
&lt;/h2&gt;

&lt;p&gt;How do you know if summaries are good? Manual review helps initially. Read summaries alongside their source texts. Do the summaries capture the key points? Do they miss anything important? Are they readable?&lt;/p&gt;

&lt;p&gt;User feedback provides ongoing signal. If users consistently click through to full content after reading summaries, the summaries might not be providing enough information. If users seem satisfied with summaries alone, they're working.&lt;/p&gt;

&lt;p&gt;A/B testing helps for specific applications. Do content previews with AI-generated summaries perform better than manually written descriptions? Measure engagement to find out.&lt;/p&gt;

&lt;p&gt;The goal isn't perfect summarization—it's useful summarization. Did the summary help someone make a faster decision? Then it worked.&lt;/p&gt;




&lt;p&gt;Summarize text with the &lt;a href="https://apiverve.com/marketplace/textsummarizer?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=text-summarization-api-tutorial" rel="noopener noreferrer"&gt;Text Summarizer API&lt;/a&gt;. Analyze sentiment with the &lt;a href="https://apiverve.com/marketplace/sentimentanalysis?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=text-summarization-api-tutorial" rel="noopener noreferrer"&gt;Sentiment Analysis API&lt;/a&gt;. Detect language with the &lt;a href="https://apiverve.com/marketplace/textlanguage?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=text-summarization-api-tutorial" rel="noopener noreferrer"&gt;Language Detection API&lt;/a&gt;. Build smarter content processing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/text-summarization-api-tutorial" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>textprocessing</category>
      <category>javascript</category>
      <category>content</category>
    </item>
    <item>
      <title>Add Secure Password Generation to Your App</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 09 Mar 2026 19:06:29 +0000</pubDate>
      <link>https://forem.com/apiverve/add-secure-password-generation-to-your-app-581b</link>
      <guid>https://forem.com/apiverve/add-secure-password-generation-to-your-app-581b</guid>
      <description>&lt;p&gt;Users are terrible at creating passwords. Study after study confirms it. "123456" and "password" appear in breach databases millions of times. Even when people try to be clever, they follow predictable patterns—a capital letter at the start, a number at the end, maybe an exclamation point if the form demands special characters.&lt;/p&gt;

&lt;p&gt;The solution isn't more password rules. Users just work around them. The solution is generating secure passwords for them—or at least making strong suggestions they can accept with one click.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Generated Passwords Matter
&lt;/h2&gt;

&lt;p&gt;When users create their own passwords, they optimize for memorability. That's the opposite of security. A memorable password is, by definition, predictable. It contains words, dates, names, patterns that humans can recall—and that attackers can guess.&lt;/p&gt;

&lt;p&gt;Generated passwords optimize for entropy. They contain random combinations that don't exist in dictionaries, don't follow keyboard patterns, and don't relate to personal information. A 16-character randomly generated password with mixed case, numbers, and symbols has roughly 100 bits of entropy. A user-created password of the same length might have 20 bits.&lt;/p&gt;

&lt;p&gt;That difference matters. 20 bits of entropy can be brute-forced in seconds. 100 bits would take longer than the age of the universe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Password Managers Changed the Game
&lt;/h2&gt;

&lt;p&gt;Here's the good news: users increasingly use password managers. This changes the calculus entirely. When a password manager handles storage and autofill, memorability becomes irrelevant. The user never types the password manually. They never need to remember it.&lt;/p&gt;

&lt;p&gt;So why are most signup forms still optimized for memorization? Generated passwords make perfect sense in this world. The user clicks "generate," the password manager saves it, and authentication happens automatically from then on. The friction that used to make generated passwords impractical has largely disappeared.&lt;/p&gt;

&lt;p&gt;Your signup flow should recognize this. Offer password generation prominently. Make it easier to accept a generated password than to create one manually. The path of least resistance should also be the path of greatest security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Password Generation Fits
&lt;/h2&gt;

&lt;p&gt;Not every password field needs generation, but many do. Consider adding it to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signup flows.&lt;/strong&gt; This is the most impactful placement. Users creating accounts are already in decision-making mode. Offering a strong generated password right when they need one catches them at the perfect moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Password reset flows.&lt;/strong&gt; Users often pick weak passwords during reset because they're frustrated and just want access restored. A generated temporary password eliminates that risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Admin account creation.&lt;/strong&gt; When IT staff create accounts for new employees, generated passwords ensure consistent security. No more "Welcome123!" as every new hire's first password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API key generation.&lt;/strong&gt; This isn't technically a password, but the same principles apply. API keys should be long, random, and impossible to guess. Users should never create them manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity Levels and When to Use Them
&lt;/h2&gt;

&lt;p&gt;Not all passwords need maximum complexity. Different contexts call for different approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strong complexity&lt;/strong&gt; includes uppercase, lowercase, numbers, and special characters. Use this for any password that will be stored in a password manager. The user won't type it manually, so keyboard convenience doesn't matter. This is the default for most modern applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Medium complexity&lt;/strong&gt; uses letters and numbers but skips special characters. This works well for temporary passwords that users will type manually—like a password reset code they read from an email while logging in on another device. Fewer characters mean fewer typos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple complexity&lt;/strong&gt; uses only letters. This might seem weak, but a long simple password can actually be more secure than a short complex one. A 20-character lowercase password has more entropy than an 8-character mixed-case password with symbols. Some legacy systems also can't handle special characters, making simple passwords a necessity.&lt;/p&gt;

&lt;p&gt;The key is matching complexity to context. Don't force users to type &lt;code&gt;K#9mPx$2&lt;/code&gt; on a mobile keyboard when &lt;code&gt;correcthorsebattery&lt;/code&gt; would be more secure and vastly easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Length vs. Complexity
&lt;/h2&gt;

&lt;p&gt;Security experts have debated this for years, and the consensus now favors length. Honestly, it's not even close. A longer password with less complexity beats a shorter password with more complexity every time.&lt;/p&gt;

&lt;p&gt;The math supports this. Each additional character multiplies the search space. Adding a 17th character to a 16-character password doubles the difficulty of brute-forcing it. Adding special characters to a short password? Diminishing returns by comparison.&lt;/p&gt;

&lt;p&gt;Modern guidance suggests 16 characters minimum for passwords that will be stored in password managers, and 12 characters minimum for passwords that might be typed manually. Below 12 characters, even maximum complexity doesn't provide adequate protection against modern cracking hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  The User Experience of Generation
&lt;/h2&gt;

&lt;p&gt;How you present password generation matters as much as the generation itself. Users need to trust the process and understand what's happening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make it visible.&lt;/strong&gt; Show the generated password clearly, not hidden behind dots. Users need to see what they're accepting. A masked password field with a "show" toggle works well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provide copy functionality.&lt;/strong&gt; One-click copy to clipboard is essential. Users will paste this password into their password manager. Make that action effortless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explain the security.&lt;/strong&gt; A brief message like "This password would take millions of years to crack" builds confidence. Nobody wants to accept a string that looks like keyboard vomit on faith alone. Users who understand why the weird-looking string is better will be more likely to accept it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allow regeneration.&lt;/strong&gt; Some users will want to generate multiple options before choosing. Others might have superstitions about certain characters. Let them regenerate as many times as they want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Password Generation Failures
&lt;/h2&gt;

&lt;p&gt;What happens when the generation API is unavailable? Your application shouldn't break. Users shouldn't be stuck.&lt;/p&gt;

&lt;p&gt;The graceful approach is falling back to client-side generation. Modern browsers provide &lt;code&gt;crypto.getRandomValues()&lt;/code&gt;, which produces cryptographically secure random numbers. While a client-side fallback might not offer all the same features as an API (like complexity levels or avoiding ambiguous characters), it can produce a strong password in a pinch.&lt;/p&gt;

&lt;p&gt;Never let an API failure prevent users from completing signup. This is an enhancement, not a gate. Fail open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing and Transmitting Generated Passwords
&lt;/h2&gt;

&lt;p&gt;Generated passwords require the same security precautions as user-created ones. Hash before storing. Transmit over HTTPS. Never log in plaintext.&lt;/p&gt;

&lt;p&gt;The one difference: temporary passwords for reset flows need special handling. These should expire quickly—24 hours maximum, ideally much sooner. They should also force a password change on first use. A temporary password that becomes permanent defeats the purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring Success
&lt;/h2&gt;

&lt;p&gt;How do you know if password generation is working? Track adoption and security outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generation rate.&lt;/strong&gt; What percentage of new signups use generated passwords versus creating their own? If the answer is under 10%, your UX isn't compelling enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reset frequency.&lt;/strong&gt; Users with generated passwords should reset less often. If they're resetting more, something's wrong with your generation or storage flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Breach exposure.&lt;/strong&gt; When credentials appear in breach databases, how many of your users are affected? Generated passwords should appear far less frequently than user-created ones.&lt;/p&gt;

&lt;p&gt;These metrics help you tune the experience over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Example
&lt;/h2&gt;

&lt;p&gt;Here's a minimal example of generating a password via API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/passwordgenerator?count=1&amp;amp;length=16&amp;amp;complexity=strong&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passwords&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="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// "K#9mPx$2vLnR@4wQ"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Present this to the user with a copy button, let them accept it, and your signup security improves immediately.&lt;/p&gt;




&lt;p&gt;Generate secure passwords with the &lt;a href="https://apiverve.com/marketplace/passwordgenerator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=secure-password-generation-tutorial" rel="noopener noreferrer"&gt;Password Generator API&lt;/a&gt;. Verify password strength with the &lt;a href="https://apiverve.com/marketplace/passwordstrength?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=secure-password-generation-tutorial" rel="noopener noreferrer"&gt;Password Strength API&lt;/a&gt;. Build authentication flows that protect your users by default.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/secure-password-generation-tutorial" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>passwords</category>
      <category>javascript</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Generate PDFs from HTML with an API</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 09 Mar 2026 19:05:52 +0000</pubDate>
      <link>https://forem.com/apiverve/generate-pdfs-from-html-with-an-api-38om</link>
      <guid>https://forem.com/apiverve/generate-pdfs-from-html-with-an-api-38om</guid>
      <description>&lt;p&gt;At some point, every application needs to generate a PDF. It's never the exciting feature on the roadmap, but it's always there: invoices, reports, receipts, certificates. PDFs survive because they look the same everywhere—every device, every printer, every OS.&lt;/p&gt;

&lt;p&gt;The traditional path to generating them is painful. Install libraries, manage system dependencies, learn layout APIs that have nothing to do with how you build the rest of your application. Or spin up headless browsers, which means infrastructure just to render documents.&lt;/p&gt;

&lt;p&gt;The simpler approach: write HTML, send it to an &lt;a href="https://apiverve.com/marketplace/htmltopdf?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=html-to-pdf-generation-tutorial" rel="noopener noreferrer"&gt;HTML to PDF API&lt;/a&gt;, receive a PDF. Your existing CSS skills work. Your existing templating works. No binary dependencies to install or browser processes to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Already Know the Layout Language
&lt;/h2&gt;

&lt;p&gt;HTML and CSS were designed for document layout. Before the web, they were literally specification languages for formatting printed pages. The print stylesheet (&lt;code&gt;@media print&lt;/code&gt;) exists precisely because browsers have always known how to render documents for paper.&lt;/p&gt;

&lt;p&gt;This makes HTML a natural fit for PDF generation. You're not learning a new layout system—you're using the one you already know. Flexbox works. Grid works. Custom fonts work. Background colors, borders, tables—all of it translates directly.&lt;/p&gt;

&lt;p&gt;The only real shift is thinking in fixed dimensions instead of responsive ones. A PDF has a specific page size. Content needs to fit within margins. Page breaks should fall in sensible places. These constraints exist in CSS already; most developers just haven't used them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common PDF Use Cases
&lt;/h2&gt;

&lt;p&gt;Understanding what you're generating helps you design better templates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invoices&lt;/strong&gt; need to be legally compliant documents. They require specific information: seller details, buyer details, line items, totals, tax breakdowns, payment terms. The layout should be clean and professional because customers will see these. Many will file them for accounting purposes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reports&lt;/strong&gt; present data with context. They often include charts, tables, and explanatory text. Reports tend to be longer than other documents and may need headers, footers, and page numbers. The audience is usually internal—executives, analysts, stakeholders reviewing information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Receipts&lt;/strong&gt; are transactional records. They're typically short, confirming what was purchased, when, and for how much. Some receipts need to look like traditional paper receipts (monospace font, narrow width). Others can be more designed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Certificates&lt;/strong&gt; are ceremonial documents. Completion certificates, awards, diplomas—these are meant to be displayed or framed. They use formal typography, borders, and careful spacing. Landscape orientation is common.&lt;/p&gt;

&lt;p&gt;Each type has different design priorities. Invoices prioritize clarity and compliance. Reports prioritize information density. Receipts prioritize scannability. Certificates prioritize aesthetics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing for Print
&lt;/h2&gt;

&lt;p&gt;Web design assumes infinite vertical space and varying horizontal space. Print design assumes fixed dimensions on both axes. This shift requires a few adjustments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page size matters.&lt;/strong&gt; Letter (8.5" × 11") is standard in the US. A4 (210mm × 297mm) is standard elsewhere. Your PDF needs to target a specific size, and content must fit within it. Design your templates with these constraints in mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Margins create breathing room.&lt;/strong&gt; Print documents need margins—both for readability and because most printers can't print to the edge. Half-inch margins are a reasonable default. Formal documents might use one-inch margins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page breaks need control.&lt;/strong&gt; A table split awkwardly between pages looks unprofessional. CSS provides &lt;code&gt;page-break-before&lt;/code&gt;, &lt;code&gt;page-break-after&lt;/code&gt;, and &lt;code&gt;page-break-inside&lt;/code&gt; properties to control where breaks occur. Use &lt;code&gt;page-break-inside: avoid&lt;/code&gt; on elements that shouldn't be split.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Colors print differently.&lt;/strong&gt; That vibrant blue on screen might look washed out on paper. If documents will be printed frequently, test your colors on actual paper. Consider offering a "print-optimized" version with higher contrast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Template Architecture
&lt;/h2&gt;

&lt;p&gt;Separate your PDF templates from your application logic. Templates should be pure HTML with placeholder tokens that your code fills in before generation.&lt;/p&gt;

&lt;p&gt;This separation provides several benefits. Designers can modify templates without touching application code. Templates can be version-controlled independently. The same template can generate different documents by passing different data.&lt;/p&gt;

&lt;p&gt;A template for an invoice might have placeholders like &lt;code&gt;{{customer_name}}&lt;/code&gt;, &lt;code&gt;{{line_items}}&lt;/code&gt;, and &lt;code&gt;{{total}}&lt;/code&gt;. Your code retrieves the order data, fills in the placeholders, and sends the completed HTML for PDF generation.&lt;/p&gt;

&lt;p&gt;Keep templates simple. The moment you start putting business logic in a template, you've created a debugging nightmare. If a template needs conditional sections or loops, handle that logic before the template receives the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embedding Images and Fonts
&lt;/h2&gt;

&lt;p&gt;PDFs often need images—logos, signatures, charts. You have two options for embedding images in HTML that becomes a PDF.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;External URLs&lt;/strong&gt; work if the images are publicly accessible. The PDF generation service fetches them during rendering. This is simplest but requires public hosting for your images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Base64 encoding&lt;/strong&gt; embeds the image directly in the HTML. The image travels with the HTML, requiring no external fetch. This is more self-contained but increases the HTML payload size. For documents that need machine-readable identifiers, you can embed a &lt;a href="https://apiverve.com/marketplace/barcodegenerator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=html-to-pdf-generation-tutorial" rel="noopener noreferrer"&gt;generated barcode&lt;/a&gt; or &lt;a href="https://apiverve.com/marketplace/qrcodegenerator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=html-to-pdf-generation-tutorial" rel="noopener noreferrer"&gt;QR code&lt;/a&gt; image directly into the HTML before conversion.&lt;/p&gt;

&lt;p&gt;For logos and standard branding elements that appear on every document, base64 encoding makes sense. For dynamic content like charts or user-uploaded images, external URLs are more practical.&lt;/p&gt;

&lt;p&gt;Custom fonts work similarly. Reference them via Google Fonts or another CDN, or embed them as base64. Standard web fonts like Arial, Georgia, and Courier are always available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Page Layouts
&lt;/h2&gt;

&lt;p&gt;Different document types call for different layouts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portrait orientation&lt;/strong&gt; (tall) is the default and works for most documents. Invoices, letters, receipts, and most reports use portrait.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Landscape orientation&lt;/strong&gt; (wide) suits documents with wide tables or horizontal content. Financial spreadsheets, Gantt charts, and certificates often work better in landscape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-column layouts&lt;/strong&gt; can fit more content per page. Newsletters and some reports use two or three columns. CSS columns or Grid make this straightforward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headers and footers&lt;/strong&gt; provide consistent information across pages. Page numbers, document titles, dates, and company logos commonly appear in headers or footers. CSS has &lt;code&gt;@page&lt;/code&gt; rules for controlling headers and footers, though support varies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Long Content
&lt;/h2&gt;

&lt;p&gt;Some documents span many pages. Reports with extensive data, contracts with many sections, or invoices with hundreds of line items all need to handle pagination gracefully.&lt;/p&gt;

&lt;p&gt;The key is controlling where page breaks occur. You don't want a page break in the middle of a table row or between a heading and its first paragraph. CSS page break properties let you specify that certain elements should stay together.&lt;/p&gt;

&lt;p&gt;For very long tables, consider repeating the header row on each page. This makes the document usable even when printed—readers don't have to flip back to see what each column represents.&lt;/p&gt;

&lt;p&gt;Test your templates with maximum-length content. Honestly, this matters more than getting the styling pixel-perfect. If your invoice template works fine with 5 line items but breaks horribly with 50, you'll find out at the worst possible time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storage and Delivery
&lt;/h2&gt;

&lt;p&gt;Generated PDFs need to go somewhere. The generation API provides a temporary download URL, but that URL expires. For documents you need to keep, download the PDF and store it in your own system.&lt;/p&gt;

&lt;p&gt;Cloud storage (S3, Google Cloud Storage, Azure Blob Storage) is the typical choice. Store PDFs with organized paths: &lt;code&gt;/invoices/2026/03/INV-001234.pdf&lt;/code&gt;. This makes retrieval and cleanup straightforward.&lt;/p&gt;

&lt;p&gt;For immediate delivery (user clicks "Download Invoice"), you can stream the PDF directly from the generation API. For background generation (nightly reports, batch invoicing), generate the PDFs, store them, and email links to recipients.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generation Example
&lt;/h2&gt;

&lt;p&gt;The actual API call is minimal. Prepare your HTML, send it, receive a PDF:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/htmltopdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;invoiceHtml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;landscape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.downloadURL contains the PDF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of the work is in preparing the HTML. The generation itself? One API call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;PDF generation isn't instant. Complex documents with many images or large tables take longer to render. Plan for this in your user experience.&lt;/p&gt;

&lt;p&gt;For user-initiated generation (clicking a download button), show a loading indicator. Generation typically takes 1-5 seconds depending on complexity.&lt;/p&gt;

&lt;p&gt;For bulk generation (monthly invoice runs, batch reports), process documents in parallel but respect rate limits. Consider running batch jobs during off-peak hours.&lt;/p&gt;

&lt;p&gt;Cache generated PDFs when the underlying data won't change. An invoice for order #12345 will always contain the same information. Generate it once and serve the cached version on subsequent requests.&lt;/p&gt;




&lt;p&gt;Generate PDFs from HTML with the &lt;a href="https://apiverve.com/marketplace/htmltopdf?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=html-to-pdf-generation-tutorial" rel="noopener noreferrer"&gt;HTML to PDF API&lt;/a&gt;. Convert Markdown with the &lt;a href="https://apiverve.com/marketplace/markdowntopdf?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=html-to-pdf-generation-tutorial" rel="noopener noreferrer"&gt;Markdown to PDF API&lt;/a&gt;. Capture entire websites with the &lt;a href="https://apiverve.com/marketplace/websitetopdf?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=html-to-pdf-generation-tutorial" rel="noopener noreferrer"&gt;Website to PDF API&lt;/a&gt;. Build document workflows without the infrastructure headaches.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/html-to-pdf-generation-tutorial" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>documents</category>
      <category>pdf</category>
      <category>javascript</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Detect VPN and Proxy Users</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 09 Mar 2026 19:05:16 +0000</pubDate>
      <link>https://forem.com/apiverve/how-to-detect-vpn-and-proxy-users-ech</link>
      <guid>https://forem.com/apiverve/how-to-detect-vpn-and-proxy-users-ech</guid>
      <description>&lt;p&gt;Should you block a user just because they're on a VPN? Probably not. But should you ignore it entirely? Also no.&lt;/p&gt;

&lt;p&gt;VPNs create a real paradox: privacy-conscious users and fraudsters look identical at the network level. Both show up as traffic from an IP that isn't the user's real location. The question isn't whether VPN users are good or bad—it's what you do with that signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What VPN Detection Actually Tells You
&lt;/h2&gt;

&lt;p&gt;When you detect a VPN, you learn one thing with certainty: the IP address you see isn't where the user physically sits. Everything else is inference.&lt;/p&gt;

&lt;p&gt;This matters because IP-based assumptions underpin many security and business decisions. Geo-restrictions assume the IP reflects the user's country. Fraud scoring assumes the IP reflects the user's typical location. Abuse prevention—often paired with an &lt;a href="https://apiverve.com/marketplace/iplookup?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=detect-vpn-proxy-users-tutorial" rel="noopener noreferrer"&gt;IP lookup&lt;/a&gt; for location context—assumes the IP can identify repeat offenders.&lt;/p&gt;

&lt;p&gt;VPN detection doesn't tell you the user is malicious. It tells you that your IP-based assumptions might be wrong. That's valuable information, but it requires nuance in how you respond.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not All Hidden IPs Are Created Equal
&lt;/h2&gt;

&lt;p&gt;Not all IP masking is equal, and treating it that way is one of the most common mistakes teams make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consumer VPNs&lt;/strong&gt; are the most common. Services like NordVPN, ExpressVPN, and Surfshark provide privacy for everyday users. Someone on a consumer VPN might be protecting themselves on public WiFi, bypassing regional content restrictions, or just valuing privacy generally. Consumer VPN usage alone is a weak fraud signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Datacenter IPs&lt;/strong&gt; are more concerning. When traffic comes from AWS, Google Cloud, or DigitalOcean, it's almost never a regular user browsing from their laptop. Datacenter IPs typically indicate automation—bots, scrapers, or programmatic access. A &lt;a href="https://apiverve.com/marketplace/botdetector?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=detect-vpn-proxy-users-tutorial" rel="noopener noreferrer"&gt;bot detector&lt;/a&gt; can help distinguish legitimate automation from malicious automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tor exit nodes&lt;/strong&gt; represent the strongest anonymity. Tor users have deliberately chosen a system designed to make tracing impossible. While Tor has legitimate uses (journalists in authoritarian countries, whistleblowers, privacy advocates), it's also the tool of choice for serious bad actors. Tor traffic warrants the highest scrutiny.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Residential proxies&lt;/strong&gt; are the hardest to detect. These route traffic through real residential IP addresses, making it appear as normal consumer traffic. They're expensive and primarily used for sophisticated operations—either legitimate (price comparison, ad verification) or illegitimate (credential stuffing, sneaker bots).&lt;/p&gt;

&lt;p&gt;If you take one thing from this section: the type of masking matters far more than the fact of masking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Blocking
&lt;/h2&gt;

&lt;p&gt;Many developers' first instinct is to block VPN users entirely. This seems logical—if VPNs enable fraud, just prevent VPN access.&lt;/p&gt;

&lt;p&gt;In practice, blanket blocking creates more problems than it solves. You'll block:&lt;/p&gt;

&lt;p&gt;Privacy-conscious professionals who use corporate VPNs for work and leave them connected during personal browsing. Travelers who use VPNs for security on hotel and airport networks. Users in countries where VPNs are necessary for accessing uncensored internet. Security researchers and journalists who have legitimate reasons for anonymity.&lt;/p&gt;

&lt;p&gt;Each blocked legitimate user is a lost customer and a support ticket. At scale, this friction compounds into significant business impact.&lt;/p&gt;

&lt;p&gt;And here's the frustrating part: determined fraudsters will find ways around your blocks anyway. They'll use residential proxies you can't detect. They'll rotate through IPs until they find ones you haven't flagged. Blocking stops the casual abusers while the sophisticated ones—the ones causing real damage—adapt and continue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Risk Scoring Instead of Blocking
&lt;/h2&gt;

&lt;p&gt;The mature approach treats VPN detection as one signal among many. VPN usage adds points to a risk score. Other signals add more points. High-risk combinations trigger friction or review.&lt;/p&gt;

&lt;p&gt;Consider this framework:&lt;/p&gt;

&lt;p&gt;A new account signing up from a consumer VPN with a valid email address? Low additional risk. Might just be a privacy-conscious user.&lt;/p&gt;

&lt;p&gt;A new account from a datacenter IP with a disposable email address? Moderate risk. The combination is suspicious even if neither signal alone would be.&lt;/p&gt;

&lt;p&gt;A new account from a datacenter IP, disposable email, making a high-value purchase with expedited shipping to an address different from the billing address? High risk. This pattern matches known fraud behavior.&lt;/p&gt;

&lt;p&gt;The VPN signal contributes to the assessment but doesn't determine it alone. This is the part most teams skip—building the scoring model—and it's also the part that actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Geo-Restriction Enforcement
&lt;/h2&gt;

&lt;p&gt;Some applications must enforce geographic restrictions. Streaming services have content licensed only for specific countries. Online gambling sites must comply with jurisdictional regulations. Financial services face different rules in different regions.&lt;/p&gt;

&lt;p&gt;VPN detection becomes more important here because users actively try to circumvent restrictions. Someone in Germany using a US VPN to access US-only content is intentionally violating the restriction. Unlike fraud prevention, where VPN usage is an indirect signal, geo-restriction enforcement directly cares whether the stated location is real.&lt;/p&gt;

&lt;p&gt;Even here, blanket blocking has drawbacks. A US user traveling abroad might use a VPN to access their US-based services. They're not circumventing restrictions—they're maintaining access to services they legitimately subscribe to from their home country.&lt;/p&gt;

&lt;p&gt;The cleanest approach is transparency. Tell users that VPN access is restricted and why. Offer them the option to disable their VPN and retry. This respects their choice while maintaining compliance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Datacenter IP Signal
&lt;/h2&gt;

&lt;p&gt;Datacenter IPs deserve special attention because they're almost never legitimate consumer traffic. Regular users browse from home connections, mobile networks, or corporate networks—not from cloud servers.&lt;/p&gt;

&lt;p&gt;When you see traffic from a datacenter IP, you're likely seeing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bots and scrapers.&lt;/strong&gt; Automated systems that collect data, test credentials, or interact with your application programmatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proxy services.&lt;/strong&gt; Some proxy services route traffic through datacenter infrastructure rather than residential IPs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legitimate automation.&lt;/strong&gt; Monitoring services, integration testing, webhook deliveries. These are valid but should probably be authenticated differently than user traffic.&lt;/p&gt;

&lt;p&gt;The response to datacenter traffic can be stricter than the response to consumer VPNs. Rate limiting is almost always appropriate. Additional authentication challenges are reasonable. For high-risk operations like account creation or purchase completion, requiring a non-datacenter IP is defensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Patterns
&lt;/h2&gt;

&lt;p&gt;The technical implementation is straightforward. Check the user's IP against a &lt;a href="https://apiverve.com/marketplace/vpndetector?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=detect-vpn-proxy-users-tutorial" rel="noopener noreferrer"&gt;VPN detection API&lt;/a&gt; on requests where you need the information—typically authentication, checkout, and account creation endpoints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://api.apiverve.com/v1/vpndetector?ip=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;clientIp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.is_vpn - boolean, is this a VPN exit node?&lt;/span&gt;
&lt;span class="c1"&gt;// data.is_datacenter - boolean, is this a cloud/hosting IP?&lt;/span&gt;
&lt;span class="c1"&gt;// data.risk_level - string, overall risk assessment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cache results by IP address. VPN status doesn't change frequently—an hour TTL is reasonable. This reduces API calls and latency for repeat visitors.&lt;/p&gt;

&lt;p&gt;Attach the results to the request context so downstream code can access it. Your fraud scoring, rate limiting, and access control logic can all reference the same detection data without redundant API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Not to Do
&lt;/h2&gt;

&lt;p&gt;A few anti-patterns to avoid:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't show accusatory messages.&lt;/strong&gt; Seriously, don't. "We detected you're using a VPN" sounds like an accusation. Users who aren't doing anything wrong will be offended. Users who are doing something wrong won't be deterred.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't rely solely on VPN detection for security.&lt;/strong&gt; It's one signal. Sophisticated attackers use residential proxies, compromised consumer devices, and other techniques that evade detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't log VPN status with personally identifiable information.&lt;/strong&gt; This creates a record that could be subpoenaed or breached. Log aggregate statistics if you need them for analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't update your detection database infrequently.&lt;/strong&gt; VPN providers constantly add new exit nodes. Tor exit nodes rotate regularly. Your detection needs fresh data to stay accurate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring Effectiveness
&lt;/h2&gt;

&lt;p&gt;Track the impact of VPN detection on your fraud and abuse metrics. Are you catching more bad actors? Are you creating friction for legitimate users?&lt;/p&gt;

&lt;p&gt;Monitor false positive rates by correlating VPN detection with downstream outcomes. If accounts flagged for VPN usage have the same fraud rate as unflagged accounts, your threshold might be wrong.&lt;/p&gt;

&lt;p&gt;Compare chargebacks and abuse reports between VPN and non-VPN traffic. If VPN users have higher rates of problematic behavior, your detection is providing useful signal. If rates are similar, you might be adding friction without benefit.&lt;/p&gt;




&lt;p&gt;Detect VPNs and proxies with the &lt;a href="https://apiverve.com/marketplace/vpndetector?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=detect-vpn-proxy-users-tutorial" rel="noopener noreferrer"&gt;VPN Detector API&lt;/a&gt;. Identify Tor exit nodes with the &lt;a href="https://apiverve.com/marketplace/tordetect?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=detect-vpn-proxy-users-tutorial" rel="noopener noreferrer"&gt;Tor Detector API&lt;/a&gt;. Get complete IP intelligence with the &lt;a href="https://apiverve.com/marketplace/iplookup?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=detect-vpn-proxy-users-tutorial" rel="noopener noreferrer"&gt;IP Lookup API&lt;/a&gt;. Build smarter risk assessment.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/detect-vpn-proxy-users-tutorial" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>fraudprevention</category>
      <category>javascript</category>
      <category>ipdetection</category>
    </item>
    <item>
      <title>Generate Barcodes for Inventory and Shipping</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Mon, 09 Mar 2026 19:04:39 +0000</pubDate>
      <link>https://forem.com/apiverve/generate-barcodes-for-inventory-and-shipping-2a8p</link>
      <guid>https://forem.com/apiverve/generate-barcodes-for-inventory-and-shipping-2a8p</guid>
      <description>&lt;p&gt;Pick up any product near you. There's a barcode on it. That pattern of black and white lines is doing more work than most features you'll build this quarter—routing packages, tracking inventory, linking physical objects to database records.&lt;/p&gt;

&lt;p&gt;For developers building inventory systems, shipping workflows, or asset trackers, barcode generation is table stakes. When products enter your system, they need barcodes. When packages ship, tracking numbers need to become scannable labels. When equipment gets tagged, asset IDs need physical representation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bars, Spaces, and Why Width Matters
&lt;/h2&gt;

&lt;p&gt;A barcode encodes data as a pattern of bars and spaces of varying widths. Scanners read these patterns by measuring the reflectivity of each segment—dark bars absorb light, white spaces reflect it. The sequence of measurements translates back into the original data.&lt;/p&gt;

&lt;p&gt;Different barcode symbologies (formats) use different encoding schemes. Some encode only numbers. Some encode letters and numbers. Some can represent any data including binary. The choice of symbology depends on what you're encoding and where the barcode will be scanned.&lt;/p&gt;

&lt;p&gt;The physical appearance matters too. Barcodes need sufficient contrast between bars and background. They need minimum sizing to be scannable at practical distances. They need quiet zones (blank space) around them so scanners know where the barcode begins and ends.&lt;/p&gt;

&lt;p&gt;Skip any of these details and you'll end up with barcodes that look right on screen but fail the moment a scanner tries to read them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code 128: The Modern Standard
&lt;/h2&gt;

&lt;p&gt;Code 128 is the most widely used barcode symbology for logistics and inventory. It's compact, efficient, and supports the full ASCII character set—letters, numbers, and symbols.&lt;/p&gt;

&lt;p&gt;The compactness matters. Code 128 encodes more data per inch than older symbologies. A Code 128 barcode containing "SKU-12345678" is physically shorter than the same data in Code 39. Shorter barcodes fit on smaller labels and scan more reliably at angles.&lt;/p&gt;

&lt;p&gt;Code 128 automatically switches between encoding modes to optimize density. Sequences of numbers encode more compactly than sequences of letters. The symbology handles this internally—you just provide the data.&lt;/p&gt;

&lt;p&gt;For most applications—product labels, shipping labels, inventory tags—Code 128 is the right choice. It's supported by virtually every scanner and works across all industries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code 39: The Legacy Workhorse
&lt;/h2&gt;

&lt;p&gt;Code 39 is older and less efficient than Code 128, but it remains common in certain industries. The US Department of Defense uses Code 39 for military asset tracking. Many automotive manufacturers specify Code 39 in their supply chain requirements.&lt;/p&gt;

&lt;p&gt;Code 39 encodes uppercase letters, numbers, and a few special characters. It's self-checking, meaning a single misread bar won't produce valid-looking garbage—it will produce an invalid barcode that the scanner rejects.&lt;/p&gt;

&lt;p&gt;The trade-off is density. Code 39 barcodes are physically wider than Code 128 for the same data. A 15-character asset ID in Code 39 might be twice as wide as Code 128.&lt;/p&gt;

&lt;p&gt;Use Code 39 when external requirements mandate it. For new systems without legacy constraints, Code 128 is generally better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing Barcode Labels
&lt;/h2&gt;

&lt;p&gt;A barcode by itself is just a pattern. Useful labels combine the barcode with human-readable information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human-readable data&lt;/strong&gt; appears below the barcode. This is the same data encoded in the barcode, printed as text. When scanners fail, humans can read the number and enter it manually. Include this on every label.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contextual information&lt;/strong&gt; surrounds the barcode. For a product label, this might include the product name, price, or size. For a shipping label, the recipient address and service level. The barcode identifies; the context explains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layout matters for scanning.&lt;/strong&gt; Barcodes need quiet zones—blank space on either side. They need minimum height for reliable scanning. They need contrast with the background. A barcode crammed into a corner with no margins will cause scanning failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Print quality affects reliability.&lt;/strong&gt; Thermal printers produce excellent barcodes. Inkjet printers work but require good paper. Laser printers sometimes produce barcodes with insufficient contrast. Test with real printers and real scanners before deploying—this is the step that saves you from warehouse chaos on launch day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inventory and SKU Labels
&lt;/h2&gt;

&lt;p&gt;Inventory management systems typically assign SKU (Stock Keeping Unit) codes to products. These SKUs become barcodes on product labels.&lt;/p&gt;

&lt;p&gt;SKU formats vary by organization. Some use sequential numbers. Some encode product attributes into the SKU structure. Some combine vendor codes with internal identifiers. Whatever your SKU format, it becomes barcode content.&lt;/p&gt;

&lt;p&gt;The workflow looks like this: A new product enters inventory. The system assigns a SKU. The system generates a barcode encoding that SKU. The barcode prints on a label. The label attaches to the product or its storage location.&lt;/p&gt;

&lt;p&gt;From that point on, the barcode is the bridge. Scanning a product during receiving updates quantity counts. Scanning during picking confirms the right product. Scanning during shipping records what went out. The barcode is the link between physical items and database records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shipping and Tracking Labels
&lt;/h2&gt;

&lt;p&gt;Shipping labels present specific requirements. They're affixed to packages that travel through rough environments. They're scanned by many different parties—your warehouse, the carrier, intermediate hubs, the recipient.&lt;/p&gt;

&lt;p&gt;Tracking numbers encode into barcodes on shipping labels. These numbers are typically assigned by carriers, though some shippers generate their own for internal tracking before carrier handoff.&lt;/p&gt;

&lt;p&gt;The label usually includes multiple barcodes. A large barcode for the tracking number. Smaller barcodes for routing information. These redundant encodings help when packages are damaged or when different scanners have different capabilities.&lt;/p&gt;

&lt;p&gt;Label placement affects scanning. Labels should be on flat surfaces, not wrapped around corners. They should be visible without opening or manipulating the package. Standard placement (top of package for flat items, side for boxes) helps automated scanning systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asset Tracking Tags
&lt;/h2&gt;

&lt;p&gt;Organizations track equipment, tools, vehicles, and other assets with barcode tags. Unlike inventory that flows in and out, assets typically stay within the organization but move between locations and users.&lt;/p&gt;

&lt;p&gt;Asset IDs often encode more than just a number. They might include category codes, location codes, or department identifiers. An asset ID like "IT-LAP-2024-0047" encodes the department (IT), asset type (laptop), year, and sequence. This structure helps humans understand the ID even without looking it up.&lt;/p&gt;

&lt;p&gt;Asset tags need durability. Equipment lasts years. Tags must survive handling, cleaning, and environmental conditions. Metal asset tags with engraved barcodes work for harsh environments. Laminated labels work for office equipment. Matching tag durability to asset lifecycle prevents premature tag failure.&lt;/p&gt;

&lt;p&gt;Periodic scanning maintains asset databases. Annual inventories scan all equipment. Check-out processes scan before equipment leaves. Check-in processes scan on return. Each scan updates location and status.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batch Generation
&lt;/h2&gt;

&lt;p&gt;Many workflows need many barcodes at once. A new shipment of products needs labels for every item. A warehouse reorganization needs location labels for every bin. An IT refresh needs asset tags for every device.&lt;/p&gt;

&lt;p&gt;Batch generation creates multiple barcodes efficiently. Rather than generating one at a time, generate all needed barcodes in a single operation. This is faster and ensures consistency—all labels from the same batch look identical.&lt;/p&gt;

&lt;p&gt;Batch output might be individual images or a print-ready document. Individual images work when labels print separately or integrate into other documents. For print-ready output, you can combine generated barcodes with an &lt;a href="https://apiverve.com/marketplace/htmltopdf?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=barcode-generation-tutorial" rel="noopener noreferrer"&gt;HTML to PDF conversion&lt;/a&gt; to produce label sheets that print all at once.&lt;/p&gt;

&lt;p&gt;Label sheets follow standard formats. Avery 5160 provides 30 labels per letter-sized page. Avery 5163 provides 10 larger labels. Matching your output to your label stock saves time and materials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Example
&lt;/h2&gt;

&lt;p&gt;Generating a barcode through an API is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/barcodegenerator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SKU-12345678&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code128&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;displayValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// data.downloadURL contains the barcode image&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;displayValue&lt;/code&gt; option prints the encoded data below the barcode for human readability. This is almost always what you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scanner Compatibility
&lt;/h2&gt;

&lt;p&gt;Not all scanners read all symbologies. Before deploying barcodes, verify that your scanners support your chosen format.&lt;/p&gt;

&lt;p&gt;Most modern scanners support Code 128 and Code 39 without configuration. Older scanners might need symbology enabled in settings. Smartphone apps generally support common symbologies but may struggle with damaged or poorly printed barcodes.&lt;/p&gt;

&lt;p&gt;Scanner distance matters. Handheld scanners read at arm's length. Fixed-mount scanners (like those at checkout lanes) read at specific focal distances. Larger barcodes scan from farther away. Match barcode size to scanning distance.&lt;/p&gt;

&lt;p&gt;And again: test in realistic conditions. A barcode that scans perfectly on your desk might fail when printed on glossy label stock. A barcode that scans in good lighting might fail in a dim warehouse corner. Test where barcodes will actually be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Linear Barcodes
&lt;/h2&gt;

&lt;p&gt;Linear barcodes (the traditional bar patterns) work well for simple identifiers but have limitations. They encode limited data. They require line-of-sight scanning—you can't scan through plastic or read a damaged bar.&lt;/p&gt;

&lt;p&gt;QR codes and other 2D symbologies encode more data in the same space. They include error correction—partially damaged codes still scan. A &lt;a href="https://apiverve.com/marketplace/qrcodegenerator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=barcode-generation-tutorial" rel="noopener noreferrer"&gt;QR code generator&lt;/a&gt; can encode URLs, contact information, or structured data.&lt;/p&gt;

&lt;p&gt;For simple identification (SKUs, tracking numbers, asset IDs), linear barcodes remain ideal. For richer data (product URLs, configuration data, complex identifiers), consider QR codes instead.&lt;/p&gt;




&lt;p&gt;Generate barcodes with the &lt;a href="https://apiverve.com/marketplace/barcodegenerator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=barcode-generation-tutorial" rel="noopener noreferrer"&gt;Barcode Generator API&lt;/a&gt;. Create QR codes with the &lt;a href="https://apiverve.com/marketplace/qrcodegenerator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=barcode-generation-tutorial" rel="noopener noreferrer"&gt;QR Code Generator API&lt;/a&gt;. Build inventory and logistics systems that work at scale.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/barcode-generation-tutorial" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>barcodes</category>
      <category>ecommerce</category>
      <category>javascript</category>
      <category>inventory</category>
    </item>
    <item>
      <title>Email Validation Best Practices: Beyond Simple Regex</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Wed, 04 Mar 2026 21:36:31 +0000</pubDate>
      <link>https://forem.com/apiverve/email-validation-best-practices-beyond-simple-regex-491l</link>
      <guid>https://forem.com/apiverve/email-validation-best-practices-beyond-simple-regex-491l</guid>
      <description>&lt;p&gt;A customer types their perfectly valid email address into your signup form. Your validation rejects it. They don't contact support—they just leave. You never know it happened.&lt;/p&gt;

&lt;p&gt;This is more common than most teams realize. Overly strict email validation quietly kills conversions while giving the false comfort that your data is "clean." Meanwhile, the truly fake addresses—typos, disposable inboxes, nonexistent domains—sail right through a basic regex check.&lt;/p&gt;

&lt;p&gt;Getting validation right means catching the junk without blocking the real people. That balance is harder than it sounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Surprisingly Permissive Email Spec
&lt;/h2&gt;

&lt;p&gt;The email address specification (defined in RFC 5321 and RFC 5322) is more permissive than most people realize. Valid email addresses can include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dots in various positions&lt;/strong&gt; - Both &lt;code&gt;first.last@example.com&lt;/code&gt; and &lt;code&gt;f.i.r.s.t@example.com&lt;/code&gt; are valid. Dots can appear almost anywhere in the local part (the part before the @).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plus signs for sub-addressing&lt;/strong&gt; - &lt;code&gt;user+newsletter@gmail.com&lt;/code&gt; is valid and widely used. Gmail and other providers use the plus sign to create aliases that deliver to the main address. Many users employ this for filtering and tracking which services share their email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apostrophes and other special characters&lt;/strong&gt; - &lt;code&gt;o'brien@company.ie&lt;/code&gt; is perfectly valid. Irish names, French names, and others frequently include apostrophes. Hyphens, underscores, and various other characters are also permitted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Numeric local parts&lt;/strong&gt; - &lt;code&gt;12345@example.com&lt;/code&gt; is valid. Some organizations use numeric identifiers as email addresses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Long top-level domains&lt;/strong&gt; - Modern TLDs go far beyond .com and .org. Addresses like &lt;code&gt;user@company.photography&lt;/code&gt; or &lt;code&gt;contact@brand.engineering&lt;/code&gt; are valid and increasingly common.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;International characters&lt;/strong&gt; - The email specification now supports internationalized email addresses with non-ASCII characters. &lt;code&gt;用户@例え.jp&lt;/code&gt; is a valid email address format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IP address domains&lt;/strong&gt; - Technically, &lt;code&gt;user@[192.168.1.1]&lt;/code&gt; is valid, though rarely used in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quoted local parts&lt;/strong&gt; - &lt;code&gt;"john doe"@example.com&lt;/code&gt; with spaces inside quotes is valid per the specification.&lt;/p&gt;

&lt;p&gt;Every restriction you add to email validation potentially rejects someone's real, working email address. That's worth sitting with for a moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Simple Regex Fails
&lt;/h2&gt;

&lt;p&gt;The internet is full of email validation regex patterns, ranging from simple to absurdly complex. Most of them cause problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple patterns reject valid addresses.&lt;/strong&gt; A pattern that only allows alphanumeric characters, dots, and @ symbols will reject plus signs, apostrophes, and other valid characters. Users with these addresses can't sign up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complex patterns are unmaintainable.&lt;/strong&gt; The regex needed to fully match the email specification is hundreds of characters long and virtually impossible to debug. Even then, it only validates format—not whether the address actually works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All regex only validates format.&lt;/strong&gt; Whether simple or complex, regex can only answer "does this string match a pattern?" It cannot answer "does this email address receive mail?" which is usually the question that actually matters.&lt;/p&gt;

&lt;p&gt;Format validation catches obviously malformed input—missing @ symbols, empty strings, addresses that are clearly not emails. For anything beyond that, format validation alone is insufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deliverability vs. Format Validity
&lt;/h2&gt;

&lt;p&gt;Understanding the difference between format validity and deliverability is crucial for email validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Format validity&lt;/strong&gt; asks: Does this string conform to the email address specification? This is what regex checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deliverability&lt;/strong&gt; asks: If I send an email to this address, will it arrive? This is what usually matters.&lt;/p&gt;

&lt;p&gt;An email address can be perfectly formatted and completely undeliverable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typos in the domain&lt;/strong&gt; - &lt;code&gt;user@gmial.com&lt;/code&gt; passes format validation. Gmail doesn't own gmial.com. The email will never arrive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-existent domains&lt;/strong&gt; - &lt;code&gt;user@thisdoesnotexist12345.com&lt;/code&gt; looks like an email address. But if the domain doesn't exist or has no mail servers configured, no email can be delivered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-existent mailboxes&lt;/strong&gt; - &lt;code&gt;randomstring8472@gmail.com&lt;/code&gt; has correct format and a valid domain. But if no one has registered that Gmail account, emails bounce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disabled or full mailboxes&lt;/strong&gt; - The address once worked but the account was closed, or the mailbox is full and rejecting new messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spam traps&lt;/strong&gt; - Some email addresses exist specifically to catch spammers. Sending to them damages your reputation.&lt;/p&gt;

&lt;p&gt;Format validation catches maybe 5% of email problems. Just 5%. Deliverability validation catches the rest.&lt;/p&gt;

&lt;p&gt;A proper email validation API returns comprehensive results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.apiverve.com/v1/emailvalidator?email=user@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="c1"&gt;// Check deliverability, not just format&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isMxValid&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSmtpValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Email is likely deliverable&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// data also includes:&lt;/span&gt;
&lt;span class="c1"&gt;// - isFreeEmail: true for Gmail, Yahoo, etc.&lt;/span&gt;
&lt;span class="c1"&gt;// - isCompanyEmail: true for business domains&lt;/span&gt;
&lt;span class="c1"&gt;// - hasTypo: true if domain looks like a typo (gmial.com)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells you not just whether the format is correct, but whether the domain has mail servers, whether those servers accept connections, and whether it's a business or free email provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking Email Deliverability
&lt;/h2&gt;

&lt;p&gt;Real email validation goes beyond format checking to verify deliverability through multiple steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DNS lookup&lt;/strong&gt; - Does the domain exist? Every email domain must have DNS records. If the domain doesn't resolve, no email can be delivered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MX record check&lt;/strong&gt; - Does the domain have mail servers configured? The MX (Mail Exchanger) records specify which servers handle email for a domain. No MX records usually means no email capability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SMTP verification&lt;/strong&gt; - Can you connect to the mail server? Does it accept mail for this address? Some mail servers will tell you whether a specific mailbox exists. Others refuse to answer (to prevent address enumeration attacks).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reputation assessment&lt;/strong&gt; - Is this domain associated with spam or fraud? Are deliverability rates historically low?&lt;/p&gt;

&lt;p&gt;These checks require network requests and can't be done with client-side regex. They typically take 1-3 seconds per address, which is acceptable for form submission but too slow for real-time validation on every keystroke.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disposable Email Detection
&lt;/h2&gt;

&lt;p&gt;Disposable email services provide temporary addresses that expire after minutes or hours. Popular services include Guerrilla Mail, 10 Minute Mail, Temp Mail, Mailinator, and hundreds of others.&lt;/p&gt;

&lt;p&gt;People use disposable emails for various reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoiding spam&lt;/strong&gt; - Signing up for services that might sell their email or send unwanted messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-time access&lt;/strong&gt; - Downloading content or accessing gated material without providing a real address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt; - Developers testing email flows without cluttering their real inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abuse&lt;/strong&gt; - Creating multiple accounts to exploit free trials, accumulate referral bonuses, evade bans, or engage in fraud.&lt;/p&gt;

&lt;p&gt;Whether to block disposable emails depends on your use case:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trial signups&lt;/strong&gt; - If you're offering a free trial and want a relationship with the user, blocking disposables makes sense. Users who won't provide a real email are unlikely to convert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Newsletter signups&lt;/strong&gt; - Blocking might cost you some legitimate subscribers who are just cautious about spam.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Account creation&lt;/strong&gt; - For platforms where account value builds over time, requiring a permanent email address is reasonable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-time downloads&lt;/strong&gt; - Blocking disposables might be unnecessary friction. The transaction is complete; you may not need ongoing communication.&lt;/p&gt;

&lt;p&gt;Disposable email detection requires maintaining an updated database of disposable domains. New services appear constantly, so static lists quickly become outdated. API-based detection stays current.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Common Typos
&lt;/h2&gt;

&lt;p&gt;Beyond validation, detecting and suggesting corrections for common typos improves user experience and data quality.&lt;/p&gt;

&lt;p&gt;The most frequently mistyped email domains include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gmial.com&lt;/code&gt; instead of &lt;code&gt;gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gmal.com&lt;/code&gt; instead of &lt;code&gt;gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gnail.com&lt;/code&gt; instead of &lt;code&gt;gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hotmial.com&lt;/code&gt; instead of &lt;code&gt;hotmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yaho.com&lt;/code&gt; instead of &lt;code&gt;yahoo.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;outlok.com&lt;/code&gt; instead of &lt;code&gt;outlook.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.con&lt;/code&gt; instead of &lt;code&gt;.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you detect a likely typo, showing "Did you mean gmail.com?" converts a future bounce into a successful signup. The user appreciates the help, and you get a working email address.&lt;/p&gt;

&lt;p&gt;Typo detection should suggest, not auto-correct. Users might have legitimate addresses at unusual domains. Let them confirm the correction rather than silently changing what they typed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation Timing and User Experience
&lt;/h2&gt;

&lt;p&gt;When and how you validate affects user experience as much as what you validate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't validate on every keystroke.&lt;/strong&gt; Red error messages appearing while the user is still typing are distracting and frustrating. Wait until they've finished—on blur (when they click away from the field) or on form submission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validate asynchronously when possible.&lt;/strong&gt; Deep validation takes time. Start the validation when the user finishes typing the email, so results are ready by the time they submit the form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provide specific, actionable feedback.&lt;/strong&gt; "Invalid email" tells users nothing. "Please include an @ symbol" identifies the problem. "Did you mean gmail.com?" solves it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distinguish between definite problems and warnings.&lt;/strong&gt; A missing @ symbol is definitely wrong. A new domain you can't verify might be fine—warn but allow submission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remember that you might be wrong.&lt;/strong&gt; If your validation rejects &lt;code&gt;o'brien@company.ie&lt;/code&gt;, you're the one with the bug, not the user. Build in escape hatches for edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different Validation for Different Contexts
&lt;/h2&gt;

&lt;p&gt;Not all email collection points need the same validation rigor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Account signup&lt;/strong&gt; requires thorough validation. You need to send activation emails, password resets, and account notifications. An invalid email breaks the entire user experience. Full deliverability checking is justified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkout and transactions&lt;/strong&gt; also warrant careful validation. Order confirmations, shipping notifications, and receipts need to reach the customer. The transaction's value justifies the validation overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Newsletter subscription&lt;/strong&gt; can use lighter validation. A bad email just means one undelivered newsletter. The cost of false rejection (losing a subscriber) may exceed the cost of false acceptance (one bounce).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contact forms&lt;/strong&gt; often need only basic validation. Honestly, full deliverability checking here is overkill. You're going to read and respond manually anyway. As long as the address looks plausible, you can handle problems individually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profile updates&lt;/strong&gt; should validate the new address thoroughly before replacing the old one. Consider requiring confirmation of both addresses—sending a verification to the new one and a notification to the old one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Role-Based and Group Addresses
&lt;/h2&gt;

&lt;p&gt;Some email addresses are technically valid but serve different purposes than individual mailboxes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role addresses&lt;/strong&gt; like &lt;code&gt;info@&lt;/code&gt;, &lt;code&gt;support@&lt;/code&gt;, &lt;code&gt;admin@&lt;/code&gt;, &lt;code&gt;sales@&lt;/code&gt;, &lt;code&gt;webmaster@&lt;/code&gt; go to teams or rotate among staff. They're valid for business communication but may not be ideal for individual user accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Group addresses&lt;/strong&gt; deliver to multiple recipients. Perfectly valid, but again not individual accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-responders&lt;/strong&gt; reply automatically to incoming messages. Sending transactional emails to these addresses generates noise.&lt;/p&gt;

&lt;p&gt;Whether these matter depends on context. For a B2B service, role addresses are normal and expected. For a consumer app expecting individual users, they might warrant gentle discouragement (not hard blocking).&lt;/p&gt;

&lt;h2&gt;
  
  
  Free vs. Business Email Providers
&lt;/h2&gt;

&lt;p&gt;Email validation can distinguish between free providers (Gmail, Yahoo, Outlook, etc.) and business domains (company-specific addresses).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free email addresses&lt;/strong&gt; are perfectly legitimate for consumers. They're also slightly higher risk for fraud, since creating new accounts is easy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business email addresses&lt;/strong&gt; suggest professional context and are slightly harder to create in bulk. For B2B applications, business emails may indicate more serious prospects.&lt;/p&gt;

&lt;p&gt;This distinction is useful for segmentation and prioritization, not for blocking. Plenty of legitimate business users use personal email addresses, especially for initial inquiries or small businesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Validation Failures Gracefully
&lt;/h2&gt;

&lt;p&gt;When validation fails, how you communicate matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be specific about the problem.&lt;/strong&gt; "We couldn't verify this email address exists. Please check for typos." is better than "Invalid email."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offer suggestions when possible.&lt;/strong&gt; "Did you mean @gmail.com?" helps users fix the actual problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allow override with acknowledgment.&lt;/strong&gt; "We couldn't verify this address. If you're sure it's correct, click Continue." respects user agency while flagging potential issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't lecture or blame.&lt;/strong&gt; "You entered an invalid email" sounds accusatory. "Please check your email address" is neutral.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make errors visible but not alarming.&lt;/strong&gt; Red text is traditional for errors, but a subtle color change is often sufficient. Save the bold red for critical problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Business Case for Good Validation
&lt;/h2&gt;

&lt;p&gt;Poor email validation has measurable costs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bounced emails&lt;/strong&gt; damage sender reputation. High bounce rates lead to deliverability problems where even valid emails land in spam folders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invalid data&lt;/strong&gt; pollutes your database. Fake addresses skew metrics and waste resources on campaigns that can't reach anyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lost customers&lt;/strong&gt; leave when signup fails. A user whose valid email gets rejected will often abandon rather than contact support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Support burden&lt;/strong&gt; increases when users can't sign up or receive emails. These tickets could be prevented by better validation.&lt;/p&gt;

&lt;p&gt;Good validation pays for itself in cleaner data, better deliverability, fewer support tickets, and more successful user signups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Effective email validation is layered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First layer: Basic format check.&lt;/strong&gt; Is there an @ symbol? Is there content on both sides? Does the domain have a dot? This catches obvious mistakes instantly with no external calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second layer: Deliverability verification.&lt;/strong&gt; Does the domain exist? Does it have mail servers? Can you reach them? This catches typos, fake domains, and non-functional addresses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third layer: Quality assessment.&lt;/strong&gt; Is it disposable? Is it a role address? Is it from a free provider? This provides context for how to treat the address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fourth layer: User experience.&lt;/strong&gt; Can you suggest corrections? Can you explain problems clearly? Can you allow edge cases while flagging concerns?&lt;/p&gt;

&lt;p&gt;Each layer builds on the previous. Skip a layer, and you miss a category of problems. Implement all layers thoughtfully, and you get clean data without frustrating legitimate users.&lt;/p&gt;




&lt;p&gt;Validate email addresses comprehensively with the &lt;a href="https://apiverve.com/marketplace/emailvalidator?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=why-your-email-validation-rejects-real-customers" rel="noopener noreferrer"&gt;Email Validator API&lt;/a&gt;. Detect disposable addresses with the &lt;a href="https://apiverve.com/marketplace/disposableemailchecker?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=why-your-email-validation-rejects-real-customers" rel="noopener noreferrer"&gt;Disposable Email Checker API&lt;/a&gt;. Build signup flows that capture real email addresses without rejecting real customers.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/why-your-email-validation-rejects-real-customers" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>emailvalidation</category>
      <category>formvalidation</category>
      <category>userexperience</category>
      <category>dataquality</category>
    </item>
    <item>
      <title>API Rate Limiting Done Right: A Complete Guide</title>
      <dc:creator>APIVerve</dc:creator>
      <pubDate>Wed, 04 Mar 2026 21:35:55 +0000</pubDate>
      <link>https://forem.com/apiverve/api-rate-limiting-done-right-a-complete-guide-4566</link>
      <guid>https://forem.com/apiverve/api-rate-limiting-done-right-a-complete-guide-4566</guid>
      <description>&lt;p&gt;Your rate limiter is probably making developers hate your API. Not because rate limiting is wrong—every API needs it—but because most implementations are hostile by default.&lt;/p&gt;

&lt;p&gt;A misbehaving client can overwhelm your servers. A bug in consumer code can accidentally DDoS you. Bad actors will abuse anything unprotected. So you add rate limiting. And then developers integrating your API hit walls they didn't expect, get blocked for behavior they thought was reasonable, and have no idea when they'll be unblocked.&lt;/p&gt;

&lt;p&gt;The fix isn't removing limits. It's making them transparent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five Problems You're Solving at Once
&lt;/h2&gt;

&lt;p&gt;Rate limiting serves multiple purposes, and understanding all of them prevents you from optimizing for just one:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service protection&lt;/strong&gt; - APIs have finite resources. Without limits, a single client making millions of requests could consume capacity needed by all other clients. Rate limiting ensures fair resource distribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost management&lt;/strong&gt; - API operations have costs: compute, database queries, third-party service calls, bandwidth. Unlimited usage from any single client creates unpredictable costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abuse prevention&lt;/strong&gt; - Bad actors use APIs for credential stuffing, scraping, spam, and other attacks. Rate limiting restricts the damage they can do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quality of service&lt;/strong&gt; - Stable response times require controlled load. Rate limiting prevents traffic spikes that degrade performance for everyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business model support&lt;/strong&gt; - Many APIs differentiate pricing tiers by rate limits. Higher limits at higher prices creates a revenue model.&lt;/p&gt;

&lt;p&gt;These are all valid reasons to implement rate limiting. The challenge is implementing it in ways that achieve these goals without unnecessarily frustrating legitimate users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Rate Limiting Goes Wrong
&lt;/h2&gt;

&lt;p&gt;Common rate limiting problems frustrate developers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bursts trigger limits unexpectedly.&lt;/strong&gt; A developer makes 10 requests quickly—well under their per-minute limit—but gets blocked. The rate limiter treats short bursts as attacks, even though bursts are normal behavior (page loads, batch operations, testing).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limits aren't communicated clearly.&lt;/strong&gt; Documentation says "1000 requests per hour" but doesn't explain how that's measured, when counters reset, or how different endpoints might have different limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error messages provide no guidance.&lt;/strong&gt; Getting "Rate limit exceeded" with no additional context leaves developers guessing. How long should they wait? Which requests counted? How close were they?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recovery is unclear.&lt;/strong&gt; After hitting a limit, when does normal service resume? Does waiting one second help? One minute? One hour?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legitimate use patterns get punished.&lt;/strong&gt; A developer following the rules—staying under stated limits—still gets blocked because their request pattern doesn't match what the rate limiter expects.&lt;/p&gt;

&lt;p&gt;Sound familiar? These all stem from the same root cause: rate limiting designed entirely from the infrastructure perspective, without ever asking how developers actually use APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bursts vs. Sustained Rate
&lt;/h2&gt;

&lt;p&gt;The most important distinction in rate limiting is between burst rate and sustained rate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sustained rate&lt;/strong&gt; measures requests over longer periods—per minute, per hour, per day. This is what most documentation describes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Burst rate&lt;/strong&gt; measures requests in short windows—per second, per few seconds. This catches sudden traffic spikes.&lt;/p&gt;

&lt;p&gt;Problems arise when rate limiters handle these poorly:&lt;/p&gt;

&lt;p&gt;A simple counter that resets every minute allows exactly 100 requests per minute. But it doesn't distinguish between 100 requests spread evenly over 60 seconds and 100 requests in the first second. The second pattern might still overwhelm backend systems.&lt;/p&gt;

&lt;p&gt;Conversely, strict per-second limiting prevents any bursting. A developer can't make 5 rapid requests even if they're making only 20 requests per minute total.&lt;/p&gt;

&lt;p&gt;Good rate limiting uses algorithms that handle both concerns:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token bucket algorithms&lt;/strong&gt; allow bursts up to a limit while enforcing sustained rates. Developers can make 10 quick requests (spending accumulated tokens) but can't sustain high rates indefinitely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sliding window algorithms&lt;/strong&gt; smooth out the boundary problems of fixed windows. Requests are counted across rolling time periods rather than resetting at arbitrary boundaries.&lt;/p&gt;

&lt;p&gt;These approaches let developers work the way they naturally would—bursts and all—while still protecting your infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communicating Limits Clearly
&lt;/h2&gt;

&lt;p&gt;Rate limit communication should happen everywhere, not just when limits are exceeded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation should be specific.&lt;/strong&gt; State the exact limits for different tiers, different endpoints, and different operations — the way we lay it out in our own &lt;a href="https://docs.apiverve.com/rate-limits?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=why-your-api-rate-limiter-makes-users-angry" rel="noopener noreferrer"&gt;rate limits documentation&lt;/a&gt;. Explain how limits are measured (sliding window? fixed window?), when they reset, and how different API calls count.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response headers should show current state.&lt;/strong&gt; Every API response should include rate limit information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current limit&lt;/li&gt;
&lt;li&gt;Remaining requests in current window&lt;/li&gt;
&lt;li&gt;When the window resets&lt;/li&gt;
&lt;li&gt;(Optionally) current usage count&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These headers let developers monitor their consumption in real-time. They can implement their own throttling before hitting limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error responses should be actionable.&lt;/strong&gt; When rate limits are exceeded, the error should specify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which limit was exceeded&lt;/li&gt;
&lt;li&gt;The limit value&lt;/li&gt;
&lt;li&gt;How long until the limit resets&lt;/li&gt;
&lt;li&gt;How many requests were over the limit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dashboards should provide visibility.&lt;/strong&gt; If your API has a developer portal, show rate limit usage graphically. Historical usage patterns help developers understand their consumption and plan capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The HTTP 429 Response
&lt;/h2&gt;

&lt;p&gt;HTTP defines status code 429 (Too Many Requests) specifically for rate limiting. Using it correctly helps clients handle rate limits automatically.&lt;/p&gt;

&lt;p&gt;Key elements of a proper 429 response:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Retry-After header&lt;/strong&gt; specifies how long clients should wait before retrying. Many HTTP libraries automatically honor this header, backing off appropriately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A clear error body&lt;/strong&gt; explains what happened in human-readable terms while also providing machine-readable details (error codes, limit values, reset times).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good rate limit handling in your client code&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callAPIWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;retryAfter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Retry-After&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;waitMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retryAfter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitMs&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;callAPIWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Retry after waiting&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern respects the &lt;code&gt;Retry-After&lt;/code&gt; header that well-designed APIs include with 429 responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistent format&lt;/strong&gt; matches your other error responses. Rate limit errors shouldn't look completely different from validation errors or server errors.&lt;/p&gt;

&lt;p&gt;Clients that receive well-formed 429 responses with Retry-After headers can implement automatic backoff. Clients that receive cryptic errors have to guess, often getting it wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Differentiated Limits
&lt;/h2&gt;

&lt;p&gt;Not all API operations cost the same. A simple read from cache consumes fewer resources than a complex query across multiple databases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tiered limits by operation type&lt;/strong&gt; make sense:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read operations might have higher limits&lt;/li&gt;
&lt;li&gt;Write operations might have lower limits&lt;/li&gt;
&lt;li&gt;Complex search or aggregation operations might have the lowest limits&lt;/li&gt;
&lt;li&gt;Batch operations might count differently than single operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Per-endpoint limits&lt;/strong&gt; allow fine-grained control. A search endpoint that's expensive to serve can have stricter limits than a simple lookup endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-method limits&lt;/strong&gt; might restrict POST/PUT/DELETE operations more than GET operations.&lt;/p&gt;

&lt;p&gt;Document these differences clearly. Developers should know that the search endpoint has a 10 requests/second limit even if the general limit is 100 requests/second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client Identification
&lt;/h2&gt;

&lt;p&gt;Rate limiting requires identifying clients. How you identify them affects fairness and accuracy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API key identification&lt;/strong&gt; is most common. Each API key has its own limits. This is fair—each customer gets their allocation—but requires authentication for all requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IP address identification&lt;/strong&gt; works without authentication but has problems. Multiple users behind a single IP (corporate networks, universities, NAT) share limits unfairly. Mobile users might share IPs with thousands of others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User account identification&lt;/strong&gt; works when your API has user login. Each user gets their own limits regardless of IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combination approaches&lt;/strong&gt; use API key when available, falling back to IP for unauthenticated requests (with stricter limits for unauthenticated access).&lt;/p&gt;

&lt;p&gt;The identification method should match your authentication model and avoid punishing legitimate users for network topology they don't control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gradual Degradation
&lt;/h2&gt;

&lt;p&gt;Hard cutoffs are jarring. One request succeeds; the next fails completely. Consider intermediate states:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning headers&lt;/strong&gt; can signal approaching limits before they're exceeded. A header indicating "80% of limit consumed" gives developers time to adjust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deprioritization&lt;/strong&gt; can slow responses rather than block them entirely. When a client exceeds soft limits, add slight delays to their requests rather than rejecting them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Graceful reduction&lt;/strong&gt; can limit functionality progressively. Allow read operations when write limits are exceeded. Allow cached responses when fresh data limits are exceeded.&lt;/p&gt;

&lt;p&gt;These approaches require more sophisticated implementation but create better developer experience. The cliff edge of rate limiting becomes a slope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helping Developers Stay Within Limits
&lt;/h2&gt;

&lt;p&gt;The best rate limiting helps developers succeed rather than just punishing failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client libraries should handle limits automatically.&lt;/strong&gt; If you provide SDKs, they should read rate limit headers and throttle requests appropriately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation should include best practices.&lt;/strong&gt; Explain how to make efficient API calls, when to use batch endpoints, how to cache responses, and how to implement backoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sandbox environments should have generous limits.&lt;/strong&gt; Developers building and testing integrations need room to experiment without production limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alerting should be available.&lt;/strong&gt; Let developers set up notifications when they approach limits, so they can investigate before getting blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrade paths should be clear.&lt;/strong&gt; When developers need higher limits, make it obvious how to get them—higher tiers, enterprise plans, special arrangements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Abuse Without Punishing Legitimate Users
&lt;/h2&gt;

&lt;p&gt;Rate limiting for abuse prevention differs from rate limiting for fair usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abuse patterns&lt;/strong&gt; include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Credential stuffing (many login attempts)&lt;/li&gt;
&lt;li&gt;Scraping (systematic data extraction)&lt;/li&gt;
&lt;li&gt;Spam (bulk creation of content)&lt;/li&gt;
&lt;li&gt;Attack probing (testing for vulnerabilities)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Legitimate high-volume patterns&lt;/strong&gt; include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batch processing&lt;/li&gt;
&lt;li&gt;Data migration&lt;/li&gt;
&lt;li&gt;Periodic synchronization&lt;/li&gt;
&lt;li&gt;High-traffic production applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge is distinguishing between them. Both might make many requests. The difference often lies in patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are requests distributed over time or concentrated?&lt;/li&gt;
&lt;li&gt;Do they follow normal usage patterns or probe unusual endpoints?&lt;/li&gt;
&lt;li&gt;Is the API key associated with a legitimate account with payment history?&lt;/li&gt;
&lt;li&gt;Does the traffic pattern match the stated use case?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sophisticated abuse detection goes beyond simple counting — tools like a &lt;a href="https://apiverve.com/marketplace/botdetector?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=why-your-api-rate-limiter-makes-users-angry" rel="noopener noreferrer"&gt;bot detector&lt;/a&gt; can help distinguish automated abuse from legitimate traffic. But whatever system you implement, minimize false positives that punish legitimate developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transparency About Limits
&lt;/h2&gt;

&lt;p&gt;Developers respect limits they understand. They resent limits that feel arbitrary or hidden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Publish your rate limits openly.&lt;/strong&gt; Don't make developers discover them through trial and error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explain the reasoning.&lt;/strong&gt; "This endpoint has a 10/second limit because it performs expensive database operations" helps developers understand and accept the limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Announce changes in advance.&lt;/strong&gt; Reducing rate limits with no warning breaks production integrations. Few things destroy trust faster than a silent limit change at 2am on a Tuesday.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider feedback.&lt;/strong&gt; If many developers hit the same limits doing reasonable things, the limits might be too restrictive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate Limiting as Developer Experience
&lt;/h2&gt;

&lt;p&gt;Rate limiting is unavoidable, but how it's implemented communicates your attitude toward developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hostile rate limiting&lt;/strong&gt; says: "You're doing something wrong. Stop it."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Helpful rate limiting&lt;/strong&gt; says: "Here's how much capacity you have. Here's how much you've used. Here's when you'll have more."&lt;/p&gt;

&lt;p&gt;The difference is communication. Same limits, same enforcement, but one approach leaves developers informed and empowered while the other leaves them frustrated and guessing.&lt;/p&gt;

&lt;p&gt;Good rate limiting becomes invisible. Developers know their limits, can monitor their usage, receive clear feedback when approaching limits, and understand how to get more capacity if needed.&lt;/p&gt;

&lt;p&gt;That's the goal: rate limiting that protects your service without developers ever feeling like they're fighting it.&lt;/p&gt;




&lt;p&gt;Build robust API integrations with APIVerve. Check our &lt;a href="https://docs.apiverve.com/rate-limits?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=why-your-api-rate-limiter-makes-users-angry" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for specific rate limit information and best practices for working within them efficiently.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://blog.apiverve.com/post/why-your-api-rate-limiter-makes-users-angry" rel="noopener noreferrer"&gt;APIVerve Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ratelimiting</category>
      <category>apidesign</category>
      <category>devrel</category>
      <category>apisecurity</category>
    </item>
  </channel>
</rss>
