> Source: https://www.trackmedia.app/blog/bluesky-character-limit · Last updated: 2026-10-06

# Bluesky character limit: 300 per post

The Bluesky character limit is 300 graphemes per post, plus a 3,000-byte cap. See how emoji, links, images and alt text count, and what TrackMedia checks.

*Guides · Published 2026-10-06*

The Bluesky character limit is **300 graphemes per post**. A grapheme is roughly one visible character, so a letter, a digit or a whole emoji each count as 1, even when the emoji is built from several pieces.

The post record also has a second, separate cap of 3,000 bytes. Both numbers come from the `app.bsky.feed.post` lexicon, the schema Bluesky publishes for a post. This page explains how the two limits differ, what links and images do to the count, and how TrackMedia handles them.

## Bluesky character limit at a glance

As of October 2026, these are the figures in Bluesky's published lexicons (the schema files in the atproto repository on GitHub).

| What | Limit | Where it comes from |
| --- | --- | --- |
| Post text | 300 graphemes | `maxGraphemes` on the post's `text` field |
| Post text, in bytes | 3,000 UTF-8 bytes | `maxLength` on the same field |
| Images per post | 4 | The images embed lexicon |
| One image file | 2,000,000 bytes | The images embed lexicon notes the old limit was 1 MB |
| Display name | 64 graphemes | The profile lexicon |
| Bio (description) | 256 graphemes | The profile lexicon |

