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

How the Bluesky API works: free access with no app review, app passwords vs. OAuth, posting with createRecord or Python, and Bluesky's published rate limits.

Canonical page: https://www.tryadeli.com/blog/bluesky-api
Last updated: 2026-10-12

By Mika Reyes, Co-founder, Adeli. Published 2026-10-12.

## 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](https://bsky.network/docs/bluesky-api), 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](https://www.tryadeli.com/docs/api/posts/publish-bluesky) treat it: "Bluesky's API is free: nothing is billed per post."

Compare that with X, which bills every post at its [published rates](https://www.tryadeli.com/blog/x-api-pricing). 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](https://bsky.network/docs/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](https://bsky.network/docs/oauth-client) 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](https://bsky.network/docs/bluesky-api) and the [atproto OAuth spec](https://atproto.com/specs/oauth).

**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](https://bsky.network/docs/bluesky-api):

1. **Create a session.** POST your handle and app password to your PDS host (for most accounts, `https://bsky.social`):

```bash
curl -X POST $PDSHOST/xrpc/com.atproto.server.createSession \
  -H "Content-Type: application/json" \
  -d '{"identifier": "'"$BLUESKY_HANDLE"'", "password": "'"$BLUESKY_PASSWORD"'"}'
```

2. **Copy `accessJwt` from the response.** You send it as a bearer token. Keep `refreshJwt` to get a new access token when it expires.
3. **Create the post record:**

```bash
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)\"}}"
```

4. **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](https://bsky.network/docs/bluesky-api/creating-a-post) 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:

```python
# 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](https://bsky.network/docs/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](https://www.tryadeli.com/docs/api/posts/publish-bluesky) and [connect docs](https://www.tryadeli.com/docs/api/connect):

- **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](https://www.tryadeli.com/docs/api/posts/delete-bluesky-post)).
- **List.** The 10 most recent top-level posts per account, with engagement counts.

A publish call looks like this:

```bash
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](https://www.tryadeli.com/product/bluesky) 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](https://www.tryadeli.com/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](https://www.tryadeli.com/pricing), and your first 3 are free.

## 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.

## Related Adeli pages

- [Bluesky API for Developers](https://www.tryadeli.com/product/bluesky.md): Connect Bluesky accounts with AT Protocol OAuth, then publish posts, threads, images, and video through one API. No per-post fee. Free to start.
- [Social Media Posting & Scheduling API](https://www.tryadeli.com/social-media-posting-api.md): Adeli's social media posting and scheduling API publishes to Instagram, TikTok, YouTube, Facebook, X, and Bluesky in one request, now or at a set time. Free to start.
- [Adeli Pricing — priced per connected account](https://www.tryadeli.com/pricing.md): Your first 3 connected accounts are free, then $5 each for accounts 4–10, $3 each for 11–50, $2 each for 51–100, and $1 each up to 2,000. Above that, talk to sales.
- [All posts on the Adeli blog](https://www.tryadeli.com/blog.md)

## About the author

Mika Reyes is the CEO behind Adeli and a tech and AI content creator. She was previously co-founder and CEO of Parallax, which was acquired by Phantom, and a product lead at LinkedIn. She is a Forbes 30 Under 30 honoree.
