Phone validation · Use cases · SMS pumping prevention

Use case

Stop SMS pumping before your app sends a single code.

Check the number before the OTP goes out. One call returns line type, disposable history and the country's SMS pumping risk, median under 500 ms. Trueguard does not sell SMS, so we earn nothing from the texts you send.

Start on the free tier

One check before every OTP.

Get your free API keyRead the API documentation
Free100 / month
Standard$49.99 / 20,000
Overage$0.0025
Median responseUnder 500 ms

How an SMS pumping attack works

Also called SMS toll fraud or artificial traffic inflation. Any OTP fraud prevention plan starts from these three steps.

01

Bots trigger the SMS

Scripts hit OTP and sign-up forms that send a text, and create large volumes of outbound messages.
02

Messages go to high-fee numbers

The numbers are premium-rate or international and controlled by the attacker or a partner.
03

A carrier shares the fee

A complicit telecom provider passes on part of the inflated termination fee. You pay the whole bill.

The three steps follow the NCSC Ireland advisory. The UK NCSC adds that the rogue party in the mobile ecosystem may never deliver the message to a handset (NCSC UK). MITRE files the technique as T1496.003, Resource Hijacking: SMS Pumping.

What it costs you

Twilio's outbound price per SMS for seven destinations, read on 3 November 2026. 10,000 sends a day is the volume Abstract's guide uses for its own model.

DestinationsmsDestinationRiskPer SMS1,000 a day10,000 a day10,000 a day for 30 days
United Stateslow$0.0083$8.30$83$2,490
United Kingdomlow$0.0560$56$560$16,800
Francelow$0.0798$79.80$798$23,940
Moroccoelevated$0.2244$224.40$2,244$67,320
Nigeriahigh$0.3868$386.80$3,868$116,040
Madagascarhigh$0.5899$589.90$5,899$176,970
Russiahigh$0.7522$752.20$7,522$225,660

Prices come from the Twilio public price CSV. The Nigeria pricing page says it is current as of October 2026. Twilio says prices can change without notice and carrier fees may apply. The totals are our arithmetic, not an incident.

As a cross-check, Healthchecks.io reported about 8,000 texts to Morocco and about $1,800 added to its Twilio bill. 8,000 times $0.2244 is $1,795.20.

France is rated low by country, yet it was the largest country by blocked requests in Prelude's 2025 sample, with 3,198,238 blocked at a 10.87% block rate. In October 2025 Prelude blocked 1.86 million requests in France at a 41.31% block rate, aimed at MVNO prefixes. A country rating cannot see that. The completion count per number block, further down, can. Prelude sells SMS verification and "blocked" is its own classification.

Healthchecks.io, May 2026

About 8,000 texts to random Moroccan numbers in about 12 hours, about $1,800 on the Twilio bill, refunded in full after the fixes. Healthchecks.io post

Blackpanda case study, March 2026

Almost 1 million fraudulent SMS in five days against a Hong Kong facilities app, with six-figure billing exposure. Per-IP limits failed because traffic came from hundreds of source addresses, each sending one or two requests. Blackpanda case study

Warning signs in your own data

Four patterns that show up before the invoice does.

Volume rises without new users

SMS sends spike while sign-ups stay flat.

Sends to countries you do not serve

Okta lists high message counts to countries outside your normal operating regions as a detection signal.

Random numbers share a prefix

Clusters of random-looking numbers that start with the same digits point to one block.

OTP completion drops

Many codes sent and few entered. Firebase calls a rate below 50% in a region a common indicator.

Sources: NCSC Ireland, Okta Threat Intelligence and the Firebase Auth FAQ.

Why spend caps and provider tools act late

Each of these limits the damage after it has started.

Auto-recharge keeps paying

Healthchecks had Twilio set to add $250 from its card whenever funds ran low. It ran until the card's daily spending limit stopped it.

AWS SNS stops within minutes

AWS says SNS stops sending within minutes of exceeding the spending quota, and you might incur costs above it in that period. AWS docs

Firebase caps skip phone auth

Firebase spend caps cover Firebase AI Logic, App Hosting, Cloud Functions and Extensions. Phone authentication is not on the list. Firebase docs

Provider tools decide at send time

Twilio's own docs say "No provider-side solution can guarantee 100% protection against sophisticated attackers." Twilio docs

Read on 3 November 2026. A check before the send does not replace any of these. It gives you a decision earlier than they can.

Trueguard does not sell SMS

Twilio and Prelude publish guides on SMS pumping, and both bill you per SMS. Trueguard bills per check and works with any SMS provider, so sending more texts does not change what we earn. Keep your provider's Fraud Guard or Protect turned on as well.

The layered defence

Six layers, each with an owner. No single one stops every attack, which is why the order and the owner matter.

01

Country allowlist

Your SMS provider runs this, through geo permissions or Firebase's SMS region policy. Allow only the countries you serve.
02

Rate limits

Your server runs this. Limit by number, prefix, IP and device, and use atomic counters. An atomic counter stops two simultaneous requests from both passing under the limit.
03

