Back to blog

sms-api

What Is an SMS API? A Plain-English Guide

What is an SMS API? A plain-English guide to how SMS APIs work, what they cost, and what you can build with one. Send your first text today.

Noa

What Is an SMS API? A Plain-English Guide

An SMS API is a service that lets your software send and receive text messages by making web requests — the same way an app might fetch weather data or process a payment. Instead of a person typing a message into a phone, your application sends a small piece of structured data to a messaging provider, and the provider handles delivery to the recipient's phone. This guide walks through how that works from the first HTTP request to the delivered text, what you can build with one, and what to look for when choosing a provider.

SMS API, Defined Without the Jargon

Take the term apart and it stops being intimidating.

SMS (Short Message Service) is the text messaging system built into every mobile phone since the 1990s. It travels over the cellular network, needs no app installed, and reaches any phone that can receive a call.

API (Application Programming Interface) is a way for one piece of software to talk to another. MDN Web Docs defines an API as a set of features and rules that enable interaction through software, as opposed to a human user interface. In other words: a human uses buttons and screens; a program uses an API.

Put together, an SMS API is the software doorway into the text messaging network. Your code knocks on that door with a request ("send this text to this number"), and the provider on the other side does everything a human would otherwise do by hand — picks a sending number, hands the message to a carrier, tracks whether it arrived, and collects any reply.

A common analogy: the API is a restaurant waiter. You (the application) hand the waiter (the API) an order written in a fixed format. The waiter takes it to the kitchen (the carrier network), and later returns with your food (a delivery confirmation) — without you ever needing to know how the kitchen operates.

SMS API vs. SMS gateway — are they the same thing?

You'll see both terms used interchangeably, and that causes confusion. They are related but not identical:

  • An SMS gateway is the underlying system that converts internet traffic into cellular network messages. It's the bridge itself.
  • An SMS API is the developer-facing interface that sits on the gateway — the documented set of requests and responses your code uses.

Every SMS API has a gateway behind it. But a gateway without an API is a piece of telecom plumbing you can't program against. When you evaluate providers, you're really evaluating the API: its documentation, its reliability, its error reporting, and what it costs.

Terms you'll meet in every SMS API doc

A short decoder ring before the technical walkthrough. These eight terms cover the majority of what messaging documentation throws at you:

| Term | Plain-English meaning | |------|------------------------| | A2P | Application-to-Person — messages sent by software to humans. Everything an SMS API sends is A2P traffic. | | P2P | Person-to-Person — messages between two humans on their phones. Carriers treat A2P and P2P traffic differently. | | E.164 | The international phone number format: +, country code, number, no spaces. +15555550123 is E.164; (555) 555-0123 is not. | | Segment | The 160-character unit SMS is billed and transmitted in. Longer texts are split into segments and stitched back together on the phone. | | Long code | A standard 10-digit phone number used for sending. What 10DLC regulates. | | Short code | A 5–6 digit number (like 55555) leased for high-volume sending. A separate, more expensive system. | | DLR | Delivery Receipt — the carrier's confirmation that a message reached the handset. Surfaces in APIs as a delivered status. | | Webhook | A URL on your server that the provider calls when events happen — deliveries, failures, incoming replies. |

You don't need to memorize these; the rest of this guide uses each one in context.

How an SMS API Works: From HTTP Request to Delivered Text

Here's the full lifecycle of a single message, using the senderZ API as a concrete example. Other providers differ in details, but the shape of the flow is nearly universal.

Step 1: Your application makes a request

Sending a text is one HTTP POST. With curl:

curl -X POST https://api.senderz.com/v1/messages \
  -H "Authorization: Bearer tf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+15555550123",
    "channel": "auto",
    "body": "Your appointment is confirmed for Tuesday at 2:00 PM."
  }'

The same request in TypeScript, using fetch:

const response = await fetch('https://api.senderz.com/v1/messages', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.SENDERZ_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: '+15555550123',
    channel: 'auto',
    body: 'Your appointment is confirmed for Tuesday at 2:00 PM.',
  }),
})

const result = await response.json()
console.log(result.message_id, result.status)

Three fields carry the whole request:

