OTP API Integration Guide: SMS Verification for Developers (2026)

SMS OTP API integration looks like a single API call, and that is why it is easy to get wrong.
A weak implementation leaks money through SMS pumping. A brittle one drops users at checkout. A rigid one fails the moment your delivery channel goes down.
This guide walks you through OTP integration end to end, written for developers who want to ship phone verification that holds up in production — with an Africa-first view on delivery, because a code that never reaches the phone fails the user even when every API call in your logs succeeded.
What is an OTP API and how does it work?
An OTP API is the service your application calls to generate, deliver, and verify a one-time code that proves a user controls a phone number or inbox. You hand it a destination and a request; it returns a result you can act on. An OTP SMS API is the same capability with SMS as the delivery channel — the most universal way to reach a phone.
That means you never store or generate codes yourself. The provider handles generation, hashing, expiry, and delivery. You handle the user experience and two API calls.
A quick vocabulary note for the search you probably ran to get here: an “SMS verification API” and an OTP API are not two products. An SMS verification API is the OTP API viewed from the phone-verification angle — generate a code, send it over SMS, validate the input. Same flow, same integration.
If you want the conceptual groundwork before the code, our guide on how OTPs work covers the fundamentals.
Why bother with a second factor at all? Because the password alone keeps failing.
According to Verizon’s 2026 Data Breach Investigations Report, human-targeted attacks — phishing and social engineering, increasingly aimed at mobile devices — remain a leading cause of breaches worldwide. A one-time code sent to a device the attacker does not hold closes the gap a leaked password opens.
How OTP verification works end-to-end
Every OTP system follows the same two-phase pattern: send, then verify.
Phase 1 — Send. Your application calls the provider with a phone number. The provider generates a random code, stores it as a hash server-side, sets an expiry, and delivers it over the channel your request names, such as SMS, voice or WhatsApp, where the provider supports that channel for the destination.
Phase 2 — Verify. The user enters the code. Your backend sends the code and the phone number to the provider’s verify step.
A match within the time window authenticates the user. A mismatch or an expired code fails closed.
The practical payoff of this split: your application never touches the raw code. The provider owns generation, delivery, storage, and expiry. You own the interface and the API calls — which is exactly why a managed OTP API saves the substantial effort a from-scratch build would cost in code generation, hashing, expiry logic, and delivery infrastructure.
What is the difference between an OTP API gateway and direct integration?

