How to Schedule Posts on Bluesky: 300 Graphemes, No Native Scheduler, Real Options
Short answer: Bluesky itself cannot schedule a post, not in the app, not in the protocol. Every tool that says it schedules Bluesky posts is doing the same thing under the hood: holding your draft somewhere and firing it off at the right minute using an authenticated session. There is no scheduledAt field to hand the server and walk away.
That is not a knock on Bluesky. It is a young, deliberately minimal protocol, and scheduling is an application-layer problem, not a protocol one. But it means the mechanics matter more here than on platforms where a scheduler just flips a status field. Here is exactly how to schedule posts on Bluesky, what to hand a third-party tool, and the limits that actually apply in 2026.
What you need
- A Bluesky account and handle.
- Either an app password or an AT Protocol OAuth session, depending on how you connect.
- A client that speaks the AT Protocol, either the official
@atproto/apiSDK or raw XRPC calls if you are rolling your own. - A plan for the fact that nothing about scheduling is native. You, or a tool you trust, has to be the thing that fires the post.
Why there's no native scheduling (the lexicon has no field for it)
Every Bluesky post is a record in the app.bsky.feed.post lexicon. Strip it down and the fields are text, createdAt, an optional embed (image, external link, quote), optional reply references, language tags, and facets for mentions, links, and hashtags. Scan the whole schema and you will not find anything like scheduledAt or publishAt.
That absence is the whole story. When you call com.atproto.repo.createRecord to publish, the post goes live immediately. Bluesky's own app does not have a "schedule for later" button anywhere in its settings, because there is nothing in the protocol for it to call. Any product that schedules Bluesky posts, including TimeToPost's calendar workflow described below, is necessarily a client sitting between you and that createRecord call, deciding when to fire it.
Worth saying plainly: this is a protocol-level gap, not a missing feature Bluesky forgot to ship. Scheduling requires something to stay online and hold state past the moment you hit "post," and a decentralized protocol built around independent servers (PDSes) does not assume any particular client is always running. The scheduling has to live in an app, not the network.
App passwords vs AT Protocol OAuth (what to give a tool)
You have two ways to let a third party post on your behalf, and they are not equivalent.
App passwords are the older, simpler route. Generate one from Settings > App Passwords in the Bluesky app. It is a separate credential from your main password, scoped to that one app, and you can revoke it individually without touching your login. Under the hood, a client uses it to call com.atproto.server.createSession and gets back access and refresh tokens. It still works, and plenty of tools use it today.
AT Protocol OAuth is the direction Bluesky's own guidance now points third-party developers toward. It is a proper OAuth flow scoped to the AT Protocol, with per-app authorization, refresh tokens issued through a standard grant flow, and no shared secret sitting in a settings page. It is more setup for a developer to implement, but it gives you tighter control over what an app can and cannot do with your account, and it is where the ecosystem is heading.
Practical rule for you: if a tool only asks for an app password, that is fine, revoke it the day you stop using the tool. If a tool offers OAuth, prefer it. Either way, never hand out your actual account password. There is no legitimate reason any Bluesky scheduler needs it.
The hold-and-fire model schedulers use
Since the protocol will not hold your post for you, a scheduler has to do four things itself:
- Store the draft. Text, images, alt text, reply target, all held in the tool's own database, not on Bluesky.
- Keep a valid session. Access tokens expire quickly and need refreshing on a cycle, whether you used an app password or OAuth.
- Fire at the scheduled minute. At the target time, the tool calls
com.atproto.repo.createRecordwith your text and any embed, and the post goes live that instant. - Handle the failure case. If the session expired, the PDS was unreachable, or a rate limit tripped, a real scheduler retries and tells you it failed. A fragile one just silently drops the post.
That fourth point is where a lot of DIY scripts fall apart. A cron job that calls the API once and gives up is not the same thing as a scheduler with retry logic and session management built in.
Ready to save hours on social media?
Schedule posts across all platforms from one dashboard.
A Bluesky scheduling workflow in a multi-platform calendar
TimeToPost does not have a one-click Bluesky connector yet. What it does give you today is the workflow layer that makes scheduling Bluesky manageable alongside everything else you post: a shared calendar so your Bluesky drafts sit next to your X, Instagram, and TikTok posts instead of living in a separate tab, the character counter tool tuned for the grapheme math below, and best-time data pulled from your other connected platforms so you are not guessing blind. Draft the post, get the length right, slot it on the calendar with the rest of your week, and fire it through your app password or OAuth session when the time comes. As direct API scheduling for Bluesky lands, it plugs into that same calendar rather than asking you to rebuild your workflow around it.
Limits: 300 graphemes and 3,000 bytes, images, write points
The number everyone quotes is 300 characters. That is close but not quite right, and the gap matters if you post with emoji or non-Latin scripts.
- The
app.bsky.feed.postlexicon caps text at 300 graphemes, which is roughly "visible characters" as a human would count them, and separately caps it at 3,000 bytes of UTF-8 encoded text. Plain ASCII text almost never hits the byte ceiling before the grapheme one. Emoji, CJK characters, and other multi-byte characters can eat through the 3,000-byte budget well before you reach 300 graphemes, so a post that "looks" short can still get rejected on bytes. If you post in a language with dense multi-byte characters, or you post heavy with emoji, count bytes, not just characters. - Images cap at 4 per post, at roughly 1MB each.
- Write operations are metered in points, not raw request counts: 5,000 points per hour and 35,000 per day. A
createRecord(posting) counts as 3 points, which works out to roughly 1,666 posts per hour before you would hit the ceiling, a number no real account gets near through normal use. - Separately, there is an IP-level cap of roughly 3,000 requests per 5 minutes.
None of these are limits a normal posting cadence bumps into. They matter mainly if you are building bulk-import tooling or backfilling a large archive of old posts at once.
Best time (no official analytics, per-audience)
Here is the honest answer: Bluesky does not ship an official analytics or insights product the way some platforms do, and there is no rigorous public study on best posting times specific to the platform yet. Anyone telling you "9am Tuesday" for Bluesky is guessing, or extrapolating from a different platform's data.
What actually works is testing against your own audience. If you already post elsewhere, your best-time patterns from those platforms are a reasonable starting point, not a guarantee, since Bluesky's audience composition and usage rhythm differ. Our best time to post in 2026 breakdown covers how we approach this per-platform, and the best time to post calculator can help you track your own results as they come in rather than relying on someone else's chart.
FAQ
Is giving a scheduler my app password safe? It is safer than giving your real password, since app passwords are scoped and individually revocable. It is not zero-risk. Only hand one to a tool you trust, and revoke it the moment you stop using that tool. Prefer OAuth where a tool offers it.
Can I schedule Bluesky threads? Yes, mechanically. A thread is just a series of posts with reply references pointing back to the previous post in the chain. A scheduler that supports Bluesky threading needs to fire each post in order and correctly reference the prior post's URI, which is more state to manage than a single standalone post.
Can I schedule video posts? Bluesky supports video, but scheduling one works the same way as an image post: your tool holds the file and metadata, then uploads and creates the record at the scheduled time. Check any tool's video specs directly, since upload size and duration limits are more restrictive than images.
What are the real rate limits? Write operations are metered at 5,000 points per hour and 35,000 per day, with a post costing 3 points, roughly 1,666 posts an hour in theory. There is also a separate cap of about 3,000 requests per 5 minutes per IP. Normal posting cadence, even several posts a day, does not come close to either.
If you are running Bluesky as one channel in a broader posting calendar, the social media scheduler and character counter are built to keep it in step with everything else you publish, even before a direct connector exists.