| Field | What it means | |-------|---------------| | to | The recipient's phone number in E.164 format (+ followed by country code and number) | | channel | Which delivery path to use — auto, imessage, sms, or rcs | | body | The message text itself |

The Authorization header carries your API key — a secret string that identifies your account. Treat it like a password: keep it in an environment variable, never in source code.

Step 2: The provider validates and queues the message

Before anything touches a phone network, the API checks the request. Is the phone number a valid E.164 number? Is the message body present and under the length limit? Is your API key active? If a check fails, you get an immediate structured error:

{
  "error": "Invalid phone number format. Must be E.164 (e.g. +15551234567)",
  "code": "VALIDATION_ERROR"
}

If everything passes, the message is accepted and placed on an internal queue, and the API responds right away — before delivery happens:

{
  "message_id": "01J1QRS7GVXK2E9M3P0WFT8DNB",
  "status": "queued",
  "channel": "auto",
  "estimated_delivery_ms": 2000
}

That 202 Accepted status code is a deliberate design choice you'll see across messaging APIs. Delivery to a phone takes one to several seconds and involves systems the API doesn't control, so the API confirms "I have your message and will deliver it" rather than making your code wait. The message_id is your receipt — you use it to check what happened next.

Step 3: Routing and handoff to the network

Behind the queue, the provider decides how to deliver the message. With senderZ, a channel of auto means the platform checks whether the recipient can receive iMessage first, and falls back through RCS to plain SMS if not. The message then leaves the provider's infrastructure and enters the delivery network — Apple's servers for iMessage, or a carrier's network for SMS.

This step is where compliance checks also run: has this recipient opted out? Is it inside permitted sending hours for marketing messages? A well-built API enforces these rules before the message goes out, not after a complaint arrives.

Step 4: Tracking delivery status

Your message_id lets you ask what happened:

curl https://api.senderz.com/v1/messages/01J1QRS7GVXK2E9M3P0WFT8DNB \
  -H "Authorization: Bearer tf_your_api_key"
{
  "message_id": "01J1QRS7GVXK2E9M3P0WFT8DNB",
  "direction": "outbound",
  "status": "delivered",
  "channel": "imessage",
  "to": "+15555550123",
  "from": "+15555550100",
  "error_reason": null,
  "attempt_count": 1,
  "sent_at": "2026-06-03T18:04:11Z",
  "delivered_at": "2026-06-03T18:04:12Z",
  "created_at": "2026-06-03T18:04:10Z"
}

A message moves through a small set of statuses:

| Status | Meaning | |--------|---------| | queued | Accepted by the API, waiting for delivery | | sent | Handed off to the delivery network | | delivered | Confirmed received on the recipient's device | | failed | Could not be delivered — error_reason says why |

Step 5: Receiving replies and events with webhooks

Polling the status endpoint works, but the cleaner pattern is a webhook: a URL on your server that the provider calls when something happens. You register it once:

curl -X POST https://api.senderz.com/v1/webhooks \
  -H "Authorization: Bearer tf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/senderz",
    "events": ["message.delivered", "message.failed", "message.received"],
    "secret": "your_signing_secret"
  }'

From then on, your server hears about deliveries, failures, and — critically — inbound messages. When a customer replies to a text, the message.received event arrives at your URL as a small JSON document:

{
  "event": "message.received",
  "message_id": "01J1QSA9M2NKXW4RB7E0YCH5TF",
  "from": "+15555550123",
  "to": "+15555550100",
  "body": "Yes, I'd like to confirm my appointment",
  "channel": "imessage",
  "timestamp": "2026-06-03T18:09:42Z"
}

That's how two-way messaging works: the API carries the outbound direction, and webhooks are the return path. The secret field from the registration is used to sign each event with HMAC-SHA256, delivered in an X-Senderz-Signature header, so your server can verify the call actually came from the provider and not from someone who guessed your URL. A minimal handler that verifies the signature and reacts to a reply looks like this:

import crypto from 'node:crypto'

