1. Overview
A Bluesky post is published once Bluesky has accepted it. Publishing goes through the samePOST /v1/posts endpoint as every other platform. A post holds text, up to 4 images, or both. Bluesky has no video publishing through SocialAPI and no way to edit a published post.
2. Supported media
Text limit: 300 characters, counted as Unicode characters. More returns
400 publishing.validation_failed.
Image limit: 4 images per post. More than 4 returns 400 publishing.validation_failed.
File limits apply to media you upload to SocialAPI (source_type: "media_id"). A file outside them fails validation with 400 publishing.validation_failed. SocialAPI does not check a url item in advance. SocialAPI downloads the image when it publishes. An image over 2 MB fails the target with 400 validation.field_invalid, and meta.field is media.
Alt text: SocialAPI publishes every image with empty alt text. There is no field to set it.
A platform_attachment_id item fails the Bluesky target with 422 publishing.media.unsupported_source: Bluesky has no uploaded media to reference by ID. Send images as url or media_id.
3. Create post
UsePOST /v1/posts with targets that name a Bluesky account. publish_now: true publishes immediately. Without it and without scheduled_at, SocialAPI saves a draft.
http:// or https:// address in the text into a link and every #tag into a hashtag. It does not turn @handle into a mention: the text publishes as plain text and the mentioned account is not notified. SocialAPI attaches no link preview card.
Platform data fields
Bluesky has noplatform_data fields. SocialAPI forwards any additional keys in platform_data.bluesky only when a table in this section lists them. None does, so SocialAPI sends no key to Bluesky and returns no warning for an unlisted key.
Scheduling
Setscheduled_at to an ISO 8601 timestamp (UTC) to defer publishing:
4. Update post
Bluesky has no way to edit a published post.PATCH /v1/posts/{pid} edits only a post in draft, scheduled, or failed status, and returns 409 post.state_invalid for a published post.
To correct a post, delete it and publish a new one.
5. Delete post
account_id. DELETE /v1/posts/{pid} always removes every target.
6. Retrieve posts
UseGET /v1/posts to list posts. Filter by platform or account:
targets array with the per-platform status and engagement metrics:
likes is Bluesky’s like count, comments its reply count, and shares its repost count. Bluesky reports no saves, so saves stays 0. metrics_synced_at is when SocialAPI last read the counts from Bluesky.
Posts published outside SocialAPI: SocialAPI imports the 25 most recent posts of the account when you connect it and when you call POST /v1/accounts/{id}/sync.
7. Quirks, errors, and recovery
No post editing
Bluesky has no way to edit a published post.PATCH /v1/posts/{pid} returns 409 post.state_invalid for a published post. Delete the post and publish a new one.
Video
Bluesky video is not supported. A video uploaded to SocialAPI asmedia_id fails validation with 400 publishing.validation_failed. A video sent as a url that SocialAPI recognizes as a video is skipped, and the post publishes without it.
Mentions are plain text
An@handle in the text is not a mention. The mentioned account is not notified.
Image download failure
SocialAPI downloads eachurl image when it publishes. When the download fails, the target fails with 500 system.internal. Check that the URL is public and returns the image, then send POST /v1/posts/{pid}/retry.
Posting caps
SocialAPI refuses a publish that would pass 25 posts in an hour or 50 posts in 24 hours for one account, with429 publishing.velocity_limit. A scheduled post is held and published when the window clears. Bluesky also limits writes per account to 5,000 points per hour and 35,000 per day, where a new post costs 3 points. When Bluesky refuses first, the target fails with 429 platform.bluesky.rate_limit. See Posting velocity caps.
Errors on a failed target
A publish that fails after the request returned puts a structured error on the target, intargets[].error:
401 platform.bluesky.auth: Bluesky rejected the saved credential. Connect the account again.403 platform.bluesky.account_takedown: Bluesky suspended the account. Connecting again does not restore access.429 platform.bluesky.rate_limit: Bluesky’s quota is used up. Wait, then retry.400 validation.platform_param: Bluesky rejected a parameter.error.messagecarries Bluesky’s reason.400 validation.field_invalid: an image sent as aurlis larger than 2 MB.
Recovery
- Auth error. Connect the account again with a working app password:
POST /v1/accounts/connectwith the sameplatformandbrand_id. See Connect accounts. - Rate limit. Wait for the
Retry-Afterheader, then sendPOST /v1/posts/{pid}/retry. See Delivery. - Image too large. Resize the image to 2 MB or less, upload it again, and publish a new post.