Skip to main content
SocialAPI connects a Bluesky account with an app password. It publishes posts with up to 4 images, reads and answers replies and mentions, hides replies on your posts, and reads and sends direct messages (DMs) when the app password allows it. Bluesky has no consent screen: you create an app password in Bluesky, send it to SocialAPI in one request, and there is no OAuth redirect and no app to register. Platform apps lists which app connects each platform.

At a glance

Capabilities

An unsupported call returns 501 resource.not_supported. Read the capabilities object on each comment instead of assuming (can_reply, can_delete, can_hide). The capability matrix for every platform is on Supported platforms.

Connect an account

A Bluesky account connects in one request: you send its handle and an app password, and SocialAPI returns the connected account.
1

Create an app password

Sign in to Bluesky in a browser and open App passwords in Settings. Click Add app password and name it, for example socialapi. Never send your account password.
2

Allow direct messages (optional)

Tick Allow access to your direct messages before you create the password if SocialAPI must read and send DMs. You can set this option only when you create the app password. To add DM access later, create a second app password with the option on and connect again with it.
3

Copy the password

Bluesky shows the app password once, in the form xxxx-xxxx-xxxx-xxxx. Copy it.
4

Connect the account

Send the handle as metadata.identifier and the password as metadata.app_password:
Response (HTTP 201):
identifier is the account’s handle or its DID. Without brand_id in the request, SocialAPI creates a brand for the account. SocialAPI also sends account.connected to your webhook endpoints. A custom-domain handle works the same way. A missing identifier or app_password returns 400 validation.field_required, with meta.field naming it. A handle Bluesky cannot resolve, or an app password Bluesky rejects, returns 400 validation.field_invalid.

Two-factor accounts

When the account has email two-factor authentication on, the first connect request returns 400 platform.bluesky.auth_factor_required and Bluesky emails a confirmation code. Send the same request again with that code as metadata.auth_factor_token.

Disconnect and revoke

Disconnecting the account (DELETE /v1/accounts/{account_id}) deletes the app password from SocialAPI. Bluesky offers no way to revoke an app password from outside, so the password stays valid on Bluesky. To cut off access completely, delete the app password in Settings on Bluesky.

Direct messages

DMs work only with an app password that has direct message access. SocialAPI checks this when you connect. Without it, every DM call returns 403 platform.bluesky.dm_scope_missing, and posts, comments, and mentions keep working. The fix is a new app password with Allow access to your direct messages on, then a new connect request. Reconnecting with the old password does not help. You list conversations with GET /v1/inbox/conversations, read a conversation with GET /v1/inbox/conversations/{conversation_id}/messages, and reply with POST /v1/inbox/conversations/{conversation_id}/messages. The request fields are on Direct messages. These rules are specific to Bluesky:
  • Text only. A Bluesky message carries text. SocialAPI sends only the text of a send request. It does not send attachments, buttons, quick_replies, cards, or list_sections.
  • No message_tag. Bluesky has no message tags: message_tag is ignored.

Comments and replies

You read the replies to a post with GET /v1/inbox/comments/{postId} and answer with the reply endpoints on Comments.
  • One flat list. Bluesky returns a thread as a tree with no cursor. SocialAPI flattens it, the pagination object is always empty, and replies deeper than 6 levels below the post are not returned. A very large thread can be cut off.
  • Hide, not delete. Only the author of the original post can hide a reply to it: another account gets 403 platform.bluesky.not_thread_owner. A thread holds at most 300 hidden replies: the 301st returns 422 platform.bluesky.hidden_replies_full. A hidden reply is not removed: Bluesky no longer shows it by default.
  • Delete only your own replies. platform.bluesky.not_own_record is returned when you try to delete a reply from another account.
  • No likes and no private replies. can_like and can_private_reply are false on every comment.

Mentions

GET /v1/accounts/{account_id}/mentions returns the posts that mention the account, from Bluesky’s mention notifications. It covers the whole account, not one post. You answer a mention with a reply to that post. See Mentions.

Webhook events

Bluesky sends SocialAPI no notification of its own, so there is no comment.received or dm.received event: read comments and DMs with the inbox endpoints. Listing mentions with GET /v1/accounts/{id}/mentions sends a mention.received event for the listed items: see Mentions you list. Bluesky does emit the post.* events for posts you publish through SocialAPI, account.connected when you connect the account, account.disconnected when SocialAPI flags the account for reconnection, and account.updated when the account loses permissions you did not remove yourself. See Webhook events.

Limits

When Bluesky’s own limit is used up, the call fails with 429 platform.bluesky.rate_limit. Wait, then retry. GET /v1/accounts/{id}/limits returns the 2 write budgets.

Scopes

Bluesky has no OAuth scopes. An app password gives SocialAPI the access of the account itself, except direct messages, which need the option you tick when you create the password. SocialAPI uses it to publish posts, read and reply to comments and mentions, and read and send DMs. The account supports the posts.read, posts.write, comments.read, comments.write, messages.read, and messages.write permissions, which SocialAPI alone enforces.
Last modified on October 19, 2026