export async function handleWebhook(request: Request): Promise<Response> {
  const rawBody = await request.text()
  const signature = request.headers.get('X-Senderz-Signature') ?? ''

  // Recompute the HMAC and compare — reject anything that doesn't match
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET!)
    .update(rawBody)
    .digest('hex')

  if (signature !== expected) {
    return new Response('invalid signature', { status: 401 })
  }

  const event = JSON.parse(rawBody)

  if (event.event === 'message.received') {
    // A customer replied — hand the text to your application logic
    console.log(`Reply from ${event.from}: ${event.body}`)
  }

  if (event.event === 'message.failed') {
    // Mark the message for retry or flag the contact
    console.log(`Delivery failed for ${event.message_id}`)
  }

  // Respond 2xx quickly — providers retry on anything else
  return new Response('ok', { status: 200 })
}

One operational note: if your endpoint returns a non-2xx status, senderZ retries three times with increasing delays (2, 4, then 8 seconds) before marking the delivery failed. Return 200 fast and do heavy processing afterward. The senderZ webhook reference covers every event payload, the retry policy, and signature verification in full.

What You Can Build with an SMS API

The pattern above — one request out, events back in — supports a wide range of real products. These are the categories that come up again and again.

One-time passcodes (OTP) and verification

The single largest use of SMS APIs. Your app generates a 6-digit code, texts it via the API, and the user types it back to prove they own the phone number. Because codes expire in minutes, delivery speed matters — which is one reason platforms that can route over iMessage (typically about a second) have an edge over SMS-only delivery (typically 3–5 seconds).

The whole flow fits in a few lines. Generate, store, send:

async function sendVerificationCode(phoneNumber: string): Promise<void> {
  // 1. Generate a 6-digit code and store it with a 5-minute expiry
  const code = crypto.getRandomValues(new Uint32Array(1))[0] % 900000 + 100000
  await saveCode(phoneNumber, code, { expiresInSeconds: 300 })

  // 2. Send it — marking the message as OTP gets it priority routing
  const response = await fetch('https://api.senderz.com/v1/messages', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SENDERZ_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      to: phoneNumber,
      channel: 'auto',
      body: `Your verification code is ${code}. It expires in 5 minutes.`,
      message_type: 'otp',
    }),
  })

  if (!response.ok) {
    const err = await response.json()
    throw new Error(`Send failed: ${err.code} — ${err.error}`)
  }
}

The message_type: "otp" field tells the platform this message is time-sensitive; senderZ gives OTP sends queue priority over routine alerts and marketing traffic. When the user submits the code, you compare it against the stored value — the API's job is done the moment the text lands.

Appointment reminders

Clinics, salons, contractors, and service businesses cut no-shows by texting reminders a day before and an hour before each appointment. The API call runs from a scheduled job; a message.received webhook catches "can I reschedule?" replies. Reminder traffic is transactional and expected, which keeps it on the right side of both carriers and regulators — provided the customer gave their number for exactly this purpose.

Order and delivery notifications

"Your order shipped." "Your table is ready." "Your driver is 5 minutes away." Transactional updates are short, expected, and read within minutes — the natural strengths of the channel. These are usually fired by an event in your own system (order state change) making a single API call.

Two-way customer conversations

Support and sales conversations over text, with each inbound message hitting your webhook and each reply going out through the send request. Paired with an AI layer, this becomes an automated assistant that can answer common questions and hand off to a human when needed.

Opt-in marketing campaigns

Promotions and announcements to customers who agreed to receive them. This is the use case with the strictest legal rules — consent, opt-out handling, and time-of-day restrictions all apply, which is covered in the next section.

What an SMS API Alone Doesn't Solve

Marketing pages tend to end at "send a text with one line of code." Real deployments run into four issues that the raw send request doesn't address — and this is where providers genuinely differ.

Deliverability and carrier filtering

Carriers filter commercial text traffic for spam, and a message that is "sent" is not guaranteed to arrive. Sender reputation, message content, volume patterns, and registration status all influence whether a carrier delivers, delays, or silently drops a message. A useful mental model: the API guarantees the handoff, not the arrival — which is why delivery receipts (Step 4 above) matter, and why serious platforms track per-message status instead of assuming success.

