How Characters Are Counted: Emoji, URLs, Encodings 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.
| What you see | Graphemes | UTF-16 code units | UTF-8 bytes |
|---|---|---|---|
| The letter a | 1 | 1 | 1 |
| An accented letter such as e-acute | 1 | 1 or 2 | 2 or 3 |
| A single emoji (U+1F9D1) | 1 | 2 | 4 |
| A joined emoji sequence | 1 | 5 or more | 11 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.
| Encoding | Bits per character | Single message | Per segment when split |
|---|---|---|---|
| GSM-7 | 7 | 160 | 153 |
| UCS-2 (Unicode) | 16 | 70 | 67 |
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.
| Platform | Every link counts as | Effect |
|---|---|---|
| X | 23 characters | Shortening a link yourself saves nothing |
| Bluesky | 22 characters | Images 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.
| Content | Bytes each | Fits in 255 bytes |
|---|---|---|
| Unaccented Latin letters and digits | 1 | 255 |
| Accented Latin, Greek, Cyrillic | 2 | 127 |
| Most CJK characters | 3 | 85 |
| Emoji and other supplementary characters | 4 | 63 |
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.
| Platform | Limit | Counted as |
|---|---|---|
| X, free account | 280 | Links flat 23 |
| X, Premium | 25,000 | Links flat 23 |
| Bluesky | 300 | Graphemes; links flat 22 |
| Threads | 500 | Characters |
| Mastodon, default server | 500 | Characters; configurable per server |
| Instagram caption | 2,200 | Characters |
| Instagram bio | 150 | Characters |
| LinkedIn post | 3,000 | Characters |
| SMS, GSM-7 | 160 | 7 bits each |
| SMS, Unicode | 70 | 16 bits each |
| Excel cell | 32,767 | Characters |
| Google Sheets cell | 50,000 | Characters |
| Search result title | about 60 | Pixel width, not characters |
| Meta description | about 160 | Pixel 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.
- For a pure character count, repeat a single unaccented letter. Repeating the letter a 255 times is exactly 255 characters, 255 UTF-16 code units and 255 UTF-8 bytes, so all four ways of counting agree and nothing is ambiguous.
- For a byte limit, repeat a four-byte character instead. If the field takes 63 of them and refuses the 64th, it is a 255-byte field rather than a 255-character one.
- For an SMS, send the plain version first, then the same text with one emoji added, and compare the segment count your provider reports.
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.
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.