Bluesky Character Limit: Every Limit, and What People Actually Use
The Bluesky character limit is 300 characters per post — but the post record enforces two ceilings: 300 graphemes and 3,000 bytes, whichever you hit first. Your bio gets 256, your display name 64, a DM gets 1,000, and a hashtag gets 64. And the limit almost never binds in practice: across 2,135 real posts I sampled in July 2026, only 1.6% used all 300, and more than half came in under 140.
The "300" part is well covered elsewhere. What isn't: how links are counted (nearly every page that ranks for this gets it wrong, in five mutually contradictory ways), and the dozen-plus other text limits nobody lists. So here's all of it, read from the AT Protocol lexicons and the Bluesky app's own source rather than from other blog posts — plus what people do with the room they're given.
What Is the Bluesky Character Limit for Posts?
300 graphemes, or 3,000 bytes, whichever runs out first. Both numbers sit in the app.bsky.feed.post lexicon, which defines what a valid Bluesky post is:
"text": { "type": "string", "maxLength": 3000, "maxGraphemes": 300 }
In AT Protocol, maxLength is measured in UTF-8 bytes and maxGraphemes in graphemes — a grapheme being what you'd point at and call one character. The official app enforces the grapheme half: MAX_GRAPHEME_LENGTH = 300 in the app's constants file.
Why two numbers? Because a grapheme can be enormous. The family emoji 👨👩👧👦 is one grapheme — it counts as 1 against your 300 — but it's seven code points joined by zero-width joiners, and 25 bytes in UTF-8. Three hundred of those would be 7,500 bytes, comfortably over the byte cap. Same story for Devanagari conjuncts: क्षि is one visible cluster and 12 bytes, so 300 of them is 3,600 bytes.
For anything written in a Latin alphabet the 300 is the only limit you'll ever meet. The 3,000-byte ceiling is a backstop against very heavy Unicode, and it's the reason the "limit" is two numbers instead of one.
Worth noting what this isn't: Japanese, Chinese, Korean, and emoji all cost 1 grapheme and 3-4 bytes here. On X those characters are weighted double — its twitter-text config gives Latin ranges a weight of 100 and everything else the default 200 — so on the free tier a Japanese post gets 140 to English's 280. Bluesky applies no weighting — 300 Japanese characters is a legal post, and carries far more information than 300 English ones. I'd never seen this pointed out as an advantage anywhere, and it's a real one: on the character-limit axis alone, Bluesky is a meaningfully more generous place to write in CJK. (One row in a longer ledger — I compare the two networks on users, growth, and feeds in Bluesky vs Twitter.)
Do Links, Spaces, and Emoji Count Toward the 300?
Spaces and emoji: yes, one apiece. Links: yes, but usually at a shortened length — and this is where the advice you'll find is not just wrong but wrong in five different directions.
I checked the pages ranking for this question. Two say a link costs exactly 22 characters; one says links don't count at all because they render as cards; two say Bluesky does no shortening whatsoever and URLs cost their full length. The 22-or-23 figure is the t.co rule borrowed from X, and Bluesky has nothing like it.
The answer has two layers, and conflating them is what produces the mess. At the protocol level there is no special treatment — as Bluesky protocol engineer Bryan Newbold put it when someone asked this exact question in February 2025, links "are not counted in any special way: all characters in the text itself are treated equality" (his typo, and he goes on): "it is possible to use the text faceting system to truncate the URL are included in the post text (to reduce character count), but still include the full URL as an annotation, so it can be clicked. this is what the Bluesky Social app does."
So the shortening is an app behaviour, not a protocol one — and you can read it. From rich-text-manip.ts, the composer runs shortenLinks() over your post, replacing each URL in the text with a display form while the rich-text facet keeps the full address — the lexicon says so outright: "The text URL may have been simplified or truncated, but the facet reference should be a complete URL."
The rule itself is three lines long. If the path (plus any query string and fragment) runs longer than 15 characters, keep the host, keep the first 13 characters of the path, and add an ellipsis. If it's 15 or shorter, the whole thing stays as-is. So a link costs at most the length of your domain plus 16 — variable, not fixed:
| The link you paste | Characters it costs |
|---|---|
https://bsky.app (16 chars) |
8 |
https://bsky.app/about (22 chars) |
14 — short path, kept whole |
https://docs.bsky.app/docs/advanced-guides/rate-limits (54 chars) |
29 |
https://useagentsky.com/blog/bluesky-character-limit (52 chars) |
31 |
And the composer counts the shortened version, not what you pasted — the character counter you watch while typing is fed by shortenedGraphemeLength, so in the official app a long URL never even looks expensive. Worth knowing if you draft in a third-party client: some count the raw text, and will tell you you're over when Bluesky would have accepted the post. Long URLs are much cheaper here than the tweet-derived advice suggests; short domains are cheaper still.
(If you want a sense of how much trouble this two-layer arrangement causes, the facet lexicon carries a warning aimed squarely at anyone who's already got it wrong: indices count UTF-8 bytes, and "some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets." That note is doing a lot of quiet emotional labour.)
Everything else counts plainly. Spaces count. Line breaks count. Punctuation counts. An accented é is one grapheme whether it arrives precomposed or as e plus a combining accent. There is no free allowance for anything.
One display quirk that isn't a limit but gets mistaken for one: the app collapses any post longer than 25 lines behind a "Show more" in the feed (MAX_POST_LINES = 25). Your text is all there — it's just folded up until someone taps.
What Are the Other Bluesky Character Limits?
Twenty of them, and the one that catches people out is measured in bytes. Here is the full set, read from the lexicons in bluesky-social/atproto and the constants in bluesky-social/social-app on July 29, 2026. Where a field has both, graphemes is the number you'll feel and bytes is the backstop.
| What you're writing | Limit | Defined in |
|---|---|---|
| Post | 300 graphemes / 3,000 bytes | app.bsky.feed.post |
| Display name | 64 graphemes / 640 bytes | app.bsky.actor.profile |
| Bio (profile description) | 256 graphemes / 2,560 bytes | app.bsky.actor.profile |
| Pronouns | 20 graphemes / 200 bytes | app.bsky.actor.profile (added September 2025; not yet surfaced in the official app) |
| Handle | 253 characters total, max 63 per dot-separated segment | AT Protocol handle spec |
.bsky.social username |
3-18 characters | MAX_SERVICE_HANDLE_LENGTH in the app |
| Hashtag | 64 graphemes / 640 bytes each | app.bsky.richtext.facet |
tags array (hidden tags, not typed inline) |
8 entries | app.bsky.feed.post |
| Image alt text | no cap in the lexicon; 2,000 graphemes in the official app | MAX_ALT_TEXT |
| Video alt text | 1,000 graphemes / 10,000 bytes | app.bsky.embed.video |
| Direct message | 1,000 graphemes / 10,000 bytes | chat.bsky.convo.defs |
| Group chat name | 50 graphemes | MAX_GROUP_NAME_GRAPHEME_LENGTH |
| List name | 64 bytes (no grapheme allowance) | app.bsky.graph.list |
| List description | 300 graphemes / 3,000 bytes | app.bsky.graph.list |
| Starter pack name | 50 graphemes / 500 bytes | app.bsky.graph.starterpack |
| Starter pack description | 300 graphemes / 3,000 bytes | app.bsky.graph.starterpack |
| Feed name | 24 graphemes / 240 bytes | app.bsky.feed.generator |
| Feed description | 300 graphemes / 3,000 bytes | app.bsky.feed.generator |
| Report reason | 2,000 graphemes | MAX_REPORT_REASON_GRAPHEME_LENGTH |
| Post languages | 3 | app.bsky.feed.post |
Four of those deserve a footnote, because they're the ones that surprise people:
- List names are counted in bytes, not graphemes — the only text field here without a grapheme allowance. 64 bytes is 64 Latin letters but only about 21 Japanese characters, so a list name that fits in English may not survive translation. (I can't tell you whether that's a deliberate choice or a field that predates the grapheme convention and never got revisited. It reads like the latter.)
- The "18-character handle limit" you'll see quoted is real but misdescribed. It applies only to the username portion of a free
.bsky.socialhandle (3-18 characters). Bring your own domain and the ceiling is the handle spec's 253 characters, with each dot-separated segment capped at 63. - The 8-tag cap isn't the one you're thinking of. Hashtags you type inline have no count limit — your 300 characters are the only ceiling, which is the version that matters in practice. The 8 applies to a separate
tagsarray on the post record, for tags attached without appearing in the text; the official app doesn't offer a way to set it. - Image alt text has no protocol limit. The images lexicon declares
altwith no cap; the 2,000 you get is the app's own choice, and its source comments that WCAG recommends 100 "but increasing limit per user feedback." Video alt text, oddly, is capped at 1,000 in the lexicon.
If you're chasing the non-text specs — banner dimensions, avatar sizes, safe zones — those live in Bluesky banner size and image dimensions. And the 64-grapheme hashtag cap is the least interesting thing about hashtags — what tags do (and don't do) for reach is its own post.
How Long Is the Average Bluesky Post?
A lot shorter than 300. I ran the numbers rather than guessing, because I couldn't find anyone who had.
For posts: I sampled 2,135 text posts from 264 accounts on July 29, 2026, via the public app.bsky.feed.getAuthorFeed endpoint, counting graphemes in the raw record text and excluding reposts, replies, and image-only posts.
| Post length (graphemes) | Share of sampled posts |
|---|---|
| Median | 117 |
| 140 or fewer (an old-school tweet) | 55.6% |
| 280 or more | 12.2% |
| The full 300 | 1.6% |
The 300-character limit is, for almost everybody, theoretical. More than half of Bluesky posts would have fit on Twitter in 2016.
Bios are the more interesting number, and the sadder one. Across 3,712 accounts sampled the same day through app.bsky.graph.getFollowers:
- 52.2% had no bio at all. Not a short one — none.
- Among accounts that had written one, the median was 118 graphemes of the 256 available.
- Of those bio-writers, only 7.2% came within six characters of the cap.
Method, so you can repeat it: both samples were drawn from the followers of six well-known accounts (bsky.app, nytimes.com, jay.bsky.team, pfrazee.com, safety.bsky.app, mcuban.bsky.social), deduplicated by DID. Every call is unauthenticated and public, so anyone can check it.
Two caveats I'd want if I were reading someone else's numbers. First, getFollowers returns most-recent-first, so this leans toward accounts that recently followed something big — a cross-section of active Bluesky, not a uniform draw of the network. Second, it's a live sample, so it moves: a smaller draw earlier the same day (786 posts) put the median at 90 rather than 117. Treat the median as a band of roughly 90-120. What didn't move between draws is the part I'd actually hang an argument on — the share of posts reaching the ceiling stayed between 1% and 2%, and the share of accounts with no bio stayed near half, every single time.
Will Bluesky Raise the Character Limit?
No sign of it. The most-supported request, atproto issue #2551 — "Increase Post Character limit to 500." — has been open since June 5, 2024 and has collected 79 comments without a resolution. Bluesky did announce a new character limit once, in a post dated April 1, 2025 — by lowering it to 299, for one day only. That post still ranks on page one for this question, which tells you something about the state of the results.
The data above suggests why nobody's in a hurry. If 1.6% of posts reach the ceiling, raising it serves a rounding error, and the intended answer for longer thoughts is already there: threads. A reply to your own post is a native, first-class thing on Bluesky, and 300 characters is a reasonable unit to think in.
Where Does Agent Sky Fit?
The limit worth worrying about isn't 300. It's the 256 that half the network leaves blank.
I can be unusually concrete about this, because Agent Sky refuses to suggest those accounts to anyone. Before any ranking happens, a candidate has to clear a filter, and one of its clauses is user.description in [nil, ""] -> false. Empty bio, empty display name, no avatar, zero posts: rejected, no scoring involved. Those four clauses have been in the file since the first commit in December 2024, and I've never seriously considered removing them.
So when 52.2% of the accounts I sampled have no bio, that isn't a soft disadvantage. They're invisible to my tool — and to every starter-pack curator and follow-back decision made by a human doing the same triage by eye in about half a second. Nobody sat down and decided to exclude them. We independently wrote the same rule, because a blank profile gives you nothing to be right or wrong about.
The fix costs nothing and takes four minutes: write 118 characters about yourself, which is all the median bio-haver managed. Say what you post about and who you'd like to hear from. That's it — the longer version is in how to optimize your Bluesky profile, and it will do more for you than any tool, mine included.
What you'd pay me for is the part after that: finding the accounts worth following, at a pace that doesn't get you flagged, and checking they really followed back before anything gets undone. It's free to start, no credit card, $9/month after.
But if you take one thing from a post about character limits, make it the smallest one. Two hundred and fifty-six characters, and most people spend zero of them.
Quick FAQ
What is the Bluesky character limit? 300 characters per post — technically 300 graphemes, with a second cap of 3,000 bytes. Both are written into the app.bsky.feed.post lexicon as maxGraphemes: 300 and maxLength: 3000. For anything you type in a Latin alphabet the 300 is the only one you'll ever hit; the byte cap only bites with scripts or emoji sequences that cost more than 10 bytes per visible character.
Does the Bluesky character limit include spaces? Yes. Spaces, line breaks, and punctuation all count as one grapheme each, exactly like letters. Bluesky counts graphemes — what a human sees as one character — so an emoji, an accented letter, and a space are all worth 1 apiece. There's no weighting system like X's, where emoji and CJK characters count double.
Do links count toward the Bluesky character limit? Yes, but usually at a shortened length. At the protocol level links get no special treatment, but the official Bluesky app rewrites any link whose path runs over 15 characters down to the host plus the first 13 characters of the path plus an ellipsis, and the composer counts that shortened form against your 300. A 54-character docs URL costs 29. This is not the fixed 22 or 23 characters people quote from X — on Bluesky the cost depends mostly on how long your domain name is.
What is the Bluesky bio character limit? 256 graphemes (2,560 bytes) for your profile description, and 64 graphemes for your display name — both set in the app.bsky.actor.profile lexicon. Almost nobody fills it: in a sample of 3,712 Bluesky accounts taken in July 2026, 52.2% had no bio at all, and among those that did the median was 118 characters, under half the allowance.