Builder guide
Free OTP for your SaaS:
login codes by WhatsApp, SMS or email, no per-code fee
Twilio Verify bills $0.05 for every successful code, plus the SMS. For a side project or an early SaaS, you can send the same codes from your own WhatsApp number, Android phone or mailbox through one free API call. Here is the code, the prompt for your AI coding tool, and the point where a paid verification service becomes worth it.
In a hurry? Summarize this article with an AI
The short answer
Can you send OTP codes for free?
Yes. Your backend generates the code, stores a hash, and sends it with one POST /message call to Unlimited Messaging, which delivers it from your own WhatsApp number, your Android phone's SIM or your mailbox. The free plan has no per-code fee and no message cap. The catch: a short footer on each message, and a human sending pace that does not fit huge bursts.
The usual route is a verification API. Twilio Verify charges $0.05 per successful verification plus the channel fee: $0.0083 per SMS in the US, so about $58 for 1,000 logins. Building it yourself on a raw SMS API is cheaper per message ($0.0083 plus a carrier fee in the US) but adds A2P 10DLC registration before the first text.
For an indie SaaS, the code logic is the easy part: about 30 lines for send and verify, shown below. What costs money is the delivery. Sending from accounts you already own removes that line: WhatsApp from your number (no Meta authentication template), SMS from your phone's plan, email from Gmail or your domain's mailbox.
This page is the builder how-to. If you want the case for WhatsApp over SMS (delivery rates, SIM swap, SMS pumping), read the WhatsApp OTP article linked below. Prices were read on the vendors' pages on November 9, 2026.
Related: why WhatsApp OTP beats SMS codes, a free SMS API from your own phone and sending from Gmail with an app password.

