Bluesky API
Bluesky is the easiest network to automate. Mostly.
No app review, no paid tier, open protocol. The catches are graphemes, facets, link cards and session limits. Bluesky is one of five networks live on our API today.
The short answer · Updated Nov 1, 2026
Bluesky's API is the AT Protocol: a post is an app.bsky.feed.post record written to the account's repository with com.atproto.repo.createRecord. Text is limited to 300 graphemes (and 3,000 bytes), links and mentions need "facets" with byte offsets, and a post can embed up to 4 images of up to 2 MB each. Bots authenticate with an app password, not the main password.
Bluesky: live. Posting to Bluesky works on the API today.
| Limit | Value | Source |
|---|---|---|
| Post text | 300 graphemes and 3,000 bytes | app.bsky.feed.post lexicon |
| Images per post | 4 | app.bsky.embed.images lexicon |
| Image size | 2 MB each (formerly 1 MB) | app.bsky.embed.images lexicon |
| Languages per post | Up to 3 in langs | app.bsky.feed.post lexicon |
| Logins (createSession) | 30 per 5 minutes, 300 per day, per account | Bluesky rate limits |
| Repository writes | 5,000 points per hour, 35,000 per day; a create costs 3 | Bluesky rate limits |
| All API requests | 3,000 per 5 minutes per IP | Bluesky rate limits |
Lexicons read in @atproto/api; rate limits from Bluesky's docs for hosted (bsky.social) accounts. Read 2026-11-01. Self-hosted PDSes set their own limits.
Is the Bluesky API free?
Yes. Posting to Bluesky through the API has no fee, no paid tier and no app review. You log in as the account, get a session token and write records. The limits are rate limits, counted in points, not dollars.
That makes Bluesky the network where going direct is most reasonable. If Bluesky is the only network you post to, the raw API below is a fine choice. A unified API helps when you post to Bluesky alongside X and LinkedIn, as in crossposting.
How do you post to Bluesky with the API?
Two calls. com.atproto.server.createSession exchanges a handle and app password for an access JWT; com.atproto.repo.createRecord writes the post to the app.bsky.feed.post collection. Each post needs text and createdAt, and the response returns the record's uri and cid.
These are Bluesky's own quick-start examples. $PDSHOST is https://bsky.social for most accounts, or the account's own PDS if it's self-hosted.
curl -X POST $PDSHOST/xrpc/com.atproto.server.createSession \
-H "Content-Type: application/json" \
-d '{"identifier": "'"$BLUESKY_HANDLE"'", "password": "'"$BLUESKY_PASSWORD"'"}'
curl -X POST $PDSHOST/xrpc/com.atproto.repo.createRecord \
-H "Authorization: Bearer $ACCESS_JWT" \
-H "Content-Type: application/json" \
-d "{\"repo\": \"$BLUESKY_HANDLE\", \"collection\": \"app.bsky.feed.post\", \"record\": {\"text\": \"Hello world! I posted this via the API.\", \"createdAt\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}}"What are the Bluesky API rate limits?
Writes are metered in points per account: creating a record costs 3, updating 2, deleting 1, within 5,000 points an hour and 35,000 a day. That works out to about 1,666 new posts an hour or 11,666 a day, far above anything a well-behaved bot needs.
The limit that bites is logins. createSession allows 30 calls per 5 minutes and 300 per day per account, so a script that logs in before every post will hit it. Store the session and refresh it; we resume a saved session and only log in again with the app password when the session is rejected.
Bluesky's docs say these limits are aimed at bots that follow or like at bulk scale, and that moderation and app-specific limits may apply on top. Our own fair-use cap is 300 posts per Bluesky account per 24 hours.
Why does Bluesky count graphemes, not characters?
Bluesky's lexicon limits post text to 300 graphemes and 3,000 bytes. A grapheme is what a reader sees as one character: a family emoji is several code points but one grapheme, so it counts as 1.
JavaScript's .length counts UTF-16 code units and gets this wrong; Intl.Segmenter gets it right. The 3,000-byte cap rarely matters in English but can in scripts with multi-byte characters or emoji-heavy posts; our preflight checks both limits. Our Bluesky character counter counts the way Bluesky does, and the Bluesky character limit page has the detail.
How do you attach images with the Bluesky API?
Upload each file with com.atproto.repo.uploadBlob, then reference the returned blob in an app.bsky.embed.images embed with alt text and, if you know it, aspectRatio. Bluesky's docs advise leaving aspectRatio out rather than guessing.
The lexicon allows 4 images of up to 2,000,000 bytes each. Bluesky's prose guide still mentions 1 MB in one place while its sample code checks 2 MB; the lexicon is the authority. We download each image from your public URL and return media_too_large if it's over 2 MB.
Should a Bluesky app use app passwords or OAuth?
Bluesky's quick start says apps with end-user login should use OAuth, and that bots and command-line tools can stick with passwords. In every password case it says to use an app password rather than the main account password.
Our connect page uses app passwords today: the account owner creates one in Bluesky's settings, enters it on our hosted page, and we store it encrypted. It can be revoked in Bluesky without changing the main password. Accounts on a self-hosted PDS work too; enter the PDS URL on the connect page.
How do you post to Bluesky with one call?
Send text and image URLs to POST /v1/posts. We count graphemes, build facets, upload blobs, write the record, then read it back with getPosts so verified: true in the response means the post exists. For agents, the Bluesky MCP server exposes the same call as a tool.
curl https://socialsforai.vercel.app/v1/posts \
-H "Authorization: Bearer $SOCIALS_API_KEY" \
-H "Idempotency-Key: launch-0412" \
-d '{
"accounts": ["acc_bluesky", "acc_linkedin", "acc_x"],
"text": "We just shipped webhooks.",
"wait": true
}'Post to Bluesky through our API
- 01
Get a key
POST /v1/workspaces returns a Free-plan API key.
- 02
Create a connect link
POST /v1/connect-links with platforms set to bluesky, and send the URL to the account owner.
- 03
Owner adds an app password
They create an app password in Bluesky's settings and paste it with their handle on the connect page.
- 04
Publish
POST /v1/posts with the new account id. The response includes the bsky.app URL and verified set to true once the post is read back.
Questions
What developers ask
Is the Bluesky API free?
Yes. There's no paid tier and no app review for posting from your own or a consenting user's account. Usage is governed by rate limits instead: 5,000 write points an hour and 35,000 a day per account on Bluesky's hosted service, with a post costing 3 points.
Does Bluesky have an API for posting?
Yes. Bluesky runs on the AT Protocol, and posting is a call to com.atproto.repo.createRecord that writes an app.bsky.feed.post record after logging in with com.atproto.server.createSession. Bluesky's quick start shows the same two calls in TypeScript, Python and plain curl, with no app registration needed.
What is a Bluesky app password?
A separate password created in Bluesky's settings for one app or bot. It logs in like the main password but can be revoked on its own. Bluesky's docs say to use an app password rather than the main password for API access.
Why aren't my links clickable on Bluesky?
Bluesky needs a facet for each link: a byte range in the UTF-8 text plus the link URI. Text alone stays plain. Posts sent through our API get link, mention and hashtag facets automatically. Link preview cards are a separate embed we don't generate yet.
How many posts can a Bluesky bot make per day?
Bluesky's write limit allows about 11,666 new records per day per account, at 3 points each within 35,000 daily points. The tighter limit is logins: 300 createSession calls per day, so reuse sessions. Our fair-use cap is 300 posts per account per day.
Can I post to a self-hosted Bluesky PDS?
Yes. The AT Protocol lets accounts live on any PDS, and the same createSession and createRecord calls work against that host. On our connect page, enter the PDS URL alongside the handle and app password; leave it blank for bsky.social.
Get an API key and send your first post in five minutes.