Bluesky API: Access, Auth, Posting, and Rate Limits

By Mika ReyesPublished 6 min read

The short answer

The Bluesky API is the open AT Protocol. There's no developer app to register, no review, and no API key: you sign in with an app password (createSession) or OAuth, then post with com.atproto.repo.createRecord. Each account can spend 5,000 write points an hour, and a post costs 3. Adeli runs the OAuth flow and publishes Bluesky posts through the same endpoint as X, Instagram, and TikTok.

The Bluesky API is the AT Protocol (atproto): an open protocol where every account is a data repository and a post is a record you write into it. There's no developer application, no review, and no fee. You authenticate as the account, with an app password or with OAuth, and create a post by calling com.atproto.repo.createRecord with an app.bsky.feed.post record.

Below is how access, auth, posting, Python, and rate limits work, all from Bluesky's developer docs, plus how Adeli's Bluesky support works.

Is the Bluesky API free?

Yes. Bluesky doesn't sell API access, and there is no paid tier or per-request price in its developer docs. That's also how Adeli's API docs treat it: "Bluesky's API is free: nothing is billed per post."

Compare that with X, which bills every post at its published rates. On Bluesky, rate limits are the only cap on how much you post.

How do I get Bluesky API access?

You already have it if you have a Bluesky account. There is no developer portal sign-up and no app approval. The access you get comes with conditions:

  • The developer guidelines apply. Bluesky's developer guidelines ban spam, including "automated or bulk interactions" that notify users, and require apps with user-generated content to offer reporting and blocking.
  • Deletion requests must be honored. All services must have a way to delete content a user asks to have deleted.
  • Bluesky can cut access. Failing to respond to known violations "may result in suspension of access" to Bluesky's infrastructure.

How does Bluesky API authentication work?

Bluesky has two ways to sign in, and neither uses an API key. Bluesky's OAuth page says apps with their own end-user login should implement OAuth, while "bots or command-line tools may use app password authentication instead."

App password + createSession AT Protocol OAuth
Best for Bots, scripts, your own account Apps where other people sign in
What the user gives you Handle and an app password Consent on their server's sign-in page
How your app is identified It isn't; you act as the account client_id is a URL to a public metadata document
Tokens accessJwt (expires after a few minutes) and refreshJwt Access tokens under 30 minutes, plus refresh tokens
Extra requirements None PKCE, PAR, and DPoP on every client

Sources: Bluesky API get started and the atproto OAuth spec.

App passwords are created in the Bluesky app's settings and are separate from your main password. Bluesky's docs tell you to use one "rather than your main account password" when you call createSession.

OAuth has no client registration. The atproto spec says the client_id "is a fully-qualified web URL pointing to the public client metadata." PKCE, PAR, and DPoP are all mandatory, and refresh tokens for public clients should last at most 2 weeks. Bluesky recommends an SDK that handles all of this rather than a hand-built client.

Not every account lives on bsky.social. A user on a self-hosted server signs in at that server, so your login has to resolve the handle to the right host.

How do I post with the Bluesky API?

Create a session, then write an app.bsky.feed.post record with createRecord. A post needs two fields, text and createdAt. This is the curl flow from Bluesky's get started guide:

  1. Create a session. POST your handle and app password to your PDS host (for most accounts, https://bsky.social):
curl -X POST $PDSHOST/xrpc/com.atproto.server.createSession \
  -H "Content-Type: application/json" \
  -d '{"identifier": "'"$BLUESKY_HANDLE"'", "password": "'"$BLUESKY_PASSWORD"'"}'
  1. Copy accessJwt from the response. You send it as a bearer token. Keep refreshJwt to get a new access token when it expires.
  2. Create the post record:
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)\"}}"
  1. Save the response. It returns the post's uri (an at:// URI) and cid, a hash of the content. You need the URI to delete the post or reply to it.

Links and mentions don't turn into links on their own. They're facets, annotations that point at byte ranges of the text, and Bluesky's creating a post guide shows how its SDK's RichText detects them for you.

How do I use the Bluesky API with Python?

Use the atproto package. It's the Python SDK Bluesky's docs use, and it handles the session for you:

# pip install atproto
from atproto import Client
 
client = Client()
client.login("handle.example.com", "your-app-password")
post = client.send_post("Hello world! I posted this via the API.")
 
print(post.uri)  # at://did:plc:.../app.bsky.feed.post/...
print(post.cid)

To tag the post's language, pass langs, for example client.send_post("Hello", langs=["en-US"]). Bluesky says this helps custom feeds filter posts. TypeScript (@bsky/sdk) and Go (indigo) SDKs follow the same pattern.