Direct integration calls a single provider’s REST endpoint straight from your backend. A gateway adds a routing layer between your app and one or more providers, switching by channel, region, or provider health.
Direct is faster to build but creates a single point of failure; a gateway adds reliability through redundancy and channel fallback. Three architecture patterns are worth knowing before you choose.
Direct API integration
The most streamlined pattern. Your application makes REST calls directly to one provider.
Your App -> OTP Provider API -> SMS / Voice Gateway -> UserBest for: early-stage products, low volume, a first verification flow.
Trade-off: zero abstraction overhead and fast to ship — but if the provider or a carrier route fails, your entire OTP flow stops. No fallback.
Gateway aggregator pattern
A routing layer sits between your application and one or more providers, directing each request by channel, region, or health.
Your App -> OTP Gateway Layer -> Provider A (SMS)
-> Provider B (Voice)
-> Provider C (WhatsApp)Best for: production apps with real reliability requirements, multi-channel delivery, regional optimisation.
Trade-off: more upfront engineering for the routing layer, repaid in channel flexibility and provider independence. A platform that handles several channels behind one API collapses much of that complexity into a single integration, though which channels it offers can vary by destination country, so check each market you serve (the provider section below shows how this works with Arkesel).
Failover chain architecture
A primary provider handles every request. On failure or timeout, your own routing logic sends it to a secondary, then a tertiary.
Your App -> Primary (SMS, short timeout)
-> Secondary (Voice, longer timeout)
-> Tertiary (WhatsApp)Best for: mission-critical flows — fintech, checkout, banking authentication.
Key mechanics: set per-channel delivery timeouts and trigger failover when no confirmation arrives; track provider error rates with a circuit breaker so a failing provider drops out of the chain; poll status endpoints so you detect an outage before your users do. When a failover fires repeatedly, treat it as a symptom — our guide on common OTP API integration errors shows you how to tell a client-side fault from a provider one.
Which OTP delivery channel should you use?
SMS is the natural first channel for OTP delivery: it reaches virtually every mobile device and arrives in seconds. Add voice as a resend option you trigger for failed SMS and for accessibility, where your provider supports it for the destination, reserve WhatsApp for smartphone users on data, and, where data is scarce, ask whether your provider offers USSD for the destination — it works on any handset and stores nothing on the device.
| Channel | Delivery speed | Device reach | Security profile | Best use |
|---|---|---|---|---|
| SMS | Seconds | Almost every mobile device | SIM-swap and interception risk | First choice, universal reach |
| Voice | Slower than SMS | Any phone with voice | Call interception, voicemail risk | Resend option, accessibility |
| Fast | App + internet required | End-to-end encrypted, device-bound | Smartphone users on data | |
| USSD | Near-instant | Any mobile device, no data | Session-based, nothing stored | Feature phones, low-data markets (provider-dependent) |
Channel choice shifts in African markets. Where feature phones and intermittent data are common, SMS and USSD matter more than app-based channels. USSD reaches any handset, needs no data, and leaves no code stored after the session, which suits mobile money and banking flows where people already use USSD.
Whether USSD plays any part in OTP depends on your provider and the destination country: with Arkesel, USSD is not a delivery channel, but a Ghanaian user who misses an SMS OTP can dial a USSD code to view it. For a deeper trade-off analysis, see our comparison of SMS, authenticator app, and email OTP.
How do you integrate an OTP SMS API into your application?
Integrate an SMS OTP API in five steps: secure your API key, set up your environment, generate and send the code, handle the response, then verify the user’s input. It is the same flow whether you came here looking for an API for OTP verification or an API for verifying users by SMS — one call to generate and send the code, one to verify it.
The walkthrough below uses Arkesel’s live OTP endpoints — a Generate call and a Verify call. Confirm the current contract in the Arkesel developer documentation before you ship, since request details can change.
Step 1: Get and secure your API key
Create an Arkesel account, open the API settings, and copy your key. Store it in an environment variable or a secret manager — never hard-code it, and never commit it to version control. A leaked OTP key is a direct line to your messaging budget.
One gotcha trips up almost every first integration: OTP works only with your Main SMS API Key — it will not work with any of your Multiple (sub) API keys. OTP usage also draws on your account’s Main Balance, so top it up before you go live and check the current Arkesel pricing for rates.
Step 2: Set up your development environment
Both OTP endpoints are plain REST on https://sms.arkesel.com, authenticated with an api-key request header and a JSON body — so you only need an HTTP client:
- Python: install
requests(orhttpx). - Node.js: use the built-in
fetch, oraxios. - PHP: enable the cURL extension.
Arkesel publishes copy-paste samples in Java, Python, PHP, and Node.js in its OpenAPI specification, which is also the source for the endpoints, parameters and status codes in this guide; these are code samples rather than maintained SDK client libraries, so you call the REST endpoint directly.
Step 3: Generate and send the OTP
Generate the code with a single POST to https://sms.arkesel.com/api/otp/generate. The request carries the destination number, the delivery channel, an expiry window, a code length, and a message template that holds the code. Arkesel generates the one-time password and delivers it over the channel you name in that request.
The medium field is required and has no default, so every call chooses SMS or voice. Voice delivery works for Ghanaian numbers only.
curl -X POST https://sms.arkesel.com/api/otp/generate \
-H "api-key: $ARKESEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"expiry": 5,
"length": 6,
"medium": "sms",
"message": "This is OTP from Arkesel, %otp_code%",
"number": "233544919953",
"sender_id": "Arkesel",
"type": "numeric"
}'The parameters map to real constraints you code against:
expiry— integer, 1 to 10 minutes.length— integer, 6 to 15 digits or characters.medium— required, with no default:smsorvoice, chosen on each request. Voice works for Ghanaian numbers only, so setsmsfor Nigerian, South African and Tanzanian numbers.message— your template; it must contain the%otp_code%slot where the code is injected (add the optional%expiry%slot to tell the user how long they have).number— the destination phone number in international format.sender_id— 1 to 11 characters.type—numericoralphanumeric.
A successful call returns code 1000:
{
"code": "1000",
"ussd_code": "<USSD retrieval code>",
"message": "Successful, OTP is being processed for delivery"
}Notice the ussd_code in the response. If a user with a Ghanaian number misses the SMS, they can dial that code to view the OTP with no data.
This is something the user does; Arkesel does not send the OTP over USSD. The value above is a placeholder: the current code and how it behaves are in the Arkesel developer documentation.
Here is the same call in Python:
import os, requests
def generate_otp(number):
res = requests.post(
"https://sms.arkesel.com/api/otp/generate",
headers={
"api-key": os.environ["ARKESEL_API_KEY"],
"Content-Type": "application/json",
},
json={
"expiry": 5,
"length": 6,
"medium": "sms",
"message": "This is OTP from Arkesel, %otp_code%",
"number": number,
"sender_id": "Arkesel",
"type": "numeric",
},
)
data = res.json()
if data.get("code") == "1000":
return {"success": True, "message": data.get("message")}
return {"success": False, "code": data.get("code"), "message": data.get("message")}Step 4: Handle the response and error codes
Do not assume success on a 200. Read the code in the response body, branch on it, and map each code to a clear user-facing message.
Authentication and transport problems surface as HTTP status codes — 401 for a missing or invalid key, 422 for a validation error, 500 for an internal error — while the application outcome lives in the code field. The full list of generate and verify codes is in the table below.
Delivery outcomes arrive after the initial response, so ask your provider whether it sends OTP delivery callbacks to a webhook, and how quickly they arrive. Where it does, a delivery callback lets your own code trigger a resend, watch delivery rates, and catch the anomalies that signal abuse. For Arkesel, check the Arkesel developer documentation before you build on one.
Step 5: Verify the user’s code
This is the call that verifies a user by SMS: prompt for the code they received, then POST it with their phone number to https://sms.arkesel.com/api/otp/verify. Validate server-side — never in the browser.
curl -X POST https://sms.arkesel.com/api/otp/verify \
-H "api-key: $ARKESEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "173882",
"number": "233544919953"
}'A successful verification returns code 1100:
{
"code": "1100",
"message": "Successful"
}In Python:
def verify_otp(number, code):
res = requests.post(
"https://sms.arkesel.com/api/otp/verify",
headers={
"api-key": os.environ["ARKESEL_API_KEY"],
"Content-Type": "application/json",
},
json={"code": code, "number": number},
)
data = res.json()
if data.get("code") == "1100":
return {"verified": True}
return {"verified": False, "code": data.get("code"), "reason": data.get("message")}An invalid code returns 1104; an expired one returns 1105. Surface those to the user as distinct, actionable messages — “that code is incorrect” and “that code has expired, request a new one” — rather than a single generic failure.
See how Arkesel’s SMS Platform tracks message delivery in real time — explore the Arkesel SMS Platform.
OTP API response and error codes
Every OTP response carries a code. Branch on it, log it, and map it to a user-facing message. Here is the full set — generate, verify, and the HTTP-level codes.
Generate OTP codes
| Code | Meaning | How to handle |
|---|---|---|
| 1000 | Successful — OTP is processing for delivery | Store the attempt, prompt the user for the code |
| 1001 | Validation error — a required field is missing | Fix the request body |
| 1002 | Message is missing the %otp_code% slot | Add %otp_code% to your template |
| 1003 | Sender ID blocked by administrator | Use an approved sender ID |
| 1004 | SMS gateway not active or credential not found | Confirm your account and Main API key |
| 1005 | Invalid phone number | Validate and format the number before sending |
| 1006 | OTP not allowed in your country | Restrict sends to the markets you serve (delivery is geo-gated) |
| 1007 / 1008 | Insufficient balance | Top up your Main Balance |
| 1009 | Voice message over 500 characters | Shorten the voice template |
| 1011 | Internal error | Retry with backoff; escalate if it persists |
Verify OTP codes
| Code | Meaning | How to handle |
|---|---|---|
| 1100 | Successful — the code is valid | Authenticate the user |
| 1101 | Validation error | Fix the request body |
| 1102 / 1103 | Invalid phone number | Verify the number matches the one you sent to |
| 1104 | Invalid code | Tell the user the code is incorrect; count the attempt |
| 1105 | Code has expired | Prompt the user to request a new code |
| 1106 | Internal error | Retry with backoff; escalate if it persists |
HTTP status codes
| Status | Meaning | What you do |
|---|---|---|
| 401 | Authentication failed — missing or invalid key | Check the api-key header |
| 422 | Validation error | Fix the payload |
| 500 | Internal error | Retry with backoff or fail over |
Codes 1006 (country not allowed) and 1005 (invalid number) are delivery blocks you can prevent before you send — validate numbers and restrict destinations up front, and those requests never fail in front of your user.
Advanced OTP generation and validation
Once the core flow works, the depth lives in how codes are generated and how strictly they are validated. This is where a verification system moves from functional to resilient.
What is the difference between TOTP and HOTP?
TOTP and HOTP are two standard algorithms for generating codes. According to OneLogin’s explainer on OTP, TOTP, and HOTP, both derive a code from a secret seed plus a moving factor; HOTP increments that factor each time a code is requested and validated (counter-based), while TOTP derives it from the current time (time-based), which is why time-based codes expire after a short window. For most app-delivered SMS verification you are not implementing the algorithm yourself — your provider does — but knowing which model is in play tells you how expiry and resynchronisation behave.
Code length, expiry, and attempt limits
Three settings carry most of the security weight — and with Arkesel you set the first two directly in the generate call:
- Length and character set. Six numeric digits balance entry friction against guessability for SMS; set
lengthhigher (up to 15) or switchtypetoalphanumericfor higher-risk actions. - Expiry window. Short-lived codes shrink the window an intercepted code is useful. Set
expirybetween 1 and 10 minutes — keep it tight, and tighter still for sensitive operations. Our guide on OTP expiration and rate-limiting best practices goes deep on choosing the window. - Attempt limits and single use. Cap verification attempts per code, lock out after repeated failures, and enforce one-time use so a code cannot be replayed across sessions.
Stronger validation patterns
Beyond the essentials, several industry techniques harden validation. These are options you can build into your own flow — not features any single provider switches on for you:
- Adaptive verification raises code strength or step-up requirements when risk signals appear (unusual velocity, a new device).
- Device and IP fingerprinting flags a code entered from an unfamiliar device or location for extra checks.
- Encrypted transmission over HTTPS/TLS keeps the code from being intercepted in transit.
- One-time URLs pair a code with a short-lived link tied to a single session.
- Anomaly detection, including machine-learning models, can flag suspicious request patterns at scale.
The constant tension is user experience against security: every extra factor adds friction, so reserve the heavier patterns for high-risk actions and keep the common path fast.
How do you protect OTP endpoints from abuse?
Protect OTP endpoints with rate limiting on both send and verify, abuse detection on the send-to-verify ratio, HTTPS-only transmission, and intelligent retries. OTP endpoints are attack magnets: left open, a single bad actor can drain your messaging budget overnight.
Rate limiting. Apply separate limits to send and verify — they have different abuse patterns. Cap sends per user and per IP over a short window; cap verify attempts per code with lockout after repeated failures.
Use distinct namespaces so an exhausted send limit does not block verification of codes already in flight.
SMS pumping protection. SMS pumping hits your messaging bill directly — bots trigger floods of sends to premium-rate ranges and profit from carrier revenue share while you pay for every message. The clearest signal is a send-to-verify ratio that collapses: sends spike, verifications flatline.
Add a CAPTCHA or challenge before the send endpoint, run a carrier lookup to filter non-mobile numbers, and restrict sends to the countries you actually serve. Our guide on SMS pumping fraud prevention breaks down detection and response in detail.
Retry intelligently. On a transient failure, retry with exponential backoff and an idempotency key so the user does not receive duplicate codes. Never retry permanent failures such as an invalid number or a blocked sender.
These measures are the front line of OTP security; for the broader checklist, see our guide on securing transactions with OTP APIs, which matters most in high-stakes flows like OTP for fintech and banking.
How do you test an OTP API before production?
Test an OTP API across four layers: mock the provider for unit tests, run the full send-deliver-verify cycle as integration tests against test numbers you control, load-test the endpoints to peak traffic, and run a security checklist. OTP touches authentication, third-party APIs, delivery, and money — test each layer before it reaches users.
Mock and sandbox. Arkesel’s SMS API provides a sandbox for testing SMS sends without affecting your balance.
In the current API specification that sandbox is a sandbox: true flag on the SMS send endpoint; the OTP generate endpoint has no sandbox field. Use the sandbox to test your SMS sending code, mock the provider at the interface level for unit tests so your business logic is isolated from provider behaviour, and run the OTP generate-and-verify cycle against a small set of real test numbers you control.
Integration tests should cover the real cases: generate returns code 1000; verify succeeds with code 1100; verify fails on a wrong code with 1104; verify fails after expiry with 1105; a used code cannot be reused; and rate limiting triggers at the threshold.
Load tests ramp from your baseline to several times projected peak and watch the degradation curve. The endpoint should fail gracefully — returning 429 or 503 — rather than timing out silently. Tools such as k6, Artillery, or Locust point cleanly at a staging environment with the provider mocked, so a load test never sends real codes.
Security checklist: confirm codes expire on schedule, codes are single-use, brute-force lockout fires after repeated failures, codes are stored hashed rather than in plaintext, transmission is HTTPS-only, and verification uses constant-time comparison so response timing leaks nothing.
How do you choose an OTP API provider?
Choose an OTP API provider on five dimensions: API and documentation quality, multi-channel support, a real sandbox, geographic delivery coverage, and compliance. For users in Africa, delivery coverage deserves the closest look: ask each provider how it routes codes to your target countries and which channels work in each.
- API and docs quality. RESTful endpoints, consistent error codes, webhook support, and working code samples in your language — not just a bare spec.
- Multi-channel support. SMS is the floor. A voice option, or your own resend path, helps when an SMS does not arrive, so ask which countries each provider’s voice channel covers. Every channel a provider offers behind one API is one fewer integration you maintain.
- A real sandbox. Test numbers and a sandbox mode so you are not spending real money during development.
- Geographic delivery coverage. For African users, ask how codes are routed to each of your target countries, whether through direct carrier connections or onward partners, and which channels work in each.
- Compliance. Data-handling practices, residency options, and audit logging for regulated industries.
In Ghana, Arkesel connects directly to MTN, Telecel, and AirtelTigo. OTPs go out from a single REST API.
Each request names its channel: SMS, or voice for Ghanaian numbers only. There is no automatic fallback: a failed SMS is not retried by voice, so any retry logic is yours to build.
For an objective side-by-side, see our OTP API provider comparison. Pricing changes too often to trust a figure in a blog post — check the current Arkesel pricing page.
Which OTP channels work in Ghana, Nigeria, South Africa and Tanzania?
With Arkesel, SMS OTP works in all four countries. Voice OTP, sent only when your request asks for it, works for Ghanaian numbers only. Ghanaian users who miss an SMS can dial a USSD code to view it; Nigerian, South African and Tanzanian numbers receive OTPs by SMS only.
| Country | SMS OTP | Voice OTP (on request) | USSD code to view a missed SMS OTP |
|---|---|---|---|
| Ghana | Yes | Yes | Yes |
| Nigeria | Yes | No | No |
| South Africa | Yes | No | No |
| Tanzania | Yes | No | No |
OTP integration production readiness checklist
Before you ship, confirm every item.
Security
- Codes expire on a short window; shorter for high-risk actions
- Codes stored as hashes, never plaintext
- Rate limiting on both send and verify endpoints
- HTTPS enforced on every OTP endpoint
- Brute-force lockout after repeated failed attempts
- CAPTCHA or challenge ahead of the send endpoint
Reliability
- Your own fallback logic in place (for example, a resend by voice where the destination supports it)
- Delivery-status monitoring, via webhooks where your provider sends OTP delivery callbacks
- Alerting when the delivery rate drops below your threshold
- Circuit breaker for provider failover
- Retry with exponential backoff and idempotency keys
Compliance
- Data-retention policy defined for OTP records
- User consent captured before sending
- Regional telecom and data regulations reviewed
- Audit logging for every OTP event
Operations
- Structured logging with request IDs for tracing
- A metrics dashboard: send volume, delivery rate, verification rate, latency
- Cost monitoring with budget alerts
- Abuse alerts on the send-to-verify ratio and geographic anomalies
Frequently asked questions
Is an SMS verification API the same as an OTP API?
Yes. They describe one capability from two angles.
An OTP API generates and validates one-time codes across channels; an “SMS verification API” names the case where the code is delivered over SMS to verify a phone number. The integration, code, and testing are identical.
How do you verify a user by SMS with the API?
Send the code the user entered, with their phone number, to Arkesel’s verify endpoint (/api/otp/verify) from your backend. A match returns code 1100 and you authenticate the user; an invalid code returns 1104 and an expired one returns 1105. Always validate server-side, never in the browser.
What expiry window and attempt limits should you set?
Keep the expiry window short so an intercepted code is useful for as little time as possible, and shorten it further for sensitive actions. Cap verification attempts per code, lock out after repeated failures, and enforce single use so a code cannot be replayed.
How long should an OTP stay valid?
Long enough for a user to receive and type the code, short enough that an intercepted code is quickly useless. Arkesel lets you set the expiry between 1 and 10 minutes per request; five minutes is a sensible starting point, and you should shorten it for high-value actions.
What if the OTP does not arrive?
First check the generate response code: 1005 means the number was invalid, 1006 means delivery is not allowed in that country, and 1007 or 1008 mean insufficient balance.
If the code is 1000 but the SMS never arrives, a user with a Ghanaian number can dial the ussd_code from the response to view it with no data. For Ghanaian numbers you can also send a new OTP by voice by setting medium to voice; Arkesel does not do this automatically. In Nigeria, South Africa and Tanzania, SMS is the only OTP channel, so any resend goes by SMS.
What does a failed OTP verification return?
The verify endpoint returns 1104 for an incorrect code and 1105 for a code that has expired. Treat them differently: prompt a retry on 1104 and count it toward your lockout limit, and offer a fresh code on 1105.
Can I send the OTP by voice instead of SMS?
Yes, for Ghanaian numbers only. Set medium to voice in the generate request and Arkesel reads the code to the user over a call instead of sending an SMS.
Voice is never sent automatically, so if you want it as a backup when SMS delivery fails, your application has to make that second request. It also helps with accessibility. Keep voice message templates under 500 characters, or the request returns 1009.
How do you prevent SMS pumping on OTP endpoints?
Monitor the send-to-verify ratio in real time — pumping shows sends spiking while verifications flatline. Add a CAPTCHA or challenge before the send endpoint, run carrier lookups to block non-mobile numbers, and restrict sends to the countries you serve.
Does Arkesel offer a single OTP endpoint?
Arkesel exposes two OTP endpoints, both on https://sms.arkesel.com and authenticated with an api-key header: a Generate call (/api/otp/generate) that creates and delivers the code, and a Verify call (/api/otp/verify) that checks the code the user entered. Build against the Arkesel developer documentation for the current contract.
Start building
You now have a complete SMS OTP API integration — the architecture, the real generate and verify contract, the full response-code table, and the production checklist to ship OTP verification that holds up under real traffic. The distance between a tutorial-grade flow and a production-grade one is everything after the first successful call: rate limiting, abuse protection, fallback channels, and monitoring.
Ship phone verification that holds up in production. Start with Arkesel Phone Number Verification, explore the Arkesel developer documentation to see how SMS and voice verification work from one API and which channels apply in each country, then create an Arkesel account and start testing in minutes. Check current rates on the pricing page.
Explore the Series
- OTP API Errors: 10 Common Issues and How to Fix Them (2026)
- OTP API Security: Best Practices for Secure Transactions (2026)
- SMS Pumping Fraud Prevention: How to Detect and Stop Artificially Inflated Traffic
- OTP Expiration Time Best Practices & Rate Limiting (2026)
- OTP API Provider Comparison 2026: Africa-Rated Guide
- OTP for Fintech Banking: Secure Transactions at Scale
- SMS vs Authenticator App vs Email OTP: Choosing the Right Delivery Channel
- What Is an OTP? How One-Time Passwords Work (2026 Guide)





