Five strings, one minute. They catch the mistakes we have found in real counters, our own included.
X does not count characters. It assigns each character a weight and allows 280 weight per post — and three rules have caught most of the counters we have checked, ours among them. A complete emoji is one unit at weight 2, however many code points it is built from. Every URL costs a flat 23, including a bare domain with no https:// in front of it. And a link stops before the full stop that ends the sentence, so the stop is charged as well.
Miss the first and you over-count, and refuse posts X would have accepted. Miss the second or the third and you under-count, and promise that a post fits which X then rejects on send. Under-counting is the expensive direction.
Copy each string with its button, paste it into the counter you are checking, on its own, and put the answer in the box. Use the button, or click the string once to select exactly it: a stray space or line break picked up by hand is counted too. Nothing is sent anywhere — the comparison happens in your browser.
| Copy this | Why it is hard | X charges | Yours | |
|---|---|---|---|---|
| 👨👩👧👦 |
Four people joined by three ZWJs — seven code points, one emoji, 2. Per code point it is (200*4 + 100*3)/100 = 11. If you get 11, you fell through to the per-code-point path. If you get 7, you counted code points and forgot the weights. If you get 11 in Python, `len()` gave you 7 and something else went wrong too. | 2 | ||
| example.com |
THE ONE THAT UNDERCOUNTS. No scheme, no www., still a link, still 23. A matcher requiring http(s):// or www. reports 11 and tells the author a post fits that the API will reject. | 23 | ||
| こんにちは |
Five kana at U+3053 upward, all above 0x10FF, so 2 each. | 10 | ||
| 🇨🇴 |
Two regional indicators forming one flag: 2, not 4. A per-code-point counter reports 4. | 2 | ||
| Read more: https://example.com/post. |
"Read more: " is 11, the link is a flat 23, and the full stop is 1: 35. A path may not END on a full stop, so the stop belongs to the sentence. A matcher that runs to the next space takes it into the link and reports 34: one short, whenever a sentence ends with a link. | 35 |
Enter what your counter reports to see how it does.
Six counters, ours among them, measured 3 September 2026 on the first four cases. Three got all four right. Three did not.
Three of the six charged 11 for a bare domain where X charges 23 — the under-counting direction, the one that gets a post rejected. One was not doing weighted counting at all: it reported 5 for five Japanese characters, where X charges 10.
Ours was one of the three that passed, and it was still wrong. Until October 2026 it took the full stop into the link in the fifth case and reported 34. That case was added to this page when we found it; the other five counters have not been measured on it.
The tools are not named here, and that is deliberate. A comparison table published by one of the tools in it is marketing, whatever the method was — and the point of this page is to be the thing you can check us with, not a thing you have to trust. Run the five cases on whatever you use. If it passes, good; nothing here asks you to switch.
Every counter we measured claims to be accurate, and not one of them publishes anything you can test it against. That includes the ones that were right. Correct-and-unverifiable looks identical, from the outside, to wrong-and-confident.
So ours is published: 74 conformance vectors with the arithmetic written out for each, as JSON with CORS enabled so your test suite can fetch it directly. MIT licensed.
const { vectors } = await (await fetch(
'https://postsiete.com/tools/x-vectors.json'
)).json();
for (const v of vectors) {
const got = yourWeightedLength(v.text);
if (got !== v.expected) console.log(v.name, 'expected', v.expected, 'got', got);
}
We publish them because we were wrong, twice. Our counter charged 11 for a joined emoji until August 2026, and the test that should have caught it compared our browser copy against our server copy and passed every run — because both copies were wrong in the same way. Two implementations agreeing with each other is evidence of consistency, not of correctness.
The second time it was the vectors. The first 49 were written by the person who wrote the counter, the counter passed all of them, and it was wrong about where a link ends. What caught that was twitter-text, the library X publishes, which our counter is now a port of. So passing these five is a good sign and not a proof: the full set is the thorough test, and what X accepts is the authority.
emojiParsingEnabled — the weighted length “considers all emoji as a single code point … including longer grapheme clusters combined by zero-width joiners.”