SMS

What is an SMS segment, and why did one message cost three?

A message that reads like one message can be billed as three. That is not a rounding quirk or a surcharge: it is the unit. SMS bills per segment, and a segment is a fixed amount of room that your text either fits into or does not.

Two things decide how many you get: which alphabet your text forces, and how much room that alphabet leaves per segment.

What exactly is a segment?

The chunk a single SMS can carry over the network. It is fixed by the standard the network implements, GSM 03.38, and not by anything Bird chooses.

There are two sizes, because there are two encodings:

EncodingOne segment holdsEach part of a longer message holds
GSM_7BIT160 septets153 septets
UCS270 UTF-16 code units67 code units

The second column is the part people miss. A message that does not fit gets smaller parts, not the same parts. Every piece of a split message carries a small header identifying which piece it is and how many there are, and that header eats into the same room the text needs. So the room does not divide evenly: 160 characters is one segment, but 161 is two segments of 153 rather than one of 160 plus a spare.

The count follows from that directly. If the encoded body fits a single segment it is one, and otherwise it is the encoded size divided by the smaller per-part size, rounded up.

Why did one message cost three?

Because the segment count is the billed quantity.

Bird works out the encoding, the character count and the segment count at the moment the send is accepted, and the price is keyed on that count alongside the destination, the message category and the sender type. When the charge is made, the quantity charged is the number of segments. Nothing else about the message changes the arithmetic, so a three-segment body is three billable units of whatever that route costs.

This is why the surprise usually arrives in the bill rather than at the API. The send succeeded, the message arrived, and the only sign anything unusual happened is a number you did not look at.

You do not have to guess at it. Every SMS response carries a segments object with count, encoding and characters, so the count is readable before you are billed for it, and worth logging on anything you send in volume.

Why does one emoji triple the cost?

Because it does not just add itself. It changes the alphabet for the entire message.

Bird tries GSM-7 first. If every character in the body can be encoded that way, the message is GSM-7 and gets 160 septets per segment. If even one character cannot be, the whole body falls back to UCS-2, and the room per segment drops from 160 to 70. Nothing is encoded per character: it is one decision for the whole message.

So a 150-character message is one segment. Add a single emoji and it is not 151 characters in one segment; it is a UCS-2 message of 151 characters that needs 67 code units per part, which is three segments. The emoji costs two of them by itself, because an emoji outside the Basic Multilingual Plane is stored as a surrogate pair and counts as two code units. Thirty-five of them fill a segment on their own.

The triggers that catch people are rarely deliberate:

  • Curly quotes. " and ' are GSM-7; the typographic “ ” ‘ ’ are not, and a word processor or CMS inserts them silently.
  • Emoji, as above.
  • Some accented letters but not others. é and ü are in the GSM-7 set and cost nothing extra. á, í and ú are not, and move the whole message to UCS-2.
  • CJK, Arabic, Thai and Hindi scripts, which are always UCS-2.

If your body is assembled from user input or pasted copy, assume it will meet one of these eventually.

Why doesn't the character count predict the cost?

Because neither encoding measures characters, and characters is a character count.

characters counts Unicode code points, which is the intuitive number and the wrong one for billing. GSM-7 measures septets, UCS-2 measures UTF-16 code units, and in both alphabets some characters cost two of the unit while counting once as a character.

Under GSM-7 the double-cost set is small and fixed: nine printable characters, , [, ], {, }, \, ~, ^ and |, plus one control code. Each costs two septets. That is what makes the boundary case so unintuitive: 159 ordinary characters followed by a euro sign is 160 characters and 161 septets, so it is two segments.

Under UCS-2 the same thing happens with any character outside the Basic Multilingual Plane, most visibly emoji.

Read count for what you will pay, against the SMS rates for the destination. Read characters only when you actually want to know how many characters there are.

How do I keep a message to one segment?

Three things, roughly in order of how much they pay back.

  1. Keep the body in GSM-7. This is worth more than shortening the text, because it more than doubles the room. Turning on options.smart_encoding on a send replaces the accidental triggers, curly quotes and the like, with their GSM-7 equivalents before the message goes out, which can halve the segment count. It is off by default, and it applies only when every remaining character can be GSM-7: if an emoji survives the replacement, the body is sent exactly as supplied. Smart encoding has the replacement table.
  2. Budget against the right limit. If the body can ever be UCS-2, the number to design against is 70, not 160. And if it can ever exceed one segment, budget against 153 or 67, because that is what each part actually holds.
  3. Watch what you interpolate. A name, an address or a product title from user data is the usual route to an unexpected UCS-2 message, and it is invisible in the template you wrote.

If you are sizing this up before you build, Bird SMS is where the sending side lives.

SMS character limits and segment counting covers the GSM-7 set character by character and how to read the segment counts back from the API.

Construye sobre la misma red.

Obtén una clave API de prueba de inmediato. El acceso a producción se desbloquea cuando añades un método de pago y verificas un remitente.

Empieza con un canal.
Añade los demás cuando estés listo.

Una clave API de prueba es tuya de inmediato. El acceso a producción se desbloquea cuando añades un método de pago y verificas un remitente.

¿Usas Claude Code, Cursor o Codex? Copia un prompt de configuración y tu agente instalará el Bird CLI y las habilidades por ti. Elige el tuyo:

Cursor