The bill
How much does an OTP cost with the usual APIs?
Per code and for 1,000 successful logins a month, US numbers. Verify bills only successful checks; raw SMS bills every send.
| Cost per code | 1,000 codes a month | What you set up | |
|---|---|---|---|
| Twilio Verify, SMS | $0.05 + $0.0083 per SMS | About $58 | Twilio account, Verify service; fraud protection included |
| Twilio Verify, WhatsApp | $0.05 + Meta's authentication fee | From about $53 | WhatsApp sender approved by Meta |
| Twilio SMS API, your own code | $0.0083 + carrier fee ($0.0035 to $0.005) | About $12 to $13 | A2P 10DLC brand and campaign registration |
| Resend, email codes | $0 on the free tier | $0 while under 100 emails a day | DNS records on your domain |
| Unlimited Messaging Free | €0 on WhatsApp, SMS and email | €0 | Link your number, phone or mailbox; codes carry a short footer |
| Unlimited Messaging Medium | Flat €19 a month, no per-code fee | €19 | Same setup, footer removed |
Twilio Verify, SMS and WhatsApp prices from Twilio's pricing pages, Resend from resend.com/pricing, read on November 9, 2026. Meta's authentication fee depends on the recipient's country (Twilio lists utility and authentication from $0.0034). SMS sent through Unlimited Messaging count against your own carrier plan.
The recipe
How do you build OTP verification in four steps?
The same logic works for SMS, WhatsApp and email; only the channel in the request changes.
- 01
Generate and store
Create a 6-digit code with a cryptographic random generator, store only its hash with an expiry of about 5 minutes and an attempt counter.
- 02
Send in one call
POST /message with to: { phone } or { email } and the channel the user picked: WHATSAPP, SMS or EMAIL.
- 03
Verify
Hash what the user typed, compare in constant time, reject expired codes and lock after 5 wrong attempts. Delete the code once it is used.
- 04
Rate limit
One new code per minute per user and a few per hour per IP. It blocks SMS pumping and keeps your number from looking like a spammer.
- digits, enough with a 5-attempt lock
- 6digits, enough with a 5-attempt lock
- typical code lifetime
- 5 mintypical code lifetime
- API call to send on any channel
- 1API call to send on any channel
The code
What does the send and verify code look like?
Two route handlers in TypeScript (Next.js App Router style; the same logic fits Express, Hono or a Supabase edge function). Replace db with your own storage.
// app/api/otp/send/route.ts
import { createHash, randomInt } from 'node:crypto';
import { db } from '@/lib/db'; // your own storage
export async function POST(req: Request) {
const { phone } = await req.json();
const code = String(randomInt(0, 1_000_000)).padStart(6, '0');
await db.otp.save(phone, {
hash: createHash('sha256').update(code).digest('hex'),
expiresAt: Date.now() + 5 * 60_000,
attempts: 0,
});
await fetch('https://api.unlimitedmessaging.app/message', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.UM_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: { phone },
channels: [{ channel: 'WHATSAPP' }],
text: `${code} is your Acme verification code. It expires in 5 minutes.`,
}),
});
return Response.json({ sent: true });
}// app/api/otp/verify/route.ts
import { createHash, timingSafeEqual } from 'node:crypto';
import { db } from '@/lib/db';
export async function POST(req: Request) {
const { phone, code } = await req.json();
const row = await db.otp.get(phone);
if (!row || row.expiresAt < Date.now() || row.attempts >= 5) {
return Response.json({ ok: false }, { status: 400 });
}
const hash = createHash('sha256').update(String(code)).digest('hex');
if (!timingSafeEqual(Buffer.from(hash), Buffer.from(row.hash))) {
await db.otp.incrementAttempts(phone);
return Response.json({ ok: false }, { status: 401 });
}
await db.otp.delete(phone);
return Response.json({ ok: true });
}Add phone and email verification codes (OTP) to this app with Unlimited Messaging. Read UM_API_KEY from the server environment only. Create two server routes. 1) POST /api/otp/send: takes { phone } or { email } and channel ('WHATSAPP', 'SMS' or 'EMAIL'); generate a 6-digit code with crypto.randomInt, store a SHA-256 hash with a 5-minute expiry and an attempts counter, allow one new code per minute per user; send it with POST https://api.unlimitedmessaging.app/message, header Authorization: Bearer UM_API_KEY, body { to: { phone } or { email }, channels: [{ channel }], subject (email only), text: '<code> is your verification code. It expires in 5 minutes.' }. 2) POST /api/otp/verify: compare hashes with crypto.timingSafeEqual, reject expired codes, lock after 5 wrong attempts, delete the code on success. Add a simple UI: choose WhatsApp, SMS or email, enter the destination, then a 6-digit input. Reference: https://unlimitedmessaging.app/llms-full.txtThe channel
WhatsApp, SMS or email: which channel should carry the code?
Let the user choose when you can. Each channel has a different reach and a different limit.
| SMS | |||
|---|---|---|---|
| User needs | A WhatsApp account on that number | Any mobile number | An inbox they can open |
| Sent from | Your linked WhatsApp number | Your Android phone's SIM | Your Gmail or domain mailbox |
| Cost with Unlimited Messaging | No per-message fee | Your carrier plan | No per-message fee |
| Pace limit | Human pace, spaced sends | 30 SMS per 30 minutes (Android) | 500 a day on Gmail, 2,000 on Workspace |
| Best for | International users, phone login | Users without WhatsApp, local numbers | Signup verification, magic-code login |
Fair play
When is a free OTP setup not enough?
Free delivery from your own accounts suits a side project or an early SaaS. These are the cases where a paid verification API earns its fee.
Launch-day bursts
Android asks for confirmation beyond 30 SMS in 30 minutes, and WhatsApp messages go out at a human pace. Hundreds of codes a minute belong on Twilio Verify or a similar service.
A footer on the free plan
On the free plan, every code ends with "Sent with UnlimitedMessaging.app". The Medium plan at €19 a month removes it.
WhatsApp is unofficial
Your number is linked as a device, so there is no Meta authentication template and no copy-code button. Abuse can get the number banned: rate limit every send.
No built-in fraud shield
Verify services detect SMS pumping and bot signups for you. With your own accounts, rate limits and a CAPTCHA on the send route are your job.
High-security 2FA
NIST SP 800-63B classes OTP over the phone network as a restricted authenticator. For admin accounts, offer an authenticator app (TOTP) or passkeys, which need no message at all.
FAQ
Questions about free OTP APIs
Sources
Sources & references
To check the information in this article for yourself:
- Verify pricing ($0.05 per successful verification plus channel fees)Twilio
- SMS pricing in the United States ($0.0083 per segment, carrier fees)Twilio
- WhatsApp pricing ($0.005 per message plus Meta's template fee)Twilio
- A2P 10DLC brand registrationTwilio Docs
- Pricing on the WhatsApp Business Platform (authentication category, free tier for service messages only)Meta for Developers
- Resend pricing (free: 3,000 emails a month, 100 a day)Resend
- SmsUsageMonitor (30 SMS in 30 minutes threshold)Android Open Source Project
- NIST SP 800-63B, out-of-band authenticators over the PSTN (restricted)NIST

Written by
Ilian · Senior Software Engineer
A senior software engineer, he founds and runs several tech companies. He builds products around messaging APIs, automation and AI integration, and shares here what he learns while building.
Ship login codes today, pay nothing per code.
Link your WhatsApp number, your Android phone or your mailbox, create an API key and send your first verification code for free.
