How Characters Are Counted: Emoji, URLs, Encodings and Bytes

How characters are counted — Code units, encodings, URLs and bytes

A string that measures 40 characters in one place can measure 80 in another, and neither counter is wrong. The word "character" means four different things depending on what is doing the counting: a UTF-16 code unit, a grapheme a reader would call one symbol, a byte of storage, or a fixed cost that a platform assigns regardless of real length. Almost every count that disagrees with a limit comes down to one of those four.

The rules below are stable. The limits at the end are not — platforms change those, so they carry a date.

Rule 1: an emoji is rarely one character

Most programming languages count UTF-16 code units, not symbols. Characters outside the Basic Multilingual Plane, which includes nearly every emoji, are stored as a surrogate pair — two code units for one visible symbol. The code point U+1F9D1 is stored as the pair D83E DDD1, so a counter that measures code units returns 2 for something a reader sees as one.

It gets further from intuition with combined emoji. A family or a flag or a profession is often several code points welded together with zero-width joiners, and each part carries its own cost. A single visible glyph can measure seven or more.

One visible symbol, several counts
What you seeGraphemesUTF-16 code unitsUTF-8 bytes
The letter a111
An accented letter such as e-acute11 or 22 or 3
A single emoji (U+1F9D1)124
A joined emoji sequence15 or more11 or more

The accented row has two answers because the same letter can be stored either as one precomposed code point or as a plain letter followed by a combining accent. They look identical and count differently.

Rule 2: one wrong character halves an SMS

This is the sharpest example of the whole problem. An SMS payload is a fixed 1,120 bits. GSM-7 spends 7 bits per character, which is where the famous 160 comes from. Unicode messages use UCS-2 at 16 bits per character, which gives 70.

The part that surprises people is that the encoding is all or nothing. A message cannot mix the two. The moment one character falls outside the GSM-7 set — an emoji, an accent, a curly quote, an em dash — the entire message re-encodes as UCS-2 and every character in it starts costing 16 bits.

SMS capacity by encoding
EncodingBits per characterSingle messagePer segment when split
GSM-77160153
UCS-2 (Unicode)167067

So a 158-character message sends as one SMS, and adding a single emoji to it does not make it 159 characters — it makes it a three-segment Unicode message. Segments are billed individually, which is why one curly apostrophe pasted in from a word processor can triple the cost of a campaign.

Rule 3: links cost a fixed amount, not their real length

Some platforms rewrite URLs and charge a flat rate for them, so the visible length of a link has nothing to do with what it costs you.

Fixed URL costs
PlatformEvery link counts asEffect
X23 charactersShortening a link yourself saves nothing
Bluesky22 charactersImages add nothing to the count

The practical consequence is counter-intuitive: running a 300-character URL through a shortener before posting to X gains you nothing at all, because the platform was already going to charge 23 for it either way.

Rule 4: databases count bytes, not characters

A column declared as holding 255 of something usually means 255 bytes, and under UTF-8 a character costs between one and four of them. Latin text is one byte per character, so 255 characters fit and the distinction never comes up. Then a name with an accent, or a message with an emoji, arrives and the field fills up early.

UTF-8 cost per character
ContentBytes eachFits in 255 bytes
Unaccented Latin letters and digits1255
Accented Latin, Greek, Cyrillic2127
Most CJK characters385
Emoji and other supplementary characters463

This is the failure that looks like a bug in your own form: the field accepts text shorter than its stated limit and rejects it silently or truncates mid-character. Testing for it is a matter of sending known lengths at the boundary, which is the method in testing character limits with repeated text.

The limits themselves

Verified 9 August 2026. Platforms change these without notice, so treat the number as a starting point and confirm anything you are building against.

Post and field limits by platform, August 2026
PlatformLimitCounted as
X, free account280Links flat 23
X, Premium25,000Links flat 23
Bluesky300Graphemes; links flat 22
Threads500Characters
Mastodon, default server500Characters; configurable per server
Instagram caption2,200Characters
Instagram bio150Characters
LinkedIn post3,000Characters
SMS, GSM-71607 bits each
SMS, Unicode7016 bits each
Excel cell32,767Characters
Google Sheets cell50,000Characters
Search result titleabout 60Pixel width, not characters
Meta descriptionabout 160Pixel width, not characters

Two of those deserve a note. TikTok's caption limit has been reported as rising from 2,200 to 4,000, and sources disagree, so it is left out rather than guessed at. And the last two rows are not character limits at all — Google truncates on rendered width, so a title of capital letters is cut sooner than the same count of narrow ones.

The spreadsheet rows connect to a further quirk: Excel caps a cell at 32,767 characters but its REPT function stops there too, while Google Sheets allows 50,000 in a cell and stops REPT at 32,000 — covered in the REPT function guide.

How to measure a length exactly

Every rule above is a reason not to trust a rough estimate. The way to know is to generate a string of known length and watch what the target does with it.

If the counting is happening inside a program rather than in a form, the language-by-language expressions for building these strings are in how to repeat a string N times in code.

Need a string of an exact length? Repeat any character the number of times you need and read the live character count before you paste it anywhere.

Open the text repeater

Frequently asked questions

Why does my text count as more characters than I typed?

Most counters measure UTF-16 code units rather than visible symbols. Emoji and other characters outside the Basic Multilingual Plane are stored as surrogate pairs, so each one counts as 2, and emoji built from joined sequences can count as 5 or more.

Why does one emoji drop an SMS from 160 characters to 70?

An SMS payload is a fixed 1,120 bits. GSM-7 encoding spends 7 bits per character, giving 160, while Unicode UCS-2 spends 16, giving 70. A message cannot mix encodings, so a single character outside the GSM-7 set re-encodes the whole message and every character in it costs 16 bits.

Does shortening a URL save characters on X?

No. X rewrites every link through its own shortener and counts it as exactly 23 characters regardless of the original length, so shortening it yourself first gains nothing. Bluesky applies the same idea at 22 characters per link.

Is a 255 character database field 255 characters or 255 bytes?

Usually 255 bytes. Under UTF-8 a character costs 1 to 4 bytes, so a 255-byte field holds 255 unaccented Latin characters but only about 127 accented ones, 85 CJK characters, or 63 emoji. This is why a field can reject text that looks shorter than its stated limit.

How many bytes does an emoji use?

Four bytes in UTF-8 for a single emoji code point, and more for sequences joined with zero-width joiners. In UTF-16 the same emoji is 2 code units, which is why a byte limit and a character limit disagree on the same text.

How do I create a string of an exact character length?

Repeat a single unaccented letter, because that is the one case where graphemes, UTF-16 code units and UTF-8 bytes all give the same number. The letter a repeated 255 times is exactly 255 by every measure, which removes encoding from the test.

← All articles