Skip to content
TechSep 6, 20267 min read

What an SMS Campaign Actually Costs

Email fails softly: a wrong audience wastes a send. SMS fails expensively, immediately, and on your own invoice. Three decisions made before the first campaign determine whether the channel is cheap or ruinous.

SH
SegmentHub Team
Customer Journey Guides

148 characters

3 billable segments · Unicode

One character changed everything

Every marketing channel has a failure mode that only shows up at scale. For email it is a damaged sending domain. For push it is uninstalls. For SMS it is an invoice that is three times what anybody estimated, from a gateway that will not explain why — and a regulator's letter about people who asked to be left alone.

Both problems are avoidable, and both are avoided in the same place: before the send, not in the report afterwards.

1. A message is not billed by the character

It is billed by the segment. A message written entirely in the basic GSM alphabet fits 160 characters into one segment. A message containing a single character outside that alphabet switches the whole message to a 16-bit encoding, and the limit drops to 70.

The characters that trip this are not exotic. They are the ones a word processor inserts silently: the curly apostrophe, the em dash, the ellipsis, the non-breaking space, and most accented letters outside a handful of Western European ones. Paste a sentence from a document into a campaign body and the encoding can flip without a single visible change on screen.

A 148-character message is one segment in the basic alphabet and three once it becomes Unicode. On a million-recipient list, that is two million messages nobody budgeted for.

There are two more details that a naive character count gets wrong:

  • Some basic-alphabet characters cost double. The euro sign, curly braces, square brackets, the tilde, the pipe, the backslash and the caret each consume two units of the 160.
  • Multi-part messages get smaller, not just more numerous. Once a message needs more than one segment, each segment gives up space to the header that reassembles them — 160 becomes 153, and 70 becomes 67. A message at 161 characters is not "one and a bit"; it is two segments of 153.

The practical rule: compute the segment count on the server, with the same code that will count it at send time, and show it live in the campaign builder. A count reimplemented in the browser will eventually disagree with the one that bills you, and it will disagree in the direction nobody notices.

Count the opt-out sentence too. "Reply STOP to unsubscribe" is nineteen characters that arrive on the invoice like any other.

2. Links are the other place the money goes

Email tracks clicks by rewriting every link into a long signed URL that identifies the campaign and the recipient. Copy that pattern into SMS and you have added 120 characters to every message — often a whole extra segment, on every send, forever.

The answer is a short link: about twenty-five characters, minted once per campaign rather than once per recipient. That costs one thing worth being honest about — clicks are then counted per link rather than per person, because per-person attribution requires a distinct URL per recipient, which is exactly the per-message row this approach exists to avoid.

Two details make short links behave well:

  • Apply your UTM parameters to the destination before it is shortened. The recipient follows an opaque link, but your analytics still sees the campaign that sent them.
  • Make the token random, and drop the ambiguous characters. A sequential token lets anyone enumerate every campaign's destinations by counting. And these links get read off a phone screen and typed by hand, so zero against capital O, and one against lowercase L, are worth removing from the alphabet.

3. STOP is a legal instruction, and it arrives with nothing attached

An inbound SMS carries a phone number. No cookie, no session, no visitor id. That single fact shapes everything about consent on this channel.

The suppression list is the most load-bearing thing in the system. It has to outlive the behavioural profile — someone who opts out and then stops visiting for six months must not become messageable again when their profile expires. It has to be checked when the audience is resolved and again for each message on the way out, because a campaign fanned out at 09:00 must not message somebody who replied STOP at 09:05. And when the list cannot be read, the correct behaviour is to refuse to send: an empty list must never be interpreted as "nobody has opted out".

One number, one decision. If you send both SMS and WhatsApp, they reach the same person at the same number. Keeping separate opt-out lists per protocol means an opt-out on one leaves the other free to keep messaging — which is precisely the failure the list exists to prevent.

Read the keyword, not the sentiment. Honour STOP, START and HELP when the entire reply is one of those words, with accents and punctuation folded so that a message typed on a localised keyboard still matches. Somebody who writes "please stop sending these on Sundays" is making a request to a human being. Unsubscribing them silently is worse than not reading the message at all.

Honour the carrier's opt-out too. A person can opt out at the network without your platform ever seeing a message. The gateway then rejects your next send with a specific code. Treat that as an opt-out and record it as one — not as a bad number, which is what it looks like if you only read the HTTP status.

Make re-subscription hard. Re-uploading last year's list is the classic way to resurrect people who opted out. An opt-out should never be lifted by a bulk import or an inferred signal; only a deliberate act — a confirmed double opt-in, or a human action recorded in the panel — should be able to.

Quiet hours are not a nicety

US rules bar marketing messages outside 08:00–21:00 local time, and several EU markets set their own windows. Unlike email, where a badly timed send is merely ignored, a 02:00 SMS is a complaint and sometimes a fine.

Default the window to on, and postpone a campaign that comes due inside it rather than dropping it. And check the implementation against the case that a naive comparison gets exactly backwards: a window from 21:00 to 08:00 wraps midnight, so "is now inside the window" is not start < now < end.

Store the number in one shape, from the first write

Every comparison in an SMS system is an exact match: the suppression lookup, the send-time deduplication, the inbound STOP lookup. That means 0532 123 45 67, +90 532 123 45 67 and 905321234567 are three different people to all of them.

Normalise to E.164 before anything is stored, using an explicit default country rather than a guess, and refuse a number that will not parse rather than writing it and finding out later. A number stored in the wrong shape produces a profile that reads as reachable in the panel, never matches its own opt-out, and cannot be found when that person replies STOP. It is the failure mode that looks like it worked.

The checklist before your first send

  • Confirm the segment count shown in the builder is computed by the same code that counts at send time.
  • Preview the exact text a handset receives, opt-out sentence included.
  • Import your existing do-not-message list on day one.
  • Set the daily budget and the per-second rate to what your gateway actually accepts.
  • Point the gateway's inbound webhook at the platform, and verify a test STOP suppresses the number.
  • Set the quiet-hours window for the markets you send to, not just your own timezone.
  • Decide the default country used to normalise numbers, and check what happens to one that will not parse.

See how targeting, cost estimation, consent and reporting fit together on the SMS & WhatsApp page, or compare the same controls in the Email Campaigns module.