SMS Character Counting and Encoding: Why 160 Is Not Always 160

May 28, 20267 min read

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:

EncodingSingle MessagePer Segment (concatenated)
GSM-7160 chars153 chars
UCS-270 chars67 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:

CharacterGSM-7 CostNotes
Basic letter (A–Z)1 charNormal
[ ] { } ~ | \^2 charsEscape sequences
Any emojiSwitches to UCS-2Entire message affected
Line break (\n)1 charCounts normally in GSM-7
Carriage return (\r)1 charOften 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

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.