Segments and pricing

SMS is billed per segment, not per message. Understanding how segments are counted is the difference between a predictable bill and an unpleasant surprise.

The two alphabets

An SMS is encoded one of two ways, decided entirely by the characters in it.

EncodingSingle messagePer segment when split
GSM-7 (standard)160 characters153 characters
UCS-2 (unicode)70 characters67 characters

Concatenated messages lose characters to a header that tells the handset how to reassemble them. That is why 161 characters costs two segments of 153 — not 160 plus 1.

One character can double your bill

A single character outside the GSM-7 alphabet switches the entire message to UCS-2, cutting capacity from 160 characters to 70.

The usual culprits, all invisible at a glance:

CharacterLooks likeActually
’An apostrophe 'Curly quote pasted from a word processor — forces UCS-2
—A hyphen -Em dash — forces UCS-2
…Three dotsEllipsis character — forces UCS-2
EmojiOne characterTwo UCS-2 units, and forces UCS-2

A 100-character message costs 1 segment. Paste one curly apostrophe into it and it costs 2 — the same text, twice the price.

Segments are the unit of billing. One SMS credit is one segment, not one message — so a two-segment message to 500 people costs 1,000 credits. See Wallet.

Characters that are safe

Accented Latin characters common in French and Spanish are in the GSM-7 alphabet and cost nothing extra: é è ù ì ò à ä ö ñ ü Å Ø Æ ß É. Only genuinely non-GSM characters force UCS-2.

Characters that cost two

These are in GSM-7 but take two units each: { } [ ] ~ ^ \ | €. A message of 81 opening braces is 162 units — two segments, despite being 81 characters.

Check before you send

Rather than implementing this yourself, ask. The quote endpoint runs the same code the billing path runs, so it cannot disagree with the charge.

Shell
curl -X POST https://api.jkasms.com/api/v1/sms/quote \
  -H "Authorization: Bearer $JKASMS_SECRET_KEY" \
  -H "X-JKASMS-Org-Id: $JKASMS_ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Your order is on the way \u2019",
    "recipients": ["0241234567"]
  }'
Response
{
  "success": true,
  "data": {
    "recipients": 1,
    "segments_per_message": 2,
    "total_segments": 2,
    "encoding": "UCS2",
    "total_cost": "0.12",
    "credits_required": 2,
    "sufficient_balance": true
  }
}

encoding: "UCS2" on a short message is the signal that something non-standard slipped in.

When you are charged

You are charged when an upstream network accepts the message — status SUBMITTED — because that is when we are charged. Not when the handset receives it.

Final statusCharged?Why
DELIVEREDYesDelivered as intended
FAILEDYesA real delivery attempt was made and paid for — e.g. the handset was off for 48 hours
REJECTEDNo — refundedThe network refused it outright (invalid or blocked number)
EXPIREDYesAccepted by a network, but no final receipt within 48 hours. Acceptance is the billing moment, and a missing receipt says more about the network's reporting than about the handset.

Refunds appear in your wallet transactions as a REFUND entry against the original message.