SMS Character Counting and Encoding: Why 160 Is Not Always 160
The 160-character SMS limit is one of the most misunderstood constraints in tech. In practice, your message might allow only 70 characters — or it might silently split into multiple segments that cost extra. The difference comes down to character encoding.
The GSM-7 Alphabet
The original SMS standard uses the GSM 7-bit alphabet (GSM-7), which encodes 128 characters in 7 bits each. Because SMS messages travel in 140-byte packets, a 7-bit encoding fits:
140 bytes × 8 bits/byte = 1120 bits
1120 bits ÷ 7 bits/char = 160 characters
GSM-7 covers uppercase and lowercase Latin letters, digits, and common punctuation. However, it is not ASCII — some characters differ:
- Tilde
~is not in GSM-7 (use=>in escape sequences) - Backtick
`is absent - Curly braces
{}are absent - Square brackets
[]are absent - The caret
^, pipe|, and backslash\require two-character escape sequences
When you use any of these escape characters, they consume 14 bits instead of 7 — effectively counting as two characters in your SMS length.
When UCS-2 Takes Over
If your message contains even a single character outside GSM-7 — an emoji, a Chinese character, a Cyrillic letter — the entire message switches to UCS-2 encoding (16 bits per character). UCS-2 uses the same 140-byte packet, but fits fewer characters:
140 bytes × 8 bits/byte = 1120 bits
1120 bits ÷ 16 bits/char = 70 characters
One emoji drops your limit from 160 to 70 characters. This is the most common reason messages split unexpectedly.
Modern SMS gateways use UTF-16 instead of UCS-2, which handles surrogate pairs for characters outside the Basic Multilingual Plane (emoji like 🎉, 🏳️🌈). Characters requiring surrogate pairs consume 32 bits (4 bytes) — counting as two UCS-2 characters each.
Message Segmentation
When your message exceeds the single-message character limit, it is split into segments. Each segment includes a User Data Header (UDH) — 6 bytes of metadata for reassembly — reducing the available space per segment:
| Encoding | Single Message | Per Segment (concatenated) |
|---|---|---|
| GSM-7 | 160 chars | 153 chars |
| UCS-2 | 70 chars | 67 chars |
A 200-character GSM-7 message splits into two segments: 153 + 47 characters. You pay for two messages.
A 75-character UCS-2 message also splits into two segments: 67 + 8 characters. You pay for two messages.
Segment limit calculation:
function calculateSegments(text) {
const isGSM7 = /^[\x00-\x7F\u00A0\u00A3\u00A5\u00A7\u00C0-\u00FF]*$/.test(text)
// Simplified — real GSM-7 detection requires checking the full alphabet
if (isGSM7) {
const singleLimit = 160
const segmentLimit = 153
return text.length <= singleLimit ? 1 : Math.ceil(text.length / segmentLimit)
} else {
const singleLimit = 70
const segmentLimit = 67
// Account for surrogate pairs (emoji = 2 UCS-2 chars)
const ucs2Length = [...text].reduce((count, char) => {
return count + (char.codePointAt(0) > 0xFFFF ? 2 : 1)
}, 0)
return ucs2Length <= singleLimit ? 1 : Math.ceil(ucs2Length / segmentLimit)
}
}
Hidden Cost Traps
Several characters silently inflate your message size:
| Character | GSM-7 Cost | Notes |
|---|---|---|
| Basic letter (A–Z) | 1 char | Normal |
[ ] { } ~ | \^ | 2 chars | Escape sequences |
| Any emoji | Switches to UCS-2 | Entire message affected |
Line break (\n) | 1 char | Counts normally in GSM-7 |
Carriage return (\r) | 1 char | Often paired with \n (2 chars) |
Smart quotes ("") and em dashes (—) also force UCS-2 encoding — they are not in the GSM-7 alphabet. If you copy-paste text from Word or Google Docs, check for these characters.
Practical Tips for SMS Content
- Stick to GSM-7 when possible — use straight quotes, basic punctuation, and no emoji
- Replace smart characters — convert
""to"",—to--, and…to... - Count before sending — use a character counter that accounts for encoding switches
- Shorten URLs — use a link shortener to save characters (a typical URL consumes 30–80 characters)
- Avoid emoji in transactional SMS — they force UCS-2 and often render inconsistently across devices
- Test with your gateway — Twilio, MessageBird, and AWS SNS may handle encoding slightly differently
Key Takeaways
- GSM-7 encoding allows 160 characters per SMS; UCS-2 allows only 70
- A single emoji or special character forces the entire message into UCS-2
- Concatenated messages reserve 6 bytes for the UDH, reducing GSM-7 to 153 and UCS-2 to 67 characters per segment
- Escape characters in GSM-7 (
[,],{,},~,^,|,\) count as two characters each - Smart quotes and em dashes from word processors silently switch encoding — sanitize input before sending
- Use a character counter that detects encoding to avoid unexpected message splits and costs
Related Guides
- Character Counting Guide: Measuring Text Length Accurately
- Word Count Best Practices for Writers and Editors
- Text Length Limits: Character Counts Across Platforms
Try It Yourself
Count your SMS characters accurately with our free Character Counter. Paste your message, see the GSM-7 vs UCS-2 encoding, segment count, and remaining characters — all in real time.