SMS character limits: GSM-7, Unicode, and why one symbol can triple your bill
10 June 2026 · The SMSAPI team
Everyone knows an SMS is 160 characters. Almost nobody bills for one. The message you wrote as a single SMS often leaves your account as two or three, and the reason is rarely the message length — it's the encoding. If you send at any volume through a bulk SMS API, the difference between 160 and 70 characters per segment is the difference between your forecast and your invoice.
This is a guide to where those numbers come from, which characters quietly flip a message into the expensive mode, and how to keep a template inside its budget.
The two alphabets of SMS
An SMS payload is 140 bytes. What changes is how characters are packed into those bytes, and there are two schemes that matter.
GSM-7 is the original 7-bit alphabet. Seven bits per character into 140 bytes gives the famous 160 characters. The alphabet covers the basic Latin letters and digits, common punctuation, and a handful of extras. If every character in your message is in that set, you get the full 160.
UCS-2 is the Unicode encoding used when anything in the message falls outside the GSM-7 set. Each character now takes 16 bits, and the same 140 bytes hold only 70 characters. The whole message switches — there is no mixed mode where only the special character pays the premium. One character outside GSM-7 reprices every character in the message.
That switch is the single most common source of surprise on an SMS bill. The message didn't get longer; it got wider.
What fits in GSM-7, and what secretly doesn't
The GSM-7 basic set is roughly: A–Z, a–z, 0–9, space, newline, and common punctuation like the full stop, comma, colon, question mark, @, #, %, &, and the straight apostrophe and quote. A few currency and accented characters are in there too, including £ and ¥.
Some characters are in the extension table, which means they're sendable in GSM-7 but cost two characters each: the square brackets, curly braces, backslash, caret, pipe, tilde, and the euro sign. A URL with a path in square brackets, or a message that pipes fields together, spends its budget faster than it looks.
Then there are the characters that look ordinary but are not in GSM-7 at all:
- Smart quotes and apostrophes (' ' " ") — the ones word processors substitute automatically. The straight versions are GSM-7; the curly versions are Unicode.
- The em dash and en dash — only the plain hyphen is in the set.
- The ellipsis character (…) — three full stops are GSM-7; the single ellipsis glyph is not.
- Emoji — all Unicode, and most count as two UCS-2 characters because they're encoded as surrogate pairs.
A template drafted in a document editor and pasted into a campaign tool routinely picks up two or three of these. The text reads identically on screen and bills at less than half the per-segment capacity.
One ₹ makes the whole message Unicode
The character that catches Indian senders most often is the rupee sign. ₹ is a relatively recent addition to Unicode and is not part of the GSM-7 set that SMS routes use, so a transactional message like a payment confirmation that includes ₹ is a Unicode message, at 70 characters per segment, even though every other character in it is plain ASCII.
You have two honest choices. Write the amount as Rs. 1,499 and stay in GSM-7, or keep the ₹ and budget the message at 70 characters per segment. Both are legitimate; what stings is choosing the symbol without re-budgeting the template. For transactional SMS at volume — order confirmations, payment receipts, balance alerts — that one glyph can be the difference between one segment and three.
Concatenation: where 160 becomes 153
When a message exceeds one segment, the network splits it and the receiving handset stitches it back together. The stitching instructions ride inside each segment as a user data header, and that header takes six bytes out of every part. The practical capacities become:
- GSM-7: 153 characters per segment instead of 160
- UCS-2: 67 characters per segment instead of 70
So a 161-character GSM-7 message is not "one segment plus one character" — it's two segments of up to 153, and you're billed for two. A 100-character message containing one emoji is 100 characters of UCS-2, which is two segments, billed as two.
The arithmetic worth memorising: 1 segment holds 160 or 70; every segment after the first holds 153 or 67. The handset shows one message either way, which is why nobody notices until the invoice.
Indian languages are always Unicode — plan for 70
Devanagari, Tamil, Telugu, Bengali, Gujarati, Kannada, Malayalam, Odia, Punjabi — none of these scripts exist in GSM-7. A Hindi or Tamil SMS is always UCS-2, always 70 characters per segment (67 once it splits). That isn't a penalty to engineer around; it's the normal cost of sending in the reader's language, and a message a customer understands is worth more than a shorter one they don't.
What it does mean is that regional-language templates need their own budgets. A 150-character English template that translates into 120 characters of Hindi hasn't stayed "one message" — it's gone from one segment to two. Budget regional templates against 70 from the start rather than translating an English draft and hoping.
If you also send beyond India, note that the same encoding rules apply on an international SMS API route — the per-segment economics just vary by destination.
Variables are where templates go over budget
Under DLT, your content template is fixed and the variables are filled at send time. The template might be comfortably inside one segment with the sample values you tested — and over it in production, when a customer's name is longer, an order ID grows a prefix, or a tracking URL gains query parameters.
Three habits prevent this:
- Budget the worst case, not the sample. Sum the fixed text plus the maximum realistic length of each variable. If the worst case crosses a segment boundary, decide now whether that's acceptable.
- Keep URLs short and stable. Long tracking links are the most common variable to blow a budget. A short branded link does the same job in a fraction of the characters.
- Watch what fills the variables. The fixed template may be pure GSM-7, but if a variable is filled with a name in Devanagari or a smart quote from an upstream system, the whole message flips to Unicode at send time. Sanitise or normalise variable content before it reaches the send call.
A practical pre-send checklist
Before a template ships, run it through five quick checks:
- Paste the final text into a plain-text editor and confirm no smart quotes, dashes, or ellipsis characters survived the journey from the draft document.
- Decide explicitly: GSM-7 or Unicode. If Unicode, budget at 70/67 and stop resenting it.
- Compute the worst-case length with maximum-width variable values, not averages.
- Check the segment count your provider reports back on a test send rather than trusting your own arithmetic — the send response tells you how the route actually encoded and split the message. The SMS quickstart shows where that appears in the response payload.
- Re-check after every copy edit. A marketer swapping a hyphen for an em dash in the campaign tool can reprice the whole campaign.
The short version
An SMS is 140 bytes, not 160 characters. GSM-7 fits 160 per segment, Unicode fits 70, and concatenated messages drop to 153 and 67. One character outside the GSM-7 set — a ₹, a curly quote, an emoji — switches the entire message to Unicode. Indian-language messages are always Unicode, so budget them at 70 from the first draft. And because DLT templates meet real-world variable values at send time, budget the worst case, keep URLs short, and read the segment count off the API response rather than assuming. The character limit isn't really a limit; it's a price schedule, and now you can read it.