What are the Bluesky API rate limits?

Writes are limited per account with a points system: 5,000 points an hour and 35,000 a day. Creating a record costs 3 points, updating 2, and deleting 1, so one account can create at most 1,666 records an hour and 11,666 a day. Likes and follows count as records too.

Limit Value Measured per
Record writes 5,000 points/hour, 35,000 points/day Account
All API requests to a Bluesky PDS 3,000 per 5 minutes IP
createSession 30 per 5 minutes, 300 per day Account
updateHandle 10 per 5 minutes, 50 per day Account
Blob upload size 50 MB per blob Upload

Source: Bluesky rate limits, fetched 2026-10-12. Requests over a limit usually get HTTP 429, and many responses carry rate-limit headers you can use for backoff.

Two practical notes:

  • Reuse sessions. At 300 createSession calls a day, a script that logs in before every post will run out. Refresh with refreshJwt instead.
  • Other servers set their own limits. These numbers are for Bluesky-run hosts. Accounts on self-hosted servers follow whatever their operator sets.

How does Adeli's Bluesky API work?

Adeli connects your users' Bluesky accounts with AT Protocol OAuth and publishes through the same POST /api/v1/posts endpoint it uses for every network. Bluesky is live on Adeli, with connect and publish only: there are no Bluesky comments, messages, or analytics endpoints yet.

What Adeli covers, from the Bluesky publish docs and connect docs:

  • Connect. Your server starts a connect session (POST /api/v1/profiles/{profileId}/connect with platform: "bluesky") and sends your user to the returned URL. For an account on a self-hosted server, pass its handle and sign-in starts at that server.
  • Publish. Text up to 300 characters, with up to 4 images (JPEG, PNG, or WebP) or one MP4 up to 100 MB. Images over 2 MB are resized to fit Bluesky's limit, and the response lists them in resizedImages.
  • Threads. Send thread with up to 24 more posts and Adeli publishes each as a reply to the one before. A thread that fails part-way returns 207 with status: "partial".
  • Delete. DELETE /api/v1/posts/bluesky/{postId} takes the record key or the full AT URI (delete docs).
  • List. The 10 most recent top-level posts per account, with engagement counts.

A publish call looks like this:

curl --fail-with-body -X POST "$ADELI_URL/api/v1/posts" \
  -H "Authorization: Bearer $ADELI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "bluesky",
    "accountId": "00000000-0000-0000-0000-000000000000",
    "text": "Herons fish at dawn. A thread:",
    "thread": [{ "text": "They stand still for minutes at a time." }]
  }'

Self-hosted accounts shape the connect flow. Most Bluesky accounts sign in at bsky.social, but an account on another server won't be found there, which is why Adeli takes an optional handle. The consent screen also asks for broad access, because Bluesky's permissions aren't finer-grained than that today. Adeli uses it to publish, list, and delete posts, and doesn't read direct messages.

When should you use Adeli instead of the Bluesky API directly?

Call Bluesky directly if Bluesky is the only network and the accounts are yours. An app password and the atproto package get a bot posting in minutes, at no cost.

Use Adeli's Bluesky API when your customers connect their own accounts, or when Bluesky is one of several networks. You skip building DPoP-bound OAuth and handle resolution, and the same request shape reaches X, Instagram, TikTok, YouTube, and Facebook through the social media posting API. Bluesky's API is free, so there's no per-post fee: one Bluesky account is one connected account on Adeli's pricing, and your first 3 are free.

faq

Frequently asked questions

Do I need an API key for the Bluesky API?

No. Bluesky has no API keys and no developer app to register. A script or bot signs in with the account's handle and an app password, and an app with its own user login uses AT Protocol OAuth, where the client is identified by the URL of a public metadata document.

Can I use the Bluesky API with Python?

Yes. Install the atproto package with pip, call client.login with a handle and an app password, then call client.send_post with your text. The call returns the post's at:// URI and its CID.

Where are Bluesky's developer docs now?

Bluesky's developer docs moved to bsky.network. Links to docs.bsky.app redirect there, including the rate limits page and the guide to creating a post.

Can Adeli read Bluesky comments, messages, or analytics?

No. Adeli's Bluesky support covers connecting accounts, publishing posts and threads, listing an account's 10 most recent posts with engagement counts, and deleting posts. There are no Bluesky comments, messages, or analytics endpoints yet.

Power your next project
with one social media API

Give your users publishing, scheduling, and analytics through one integration your team can actually maintain.

Try with agents

Start with free credits. No credit card required.