The habits that keep delivery rates healthy are behavioral, not technical:

  • Text people who asked to hear from you. Opt-in lists generate few spam reports; purchased lists generate many, and spam reports poison the sending number for everyone.
  • Ramp volume gradually on new numbers. A number that goes from zero to thousands of messages overnight looks like a spammer to every filter watching it.
  • Identify yourself in the message. "Hi, it's Riverside Dental" outperforms an anonymous text on both response rate and filtering.
  • Keep sending patterns human-shaped. Bursts of identical messages to hundreds of new numbers trip filters that conversational, varied traffic never touches.

In the United States, the Telephone Consumer Protection Act (TCPA) governs commercial texting. The short version: you need the recipient's consent before texting them, you must honor opt-out requests (the word STOP has a legally significant meaning in a text reply), and marketing messages have time-of-day restrictions. The FCC publishes consumer guidance on unwanted texts and robotexts that shows how seriously regulators treat this. Some APIs leave every bit of this to you; senderZ processes STOP and START keywords automatically, blocks sends to opted-out numbers before they leave the platform, and enforces quiet hours for marketing traffic.

Registration requirements like 10DLC

Sending commercial SMS through traditional US A2P routes requires registering your business and your messaging campaigns with carriers — a system called 10DLC, with fees, review queues, and per-message surcharges. It's a large enough topic that we wrote a dedicated explainer: What Is 10DLC? And How to Send SMS Without It. The one-paragraph version: registration takes days to weeks and adds recurring costs, and delivery paths exist (iMessage among them) that don't require it at all.

Character limits and message segments

A single SMS carries 160 characters using the basic Latin alphabet — or only 70 characters when the text includes emoji or non-Latin characters. Longer messages are split into segments of 153 (or 67) characters and reassembled on the phone. This matters because per-message providers bill per segment: a 320-character message with an emoji is five segments, not one. Flat-rate platforms and internet-based channels like iMessage make the segment question irrelevant, but you should know it exists whenever you see per-message pricing.

Failures need handling

Numbers get disconnected. Phones stay off for days. Carriers reject traffic. Your code should treat failed as a normal outcome, read the error_reason, and decide: retry, try another channel, or flag the contact as unreachable. In practice that's a webhook branch, not a monitoring dashboard:

// Inside your webhook handler
if (event.event === 'message.failed') {
  const message = await getMessageRecord(event.message_id)

  if (message.attempt_count < 3) {
    // Transient failure — requeue with the channel forced to SMS
    await resendMessage(message, { channel: 'sms' })
  } else {
    // Persistent failure — stop sending and surface it
    await markContactUnreachable(message.to)
  }
}

The APIs worth using make failures visible and specific instead of leaving messages in a permanent "sent" limbo. If the status response has an error_reason field and the webhook stream includes message.failed, you can automate recovery; if failures disappear silently, you can't.

SMS API vs. iMessage API vs. RCS: The Channel Question

"SMS API" has become shorthand for "text messaging API," but SMS is only one of the channels a modern messaging platform can use — and for US audiences it's often not the strongest one.

| Channel | Transport | Delivery speed | Rich features | Registration required | |---------|-----------|---------------|---------------|------------------------| | SMS | Cellular network | ~3–5 seconds | Plain text, 160-char segments | 10DLC for commercial A2P traffic | | iMessage | Internet (Apple) | ~1 second | Read receipts, typing indicators, high-res media, group threads | None | | RCS | Internet (carrier/Google) | ~1–2 seconds | Rich cards, media, read receipts | Varies by route |

The practical takeaway: you rarely want to pick one channel per recipient by hand. A platform-level channel: "auto" setting gets each message onto the strongest channel that recipient supports, from a single API call.

How auto-routing decides

When senderZ receives a send with channel: "auto", the routing layer works down a fallback ladder:

  1. Check iMessage capability. Can this recipient receive iMessage? The result is cached, so repeat sends to the same contact skip the lookup.
  2. Fall back to RCS if iMessage isn't available and the recipient's device supports rich messaging.
  3. Fall back to SMS as the floor — any phone that can receive a call can receive SMS.

The message record keeps both facts: the channel you requested (auto) and the channel that actually delivered (imessage, rcs, or sms), so your analytics stay honest about what happened. If the iMessage side interests you, how to send iMessage from an API explains that delivery path end to end.

