149 countries · crypto-native · no KYC

SMS Unicode Encoding: GSM-7, UCS-2, and the Bytes Underneath

Why does one curly quote turn a 160-character text into a 70-character one? The answer is in how SMS encodes characters into a fixed number of bits. Here's the mechanism, not just the rule.

$0.035/msg from sub-100ms median 98.6% delivered
SMS Unicode Encoding: GSM-7, UCS-2, and the Bytes Underneath — smsroute
$0.004
per SMS from
149
countries
60s
to first message
6
crypto rails
Most developers learn the rule from our character-limit guide: 160 characters in GSM-7, 70 in Unicode, and one stray special character flips the whole message to the lower limit. This is the deeper *why*. SMS Unicode encoding comes down to how many bits each character costs, and understanding the bit-level mechanism explains every otherwise-baffling behavior — why the limit is exactly 160, why it drops to exactly 70, why some 'GSM' characters cost double, and why your options for non-Latin text are what they are. Once you see the bytes, the rules stop being arbitrary.

The rule everyone knows, the mechanism few do

Most developers learn the rule from our character-limit guide: 160 characters in GSM-7, 70 in Unicode, and one stray special character flips the whole message to the lower limit. SMS Unicode encoding comes down to how many bits each character costs. Understanding the bit-level mechanism explains every otherwise-baffling behavior: why the limit is exactly 160, why it drops to exactly 70, why some 'GSM' characters cost double, and why your options for non-Latin text are what they are.

This is the reference for when 'just keep it under 160' isn't enough: when you're building the encoding logic, supporting many languages, or debugging why a message costs more segments than its character count suggests. For A2P (application-to-person) messaging, understanding encoding is critical because carrier filtering and concatenation handling depend on correct segment counting. When submitting via SMPP, the data_coding field must be set correctly (0x00 for GSM-7, 0x08 for UCS-2) to avoid encoding mismatches.

The bit math behind the limits

The bit math behind the limits — comparison diagram

An SMS payload is a fixed size: 140 bytes, or 1,120 bits. That single constant, divided by the bits-per-character of each encoding, produces every limit you've memorized. This is defined in the 3GPP TS 23.038 specification, which standardizes the GSM 7-bit default alphabet and UCS-2 encoding for SMS. When concatenation is used (via UDH), the payload shrinks to 153 bytes per segment, reducing the GSM-7 limit to 153 characters.

Encoding Bits per character Chars in 1,120 bits Covers
GSM-7 7 bits 160 Basic Latin, digits, common punctuation
UCS-2 (Unicode) 16 bits 70 Any Unicode character: all scripts, emoji
GSM-7 extension char 14 bits (2 slots) counts as 2 { } [ ] ( ) ~ | \ ^ €

There it is: 1,120 ÷ 7 = 160, and 1,120 ÷ 16 = 70. The limits aren't arbitrary conventions; they're arithmetic on a fixed payload. UCS-2 costs 16 bits per character because it encodes the full Unicode space, so it fits less th

Why one character flips the whole message

The behavior that surprises everyone: a single non-GSM-7 character switches the *entire* message to UCS-2, not just that character. This follows directly from the encoding model. A message uses one encoding for all its characters: it can't mix 7-bit and 16-bit characters in the same payload. So the moment any character falls outside the GSM-7 set, the whole message must use UCS-2, and every character in it now costs 16 bits, dropping the limit to 70.

The transliteration trade-off

When cost matters and content allows, you can *transliterate* — replace non-GSM characters with GSM-7 approximations (é→e, curly quote→straight quote, — →-) to keep a message in the cheaper 160-character encoding. It's a real lever, and a real trade-off.

  1. Sanitize incidental non-GSM charactersCurly quotes, en/em-dashes, and stray Unicode punctuation from word processors add no meaning — transliterate them to GSM equivalents and keep the message in GSM-7. Pure savings, no loss.
  2. Don't mangle meaningful charactersTransliterating a person's name (José→Jose) or an accented word can change or degrade meaning; for names and languages where accents matter, accept UCS-2 rather than corrupt the content.
  3. Never transliterate non-Latin scriptsYou can't render Arabic or Chinese in GSM-7 meaningfully — those markets are UCS-2, and forcing Latin would make the message unreadable. Plan around the 70-character limit for them.
  4. Detect encoding before sendingScan the final body for non-GSM characters and compute the real segment count and cost — the same check your batch and cost logic needs so a stray character doesn't silently multiply spend. Use a REST API to validate encoding before submission, and ensure phone numbers are in international format (E.164) to avoid routing errors.

The judgment call: transliterate incidental punctuation to save cost, but never transliterate meaning. A straight quote for a curly quote is free; stripping the accent from someone's name to save a segment is a bad trade that users notice.

Applying it

SMSRoute is a no-KYC SMS API with crypto billing (BTC, ETH, USDT, XMR, LTC, and SOL) that bills by segment like every SMS API: so the encoding math is your cost math. The practical takeaways from the bytes: 160 and 70 are 1,120 bits divided by 7 and 16; one non-GSM character flips the whole message because a payload uses one encoding; extension characters cost double; and non-Latin markets are always UCS-2. Sanitize incidental Unicode to stay in GSM-7 where meaning allows, accept UCS-2 where it doesn't, and always compute the real segment count before sending. Understand the encoding at this level and you stop being surprised by your segment bills: you write copy that costs what you expect. SMSRoute's published route pages list delivery from $0.004/message (premium direct-carrier corridors up to $0.035) with sub-100ms median submission and ~98.6% delivered success (smsroute.cc route pages, 2026). For high-throughput A2P traffic, consider using long codes or short codes depending on your use case and regulatory requirements (e.g., 10DLC in the US). When sending OTPs or 2FA codes, ensure your sender ID is registered and compliant with local regulations like TRAI's DLT guidelines in India.

FAQ

Why is the SMS limit 160 characters for GSM-7 but 70 for Unicode?
Because an SMS payload is a fixed 1,120 bits, and each encoding uses a different number of bits per character. GSM-7 uses 7 bits (1,120 ÷ 7 = 160), while UCS-2 Unicode uses 16 bits to cover all scripts and emoji (1,120 ÷ 16 = 70). The limits are arithmetic on a fixed payload, not arbitrary conventions.
Why does one special character change my whole SMS encoding?
Because a message uses a single encoding for all its characters — it can't mix 7-bit GSM-7 and 16-bit UCS-2 in one payload. The moment any character falls outside the GSM-7 set (a curly quote, an emoji, an accent), the entire message must switch to UCS-2, so every character now costs 16 bits and the limit drops from 160 to 70.
What is transliteration in SMS, and should I use it?
Replacing non-GSM-7 characters with GSM-7 approximations (é→e, curly quote→straight quote) to keep a message in the cheaper 160-character encoding. Use it for incidental punctuation from copy-pasted text — pure savings. Don't use it on meaningful characters like names, and never on non-Latin scripts, where it would corrupt or destroy the content.
Are non-Latin language SMS always Unicode?
Yes. Arabic, Chinese, Cyrillic, Hindi, Thai and other non-Latin scripts fall entirely outside the GSM-7 character set, so messages in those languages always use UCS-2 encoding with a 70-character-per-segment limit. Transliterating them to Latin isn't a real option, so plan copy around the 70-character limit for those markets.

Send your first SMS in 5 minutes

No KYC. Pay with BTC, ETH, USDT, XMR, LTC, and SOL. Live routes to 149 countries.

Get an API key →