The Trueguard check

Runs before the send. It returns line type, disposable history and the destination country's SMS pumping risk.
04

A challenge

Your app runs this when your rule says challenge. A CAPTCHA is the mitigation MITRE lists as M1013.
05

OTP completion per number block

Your system runs this. It catches attacks that pass every check above, and the next section shows how.
06

Email on the same signup

Check the address with Trueguard email validation. The Okta and Healthchecks attacks used disposable email addresses, so use both endpoints together.

Layer 6 uses the Trueguard email validation API on the same signup. The phone and email endpoints are separate calls, so you make both.

Where the check goes

Form, rate limit, country allowlist, then the check, then your rule, then your SMS provider.

form → rate limit → country allowlist → POST /v2/phone/validation → your rule → your SMS provider

const res = await fetch("https://api.trueguard.io/v2/phone/validation", { method: "POST", headers: { "X-API-KEY": process.env.TRUEGUARD_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ phone: "+14159929960" }) }); console.log(await res.json());
const ALLOWED = new Set(["US", "GB", "DE"]); // countries you serve const decide = (r) => { if (!ALLOWED.has(r.location.countryCode)) { return "block"; } if (r.risk.status === "invalid" || r.risk.isDisposable) { return "block"; } if (r.line.type === "premiumRate" || r.line.smsCapable === false) { return "block"; } // high, elevated, or null (country not rated) if (r.risk.smsDestinationRisk !== "low") { return "challenge"; } if (r.line.voipType === "nonFixed") { return "challenge"; } if (r.risk.status === "unknown") { return "challenge"; } return "send"; };

risk.status covers validity, disposable and premium rate only. It does not change for destination risk, range risk or VoIP, so your rule reads smsDestinationRisk and voipType itself. Morocco is rated elevated and Pakistan is not rated, so elevated and null both get a challenge here. If you serve an unrated country, skip the challenge for it.

Count OTP completion per number block

Trueguard never sees whether a code was entered. This runs on your side.

const MIN_SENDS = 20; // your choice const blockOf = (e164) => e164.slice(0, 8); // "+" and the first 7 digits const hourKey = () => Math.floor(Date.now() / 3_600_000); // Atomic counter that expires on its own const bump = async (key, seconds) => { const n = await redis.incr(key); if (n === 1) { await redis.expire(key, seconds); } return n; }; // Before sending const canSend = async (e164) => { const block = blockOf(e164); if (await redis.exists(`otp:paused:${block}`)) { return false; } const hour = hourKey(); const sent = Number(await redis.get(`otp:sent:${block}:${hour}`)) || 0; const ok = Number(await redis.get(`otp:ok:${block}:${hour}`)) || 0; if (sent >= MIN_SENDS && ok / sent < 0.5) { return false; } // Volume rule: more than 50 sends in 10 minutes pauses the block for 1 hour if ((await bump(`otp:burst:${block}`, 600)) > 50) { await redis.set(`otp:paused:${block}`, "1", "EX", 3600); return false; } await bump(`otp:sent:${block}:${hour}`, 7200); return true; }; // When the user enters the right code const onVerified = (e164) => bump(`otp:ok:${blockOf(e164)}:${hourKey()}`, 7200);

A block is the first 7 digits of the E.164 number. Prelude says 6 to 8 digits and Abstract says 7 to 8. The 0.5 is Firebase's per-region figure applied to a block, and MIN_SENDS is your choice. The volume rule is Prelude's example, more than 50 sends in 10 minutes pauses the block for 1 hour. Every counter is an atomic INCR, so two requests cannot read the same value.

Vendor guidance, not standards

We found no neutral source that publishes a number for this. These are the figures vendors publish, read on 3 November 2026, with whether each one sells SMS.

SourceSells SMSWhat it says
Firebase Auth FAQYes, GoogleA healthy verified-to-sent rate is commonly 70 to 85%. Below 50% in a region is a common indicator of SMS traffic pumping.
Prelude 2025 reportYesA prefix is the first 6 to 8 digits. Example rule: more than 50 requests to a prefix in 10 minutes blocks it for 1 hour.
Abstract guideNo, sells phone validationAlert at 10 or more sends per prefix of 7 to 8 digits in an hour. Offices and campuses share prefixes.
Vonage Fraud DefenderYesVerify conversion alerts are raised at a conversion rate of 35% or below.
Twilio Verify docsYesAlert when the conversion rate drops, especially in an unexpected country. No number is given.

Sources: Firebase, Prelude, Abstract, Vonage and Twilio. Abstract calls its figures a calibration starting point against your own baseline.

What the check costs next to the SMS

Trueguard is $0.0025 a check on the paid plan. Here it is next to the SMS price of each destination.

DestinationSMSCheck as a share of the SMSChecks paid for by one blocked SMS
United States$0.008330%3
United Kingdom$0.05604.5%22
France$0.07983.1%31
Morocco$0.22441.1%89
Nigeria$0.38680.65%154
Madagascar$0.58990.42%235
Russia$0.75220.33%300