Sources: the [post lexicon](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/post.json), the [images embed lexicon](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/embed/images.json) and the [profile lexicon](https://raw.githubusercontent.com/bluesky-social/atproto/main/lexicons/app/bsky/actor/profile.json). Replies and quote posts are the same record type as any other post, so the same text limits apply.

## Why does Bluesky count graphemes and bytes?

The AT Protocol [Lexicon specification](https://atproto.com/specs/lexicon) defines the two terms. `maxGraphemes` counts Unicode grapheme clusters, which loosely match distinct visual characters, including emoji made of several code points and many bytes. `maxLength` on a string counts UTF-8 bytes, and the spec warns that JavaScript stores strings as UTF-16, so you must re-encode the text to count bytes accurately.

Bluesky's [Creating a post](https://docs.bsky.app/docs/tutorials/creating-a-post) guide shows the gap. It runs a family emoji through the SDK's RichText helper and reports a length of 25 and a grapheme length of 1. The figure below adds the JavaScript view of the same emoji, which we computed ourselves: 11 code units.

![A table showing one family emoji counted three ways: 1 grapheme, which is the 300 limit; 11 UTF-16 code units, which has no Bluesky limit; and 25 UTF-8 bytes, which is checked against the 3,000 byte cap](https://www.trackmedia.app/assets/img/blog/bluesky-character-limit-fig1.svg)

*One family emoji is 1 grapheme, 11 UTF-16 code units and 25 UTF-8 bytes. Bluesky limits the first and the last.*

In practice the 300-grapheme limit is the one you hit. Plain English text uses 1 byte per character, so a 300-character post is 300 bytes. Two examples we computed: the Japanese word "konnichiwa" in kana (5 graphemes) is 15 bytes, and a single smiley is 4 bytes.

## When does the 3,000-byte cap matter?

Only when your characters are unusually heavy. A full 300-grapheme post would need to average more than 10 bytes per character to hit it. Long emoji sequences are the usual cause: at 25 bytes each, 120 family emoji already reach 3,000 bytes, while still being only 120 graphemes.

A post of 121 of them is 121 graphemes, well under 300, but 3,025 bytes, over the byte cap. If you write normal sentences, you will never see this. If you build posts from emoji strings, count bytes as well as graphemes.

## Do links, mentions and hashtags count toward the limit?

They count as the text you write, because the limit is on the `text` string. Bluesky's [rich text guide](https://docs.bsky.app/docs/advanced-guides/post-richtext) explains that links, mentions and hashtags are "facets," annotations that point at a range of bytes in the text. A facet marks a piece of the text as clickable; it does not replace it.

Nothing in the lexicon or the two guides gives a link a fixed count the way X does. The facet lexicon does say that the displayed text of a link may be simplified or truncated while the facet itself keeps the complete URL, so a client can show a shortened link and still point at the full address. We did not verify what the official Bluesky app does with long links, so check your own post before you rely on a number.

Two details from the same pages are worth knowing. A mention facet carries the account's DID, a permanent identifier, rather than its handle, so a client has to look the handle up first. A hashtag facet's tag should be stored without the # sign, with a limit of 64 graphemes.

## How many images, how large, and what about alt text?

A post can carry up to four images, and each image takes its own alt text. The images lexicon marks `alt` as required and sets no length limit on it in the version we read. Images do not use any of the 300 graphemes.

Bluesky's pages disagree on image size, so we attribute each. The images lexicon says a file may be up to 2 MB (it was 1 MB before), with a `maxSize` of 2,000,000. The [Creating a post](https://docs.bsky.app/docs/tutorials/creating-a-post) guide says 2 megabytes. The [Posts in-depth](https://docs.bsky.app/docs/advanced-guides/posts) guide still says 1,000,000 bytes in its prose, while its own sample code checks against 2,000,000. Treat the lexicon as current.

A post takes one embed, chosen from images, video, a website card and a few other types. The video lexicon says an mp4 may be up to 300 MB (it was 100 MB before), and it sets no length limit on the video's alt text.

## What if you need more than 300?

Write a thread. Bluesky's [Creating a post](https://docs.bsky.app/docs/tutorials/creating-a-post) guide says reply posts need to reference both the immediate parent post and the original root post of the thread. A reply uses the same post record as any other post, so each reply has its own 300-grapheme allowance.

## How does TrackMedia handle the Bluesky limit?

TrackMedia's Bluesky connector counts graphemes, the same unit the lexicon uses. When Bluesky is one of your selected networks, the [composer](https://www.trackmedia.app/#publish) shows a live counter for it, and the counter's tooltip reads "counted as the platform counts them." A post over 300 is flagged before it goes out, with how many characters it is over.

Here is what the connector does when it publishes, taken from its code:

- **Links, hashtags and mentions** are detected and turned into facets, with offsets measured in UTF-8 bytes as the lexicon requires. A mention is made clickable only if its handle resolves; otherwise it stays plain text and the post still publishes.
- **Up to four images**, with alt text sent when you have written it.
- **Tracked links** are checked at their real length. A short link can be longer than the URL it replaces, so preflight counts each tracked link at the longer of the two.
- **Threads** are published as a chain of replies, with media on the first post only.

There are limits you should know about. The counter checks the 300-grapheme limit and does not check the 3,000-byte cap, so an extreme emoji string could pass the counter and still be too large. The connector makes links clickable but does not attach a website card. The crosspost step that splits overflow into a thread measures text in code units (JavaScript string length), which is stricter than graphemes for emoji-heavy text.

If you publish the same text elsewhere, a post near 300 graphemes can be over X's limit. See [the Twitter character limit](https://www.trackmedia.app/blog/twitter-character-limit) for how X counts, and [how to schedule and edit posts on X](https://www.trackmedia.app/blog/how-to-schedule-and-edit-posts-on-x). In TrackMedia you can write a shorter version for X only, as covered in [how to manage multiple social media accounts](https://www.trackmedia.app/blog/how-to-manage-multiple-social-media-accounts). Crosspost rules can also split overflow into a thread where the destination supports one, using the "When the text is too long" setting, though rules start switched off. TrackMedia can also watch a Bluesky account as a crosspost source.

When you are ready to write once and check every network's limit as you type, [start for free with TrackMedia](https://go.trackmedia.app/signup).

## How we checked this

We opened each page below in a browser on October 6, 2026 (the docs.bsky.app pages redirect to bsky.network, and the lexicon files were read as raw JSON on raw.githubusercontent.com). TrackMedia statements come from reading the connector and composer code, not from testing on a live Bluesky account.

- [app.bsky.feed.post lexicon](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/post.json): 300 graphemes, 3,000 bytes, the embed types.
- [app.bsky.embed.images lexicon](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/embed/images.json): four images, 2,000,000 bytes, required alt text.
- [app.bsky.embed.video lexicon](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/embed/video.json): mp4 up to 300 MB, alt text.
- [app.bsky.richtext.facet lexicon](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/richtext/facet.json): link, mention and tag facets, byte offsets.
- [app.bsky.actor.profile lexicon](https://raw.githubusercontent.com/bluesky-social/atproto/main/lexicons/app/bsky/actor/profile.json): display name and bio limits.
- [AT Protocol Lexicon specification](https://atproto.com/specs/lexicon): definitions of maxLength and maxGraphemes.
- Bluesky developer docs: [Creating a post](https://docs.bsky.app/docs/tutorials/creating-a-post), [Posts in-depth](https://docs.bsky.app/docs/advanced-guides/posts) and [Links, mentions, and rich text](https://docs.bsky.app/docs/advanced-guides/post-richtext).

What we could not verify: we searched the Bluesky Help Center for "character limit" and found no page that states the post limit (the two results were about handles and two-factor codes), and none of the developer guides we read states 300, so that figure rests on the lexicon alone. We did not check how the Bluesky app displays long links, how a server responds to a post over the byte cap, or the definition of the gallery embed type that the post lexicon now lists. The 11 code-unit and byte counts for the emoji were computed by us.
