DEV Community

Cover image for I called Bluesky simple. Time for the asterisks.
Eugeniya Ivanova
Eugeniya Ivanova

Posted on • Originally published at publora.com

I called Bluesky simple. Time for the asterisks.

In my last post I called posting to Bluesky "refreshingly straightforward." I still think that's true. I just stopped at "Hello, world."

Getting started really is easy. One request creates a session with your handle and app password. Another writes a record into app.bsky.feed.post. Two API calls later, your post is live.

Then you add a link.

The URL is sitting right there in the text, but it isn't clickable. Bluesky doesn't automatically treat URLs as links. Instead, you describe where the link appears using facets—metadata that points to the exact byte range occupied by the URL.

And "byte" is the word that ruins your afternoon.

If your post is plain ASCII, character positions and byte positions happen to match. Add Unicode, and they no longer do. Put one đź‘‹ at the beginning of the post and every offset after it shifts. The text still looks right, but your facet now points somewhere else.

If you've ever wondered why the official SDK ships a RichText helper, this is why. It detects links, mentions, and hashtags, then computes the byte offsets correctly so you don't have to.

Tokens have their own catch.

Bluesky gives you two: a short-lived access token and a longer-lived refresh token. Log in and post immediately, and the access token is usually enough. Schedule the post for later, and it may have expired by the time the job runs. At that point, refreshing it becomes part of the normal publishing flow.

Images are another step of their own. Upload each image as a blob, then reference it in the post record. You can attach up to four images. The post itself is limited to 300 characters, so if you're over the limit, deciding what to cut is your responsibility.

None of these pieces is especially difficult. The friction comes from having to deal with all of them yourself. "Send a post" quietly becomes "calculate byte offsets, refresh tokens, upload blobs, validate limits."

That's perfectly reasonable if you're building directly on AT Protocol.

At Publora, we hide those details behind a single request: content, platforms, and an optional scheduled time. Facets for links, mentions, and hashtags are generated automatically, tokens are refreshed when needed, images are uploaded, and posts over 300 characters are trimmed to fit. It's the same Bluesky API underneath—we just take care of the repetitive parts.

I'm obviously biased, so here's the recommendation I'd make anyway: if you're building specifically for Bluesky, use the official SDK. It's well designed and saves you from implementing the fiddly bits yourself. But once Bluesky becomes one destination among several, another layer starts to make a lot of sense.

The first two API calls really are refreshingly simple.

Everything after them is the actual integration.

Further reading

For the full details—facets, blobs, replies, threads, embeds, and more—the official guide is the place to start:

Posting via the Bluesky API

And if you'd rather follow someone building a full CLI from scratch:

Drafted with AI, then reviewed against the Bluesky documentation and Publora's own implementation guide before publishing.

Top comments (0)