Skip to content

Character limits & segments

SMS over GSM has a hard 160-character limit per segment for ASCII content. Longer messages are split into multiple segments, each billed as a separate SMS. Lisoloo computes the segment count at submit time and bills accordingly.

Length (characters)TypeSMS count
1160Standard SMS1
161320Long SMS (2-segment)2
321480Long SMS (3-segment)3
481640Long SMS (4-segment)4
concatenatedceil(len / 153) after the first segment
up to 1600Maximum10

For multi-segment messages the per-segment capacity drops from 160 to 153 characters — the extra 7 characters per segment are consumed by the User Data Header (UDH) that the carrier uses to reassemble the message on the handset.

A message longer than 1 600 characters is rejected with 1110 INVALID_FIELD_FORMAT at submit time.

total_cost = total_recipients × segments_per_message × unit_price

unit_price is your merchant-level price per SMS segment in currency, returned by GET /balance. A 200-character message to 50 recipients at $0.02 per segment costs 50 × 2 × 0.02 = $2.00.

Messages containing characters outside the GSM 03.38 alphabet (any emoji, accented characters in some scripts, CJK, Arabic, …) are sent as UCS-2 (16-bit per character). UCS-2 segments cap at 70 characters single-segment, 67 characters per segment when concatenated. A single 🎉 anywhere in the body flips the entire message to UCS-2.

EncodingSingleConcatenated
GSM 7-bit160153
UCS-2 (any non-GSM char)7067

If you don’t know your character set ahead of time, assume UCS-2 for cost estimation.

POST /send returns the computed segment count for the batch:

{
"data": {
"message_id": "...",
"total_recipients": 50,
"total_messages": 100,
"total_cost": 2.00,
"currency": "USD",
"status": "pending"
}
}
  • total_recipients is len(to).
  • total_messages is total_recipients × segments_per_message.
  • total_cost is total_messages × unit_price, deducted from your balance at submit time.

For scheduled or recurring sends the totals reflect the entire schedule, not per-occurrence.

If you want to estimate cost before calling POST /send, count segments client-side:

def segments(message: str) -> int:
is_gsm = all(c in GSM_03_38 for c in message)
n = len(message)
if is_gsm:
return 1 if n <= 160 else (n + 152) // 153
return 1 if n <= 70 else (n + 66) // 67

The full GSM 03.38 character table is at 3GPP TS 23.038.

  • Sending types — segment count multiplies across scheduled_dates and recurring_schedule
  • Check balance — query unit_price and available_sms
  • Rate limits — segments don’t count against rate-limit; requests do