Migrate from Twilio Verify
Two calls become two calls. The main differences: no Verify Service to create, no phone number to buy, and you're billed only when a code actually verifies.
Send a code
js
// Twilio
await twilio.verify.v2.services(SERVICE_SID)
.verifications.create({ to: phone, channel: 'sms' });
// SimpleSMS
await fetch('https://api.joinsimplesms.com/v1/verify', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SIMPLESMS_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ to: phone }),
});Check a code
js
// Twilio
const check = await twilio.verify.v2.services(SERVICE_SID)
.verificationChecks.create({ to: phone, code });
if (check.status === 'approved') { /* ... */ }
// SimpleSMS
const res = await fetch('https://api.joinsimplesms.com/v1/verify/check', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SIMPLESMS_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ to: phone, code }),
});
const { verified } = await res.json();
if (verified) { /* ... */ }In Python
python
# Twilio
client.verify.v2.services(SERVICE_SID).verifications.create(to=phone, channel="sms")
check = client.verify.v2.services(SERVICE_SID).verification_checks.create(to=phone, code=code)
ok = check.status == "approved"
# SimpleSMS: pip install joinsimplesms
from joinsimplesms import SimpleSMS
sms = SimpleSMS() # reads SIMPLESMS_API_KEY
sms.verify.send(to=phone)
ok = sms.verify.check(to=phone, code=code)["verified"]What maps to what
| Twilio | SimpleSMS |
|---|---|
to | to (or phone, both work) |
| Account SID + Auth Token | one API key |
| Verify Service SID | nothing; no service to create |
| A purchased phone number | nothing; we send from our pool |
check.status === 'approved' | verified === true |
status: 'pending' | verified: false, status: 'pending' |
| 404 on bad code | 200 with verified: false (a wrong code isn't an exception) |
| Fraud Guard | Shield, always on |
| ~$0.05 per attempt | $0.025, only when verified |
Things that get simpler
- No Verify Service. Delete the
VA...SID from your config. - No number. Delete the number provisioning step entirely.
- Billing follows success. Twilio charges per verification attempt; we
charge when
verifiedcomes back true, so pumping attacks and abandoned signups cost you nothing. - Attempt/expiry state is in the response.
attempts_remainingandexpires_inlet you render "2 tries left" and a countdown without tracking anything yourself.
Things to watch
- US and Canada only right now. If you verify internationally, keep Twilio for those routes or talk to us.
- The SDK is optional. The plain
fetchcalls above are the whole integration;npm install joinsimplesmsadds typed errors, retries, and the CLI. - Sandbox has no resend cooldown so you can iterate; live enforces 60 seconds per number.