01 · Definition
What is GSM-7 encoding?
GSM-7 encoding is the default character set for SMS, defined in 3GPP TS 23.038 (originally GSM 03.38). Each character is a 7-bit value, a septet, so the 140 bytes of user data in one SMS hold 160 characters. The basic table has 128 entries: Latin letters and digits, common punctuation, a handful of accented letters such as é, ü, ñ and à, a few Greek capitals and symbols such as @, £, $ and ¥.
A small extension table adds characters reached through an escape code: the form feed, ^, {, }, \, [, ~, ], | and €. Each costs two septets, the escape plus the character, so a message full of brackets hits the limit sooner than its visible length suggests. When a message runs past 160 septets it becomes a Concatenated SMS: every part carries a 6-byte User Data Header telling the phone how to reassemble it, which leaves 153 septets per part, and operators bill each part.
Anything outside those two tables cannot travel as GSM-7. A single curly apostrophe pasted from a word processor, an en dash, the rupee sign, a Devanagari letter or an emoji forces the whole message into UCS-2 Encoding, which uses 16 bits per character: 70 characters in one SMS and 67 per part. That is why the same OTP can be one part in plain English and two or three in Hindi. 3GPP also defines national language shift tables for scripts such as Hindi, Bengali and Tamil, but support across networks and handsets is patchy, so most senders treat Indic text as UCS-2.
Key takeaways
- GSM-7 is the default 7-bit SMS alphabet from 3GPP TS 23.038 (GSM 03.38): 128 basic characters plus a small extension table.
- One GSM-7 SMS holds 160 characters; longer messages split into parts of 153 because each part carries a joining header.
- Extension characters such as € [ ] { } ^ ~ | and the backslash cost two septets each.
- One character outside GSM-7, such as a curly quote, the rupee sign or an emoji, switches the whole message to UCS-2 and 70 characters.
- Over SMPP,
data_coding0 normally means the SMSC default alphabet (GSM-7) and 8 means UCS-2.
02 · Specifications
GSM-7 encoding: key facts and limits
| Standard | 3GPP TS 23.038 (GSM 03.38) |
|---|---|
| Bits per character | 7 (one septet) |
| Single SMS | 160 septets in 140 bytes |
| Per part, concatenated | 153 septets (6-byte UDH per part) |
| Two-septet characters | € [ ] { } \ ^ ~ | and form feed |
| When it can't be used | UCS-2 takes over: 70 per SMS, 67 per part |
| SMPP data_coding | 0 = SMSC default (GSM-7), 8 = UCS-2 |
03 · Code samples
GSM-7 encoding in code
// GSM 03.38 basic table and extension table (escape code omitted).const BASIC = new Set("@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà");const EXTENSION = new Set("\f^{}\\[~]|€"); // 2 septets eachexport function smsParts(text) {const chars = Array.from(text);const isGsm = chars.every((c) => BASIC.has(c) || EXTENSION.has(c));if (!isGsm) {const units = text.length; // UTF-16 code units: emoji count as 2return { encoding: "UCS-2", units, parts: units <= 70 ? 1 : Math.ceil(units / 67) };}const septets = chars.reduce((n, c) => n + (EXTENSION.has(c) ? 2 : 1), 0);return { encoding: "GSM-7", units: septets, parts: septets <= 160 ? 1 : Math.ceil(septets / 153) };}smsParts("482913 is your OTP. Do not share it."); // { encoding: "GSM-7", units: 36, parts: 1 }smsParts("Don’t miss your refill"); // { encoding: "UCS-2", units: 22, parts: 1 } (curly apostrophe)
Close enough for a live counter. Encoders never split an escape pair or an emoji across parts, so long messages can need one part more than this estimate.
04Playground
A working model of GSM-7. Use its controls to replay it step by step.
Type an SMS and watch the parts add up
Encoding
UCS-2
16-bit Unicode · 70 per SMS, 67 per part
Characters
51
51 UTF-16 units
SMS parts
1
up to 70 in one
Left in part
19
before part 2
Billed as 1 SMS
Part 151 / 70
आपका ऑर्डर भेज दिया गया है। डिलीवरी कल शाम तक होगी।
How it splits
- Alternating parts
- Forces UCS-2
Why
“आ”, “प”, “क” and 22 more are not in the GSM-7 alphabet, so the whole message is sent as UCS-2: one SMS holds 70 characters instead of 160.
- आ (U+0906)
- प (U+092A)
- क (U+0915)
- ा (U+093E)
- ऑ (U+0911)
- र (U+0930)
- ् (U+094D)
- ड (U+0921)
- +17 more
Hindi text: UCS-2, 1 SMS part.
05 · Operation
How does GSM-7 encoding work?
Check every character
Look each character up in the GSM 03.38 basic and extension tables. If any character is in neither, the whole message must be sent as UCS-2.
Count septets, not characters
Basic characters cost one septet and extension characters two. Spaces and line breaks count as well.
Apply the single-part limit
Up to 160 septets fit in one SMS. Beyond that, the message is split into parts of up to 153 septets each.
Never split an escape pair
Encoders keep an escape and its character in the same part, so a part can end one septet short of 153.
Declare the encoding on submission
Over SMPP, set
data_coding0 for the SMSC default alphabet (normally GSM-7) or 8 for UCS-2; over an HTTP API, use your provider's unicode flag.
06 · Cautions
Common mistakes with GSM-7 encoding
Pasting from word processors
Curly quotes, en dashes, ellipses and no-break spaces look harmless but aren't in GSM-7. Normalise them to straight quotes, hyphens, three dots and plain spaces before sending.
Counting characters instead of septets
A 158-character message with two euro signs is 160 septets; add one bracket and it becomes two parts. Count with the extension table.
Normalising after DLT registration
In India, cleaning quotes in code can make the sent text differ from the registered DLT template. Register the GSM-7-safe version and send exactly that.
Trusting data_coding 0 everywhere
Value 0 means the SMSC's default alphabet, which some SMSCs treat as Latin-1 or ASCII. Confirm with your provider how GSM-7 should be submitted, and test that @ (septet 0x00) survives.
07 · Worked example: A Chennai pharmacy's refill reminder
Example: A Chennai pharmacy's refill reminder
A Chennai pharmacy chain's refill reminder reads "Your refill is ready. Don’t forget to collect it from the Anna Nagar store before 8 pm today." It is 95 characters, yet it goes as two UCS-2 parts, because the apostrophe in "Don’t" was a curly one pasted from a design document. Swapping it for a straight apostrophe brings the message back to one GSM-7 part, halving the cost of every send. The Tamil version stays UCS-2, so the team shortens it to fit in 70 characters and registers both versions as separate DLT templates.
08 · Rationale
Why does GSM-7 encoding matter?
Encoding decides cost and readability. A message that tips from GSM-7 into UCS-2 can triple its part count, and with it the bill, without anyone changing the wording. In India it also matters for DLT: the text you send must match the registered DLT Content Template, and a smart quote that slips in during an edit both breaks the match and changes the encoding. Developers who normalise text to GSM-7 where possible, and marketers who know which characters are expensive, keep OTPs and alerts to a single part.
09 · Questions and answers
GSM-7 FAQs
- Why does my SMS only allow 70 characters?
- Because it contains at least one character outside GSM-7, so it is sent as UCS-2. Typical culprits are curly quotes, the rupee sign, emoji and non-Latin scripts; replace them with GSM-7 equivalents to get 160 characters back.
- Is the rupee symbol in the GSM-7 character set?
- No. The rupee sign is not in GSM 03.38, so a single one switches the message to UCS-2. Write Rs. or INR instead when you need to stay within 160 characters.
- Why are long SMS split into 153 characters, not 160?
- Each part of a concatenated SMS carries a 6-byte User Data Header that tells the phone how to join the parts. That header takes 7 of the 160 septets, leaving 153 for text in every part.
- Do spaces and line breaks count in GSM-7?
- Yes. Spaces, line feeds and carriage returns are GSM-7 basic characters costing one septet each, so a template with extra blank lines can tip into a second part.
- What is the difference between GSM-7 and UCS-2?
- GSM-7 packs a limited Latin-based alphabet into 7 bits per character, 160 per SMS. UCS-2 uses 16 bits per character and covers almost every script, but fits only 70 per SMS, and any non-GSM character switches the whole message to it.
10 · Recommended practice
Best practices for GSM-7 encoding
- Show a live part counter in your template editor.
- Keep OTP text in plain GSM-7 English when cost matters, and use WhatsApp for rich regional-language content.
- Avoid the euro sign and brackets in short alerts; each costs double.
- Test with the longest realistic variable values, not the sample ones.