Twilio Line Type Intelligence is $0.008 a request, about 96% of a US SMS. The Twilio price comes from our Twilio Lookup comparison, read on 2 November 2026.

The check is billed during an attack too. At 10,000 sends a day for 30 days that is 300,000 checks, which is $49.99 plus 280,000 times $0.0025 ($700), so $749.99. The same volume of Morocco SMS at Twilio's price is $67,320. That is why rate limits and the country allowlist run before the check.

What this check does not do

  • smsDestinationRisk is country level. Only 24 countries are rated (11 high, 4 elevated, 9 low) and every other country returns null.
  • rangeRisk measures how many numbers in the same block appeared on receive-SMS sites in the last 90 days. It does not tell you which blocks attackers use for pumping.
  • There is no HLR lookup or network line test, so the check cannot tell you whether a line is in service.
  • There is no per-number pumping score. Twilio sells one at $0.025 per lookup outside North America, as listed on our Twilio Lookup comparison.
  • A valid number can still be pumped. IPQS makes the same point, that even a valid phone number can be used in an SMS pumping attack. That is why layers 2 and 5 exist.

Run the rule on one number.

Try +14086343220, a number listed on a receive-SMS site.

Or try

Frequently asked questions

Every field is in the phone validation API documentation.

Why did I get a huge SMS bill overnight, and how do I stop SMS pumping?

Bots requested codes to high-fee numbers, and a carrier or aggregator shared the termination fee. Healthchecks.io sent about 8,000 texts to random Moroccan numbers in about 12 hours and added about $1,800 to its Twilio bill. The fix order is a country allowlist, rate limits per number, prefix and device, a check of the number before you send, and a watch on how many codes get entered.

Bots hit my OTP endpoint and IP rate limits don't help, they rotate IPs. What now?

Okta reports that attackers use proxy services to spread source IPs, and Blackpanda describes an attack that defeated per-IP limits with hundreds of addresses sending one or two requests each. Limit by number prefix and by device, and use atomic counters. Then check the number itself with Trueguard. The destination does not rotate, so line type, disposable history and country risk still apply.

Should I only allow SMS to certain countries? Which destinations are risky?

Yes. Start with an allowlist of the countries you serve, in your SMS provider or your own code. In Prelude's 2025 sample the highest block rates were Madagascar at 95.20%, Tajikistan at 89.44% and Angola at 79.65%. Prelude sells SMS verification and the data is its own classification. Trueguard's smsDestinationRisk rates 24 countries and returns null for the rest.

Why didn't my spend cap, budget alert or disabled auto-recharge stop the charges?

Healthchecks had auto-recharge set to add $250 whenever funds ran low. It kept topping up until the card's daily spending limit stopped it. AWS says SNS stops sending within minutes of exceeding the spending quota, and that you might incur costs above it in that window. Firebase lists spend caps for only four services, and phone authentication is not among them, so a budget there alerts and does not pause SMS. Read on 3 November 2026.

Will Twilio, AWS or Firebase refund SMS pumping charges, and how do I ask?

Healthchecks reports that Twilio agreed to refund the full amount after it set free-user SMS to 0, tightened geo permissions and enabled SMS pumping protection. Twilio's Verify Fraud Guard Promise (terms last updated 10 July 2024) credits Verify and SMS fees only when Fraud Guard is in Max Protection Mode, you file a support ticket within 30 days of the end of the calendar month, and the affected numbers did not use Geo Permissions, Safe List or the RiskCheck parameter. We found no refund policy listed in the AWS or Firebase docs we read on 3 November 2026. Open a support case with dates and volumes.

A lookup roughly doubles my per-SMS cost. Is it worth it? Is there anything cheaper?

Twilio Line Type Intelligence is $0.008 a request, about 96% of a $0.0083 US SMS. A Trueguard check is $0.0025 on the paid plan, which is 30% of a US SMS and 1.1% of a Morocco SMS at Twilio's price. Two steps cost nothing and run before any paid check: an allowlist of the countries you serve, and offline number parsing with libphonenumber.

Real users get blocked by rate limits or Fraud Guard. How do I avoid false positives?

Twilio lists false positives under 1% for Standard and under 2% for Max in its Fraud Guard docs. In your own rule, block only invalid, disposable, premium rate and not-SMS-capable numbers. Challenge instead of blocking when the country is elevated or unrated and when the number is non-fixed VoIP. Prefix limits can hit shared ranges, because Abstract notes that offices and campuses share prefixes legitimately.

How do I add a check before Supabase, Firebase, Auth0 or better-auth sends the SMS?

Call the validation endpoint from your server, before the line that triggers the send, and send only when your rule returns send. If your client calls the provider directly, an attacker can skip your check, so also set the provider's own region policy. The provider's setting is the backstop for requests that never touch your server.

Check numbers before you pay to text them.

100 free checks a month, no card. Works with any SMS provider.

Get your free API key

No credit card required.

trueguard-logo© 2026 Trueguardinfo@trueguard.io