How to Choose an SMS API

A short checklist, in rough priority order:

1. Delivery channels. SMS-only, or SMS plus iMessage and RCS with automatic fallback? For US consumer audiences, iMessage capability changes both delivery speed and how messages look on screen.

2. Compliance built in, or left to you. Opt-out processing, consent logging, and quiet-hour enforcement are legal requirements, not features. If the provider doesn't handle them, you're building them yourself before launch.

3. Pricing model. Per-message pricing (typically $0.008–$0.01 per SMS segment plus carrier surcharges through traditional providers) suits unpredictable volume; flat monthly pricing suits steady conversation-heavy use. senderZ uses flat monthly plans with unlimited messages — the plan tier caps how many new contacts you reach per day, not how many messages you send.

4. A test mode. You want to integrate and run your test suite without texting real phones. senderZ sandbox keys (prefixed tf_test_) accept the same requests as production and synthesize the full delivery lifecycle — statuses, webhooks, failures — without a real message leaving the platform.

5. Webhook quality. Signed events, delivery receipts, inbound message support, and retry behavior on your server's downtime. This is the half of the API you'll live with daily once sends are working.

6. Documentation you can act on. Every request should show the exact response you'll get back, including the error cases. If the docs only show the happy path, you'll be discovering the error format in production.

7. Time to first message. A reasonable benchmark: signup to first delivered message in under five minutes. The senderZ quickstart is written against that bar — API key, one request, delivered text.

The pricing-model decision deserves its own summary, because it's the one that compounds over time:

| | Per-message pricing | Flat monthly pricing | |---|---------------------|----------------------| | Cost scales with | Message volume (per segment) | Plan tier (new contacts per day) | | Long messages | Cost more (multiple segments) | Same as short ones | | Two-way conversations | Every reply-and-response costs | Included | | Predictability | Varies month to month | Fixed | | Fits | Spiky, low-volume sending | Conversation-heavy, steady use |

Neither model wins universally. High-volume one-way blasts can favor per-message rates; anything conversational — support threads, reminders with replies, AI-assisted follow-ups — tends to favor flat pricing, because two-way traffic doubles the metered message count without doubling the value.

FAQ

FAQ

Frequently asked questions

What is an SMS API in simple terms?

An SMS API is a service that lets software send and receive text messages through web requests. Your application sends a structured request — recipient number, message text — to a provider, and the provider delivers the text over the phone network and reports back what happened.

Is an SMS API the same as an SMS gateway?

Not quite. The gateway is the underlying bridge between the internet and the cellular network; the API is the documented, developer-facing interface built on that bridge. Every SMS API uses a gateway underneath, but the API is the part you actually program against and evaluate.

Do I need to be a developer to use an SMS API?

For the API itself, yes — it's designed for code. But many platforms, senderZ included, offer a no-code interface on the same infrastructure, so a business can send campaigns and manage conversations without writing anything. The API and the visual interface are two doors into the same system.

How much does an SMS API cost?

Two models dominate. Per-message pricing runs roughly $0.008–$0.01 per SMS segment through traditional providers, plus carrier surcharges and 10DLC registration fees. Flat-rate platforms charge a monthly subscription with unlimited messages — senderZ plans start at $49/month with no per-message fees.

Can an SMS API send iMessage instead of SMS?

A traditional carrier-based SMS API cannot — iMessage runs on Apple's network, not the cellular SMS system. Platforms built for both, like senderZ, check whether the recipient supports iMessage and route there first, falling back to SMS automatically when they don't.

Does using an SMS API require 10DLC registration?

It depends on the delivery route. Commercial SMS through traditional US A2P channels requires 10DLC registration — brand vetting, campaign approval, and recurring fees. Internet-based channels like iMessage have no carrier registration requirement, which is why multi-channel routing can remove 10DLC from the critical path for many use cases.


Ready to see the request-to-delivered flow for yourself? The senderZ quickstart takes you from API key to a delivered message in a few minutes, and every plan starts with a 14-day free trial — no credit card, no carrier registration, no waiting on approval queues.

Tagged sms-api sms api-basics

Ready to start sending?

Create your free account and send your first message in minutes.