Bluesky API
Use the Crosspostify API to publish image, multi-image and video posts to Bluesky — the open, decentralized social network built on the AT Protocol.
Bluesky is a decentralized social network built on the AT Protocol — an open standard for social networking that gives users control over their identity and data. It has grown rapidly as an alternative to X/Twitter, with a strong community of developers, journalists, researchers, and creators.
The Crosspostify Bluesky integration supports three post types: image, multi-image (up to 4 images) and video. Text-only posts are not available for Bluesky (/post/text only accepts Facebook and Threads), and threads (reply chains) are not supported. Bluesky posts have a 300-character limit; Crosspostify cuts longer text to 300 characters when posting. The title parameter is not used on Bluesky — all content goes in caption.
This page covers everything specific to Bluesky when using the Crosspostify API. For general authentication, rate limits, and error handling, see the full API Reference.
Bluesky-Specific Parameters
Bluesky has a minimal parameter set. There are no platform-exclusive fields — just accountId and caption. The title parameter is not rendered on Bluesky and should be omitted.
accountId✓ AppliesRequired. Your Bluesky account ID from /social-accounts.caption✓ AppliesThe post text. Cut to 300 characters when posting (no error). Hashtags, mentions and links stay plain text.title✗ IgnoredNot rendered on Bluesky — omit this field. All content goes in caption.link✗ IgnoredIgnored for Bluesky — put URLs in caption.boardId✗ IgnoredPinterest only — ignored for Bluesky.is_aigc✗ IgnoredTikTok, Instagram and Facebook only — ignored for Bluesky.thumbnailTimestamp✗ IgnoredTikTok only — ignored for Bluesky.facebookFirstComment✗ IgnoredFacebook only — ignored for Bluesky.textFormatPresetId✗ IgnoredFacebook text posts only — ignored for Bluesky.tiktok / instagram / facebook✗ IgnoredSettings for those platforms — ignored for Bluesky.Bluesky has a 300-character limit per post. Captions longer than 300 characters are cut to the first 300 characters when posting. No error is returned, and GET /posts still shows the full caption. Emoji can count as 2 or more characters toward this cut. Plan your content accordingly — Bluesky rewards concise, punchy writing.
Bluesky uses a rich text system called facets to make links, hashtags and @mentions clickable. Crosspostify posts the caption as plain text: #hashtags, @handles and links are not converted to facets and are not clickable. Alt text is not supported either; images and videos are posted without alt text.
Supported Endpoints
Bluesky supports three Crosspostify post types. Text-only posts (/post/text) are not available for Bluesky.
/post/imageImage PostPublish a single image to Bluesky. The image is embedded inline in the post alongside the caption text.
Image Post Notes
- Caption and image appear together in a single post — captions over 300 characters are cut (no error)
- Images are displayed inline in the feed
- Crosspostify does not shrink images for Bluesky. Bluesky has its own image size limit, much smaller than the 20 MB Crosspostify accepts, so large images can fail at publish time. Keep image files small.
- Images are posted without alt text (alt text is not supported)
- Upload your image first via
POST /mediaand pass the returnedidasmediaId scheduledDateaccepts any ISO 8601 date-time.Zor an offset (e.g.+01:00) is converted to UTC; no offset means UTC. It must be in the future.- Omit
scheduledDateto post immediately
Request Body
mediaIdstringrequiredThe ID of the uploaded image file. Obtain this from POST /media.
scheduledDatestringoptionalISO 8601 date-time in the future, e.g. 2030-01-15T10:00:00Z (offsets are converted to UTC). Omit to post immediately.
postsarrayrequiredArray of post objects. Include one object per Bluesky account.
posts[].accountIdstringrequiredThe Bluesky account ID. Retrieve from GET /social-accounts.
posts[].captionstringoptionalPost text. Cut to 300 characters when posting (no error). Hashtags, mentions and links stay plain text.
Response
202 Accepted. status is SCHEDULED when scheduledDate is set, otherwise QUEUED. The post is published afterwards; check the result with GET /posts?id=<id>.
Code Example
curl -X POST "https://v1.api.crosspostify.com/post/image" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"mediaId": "img_12345",
"scheduledDate": "2030-01-15T10:00:00Z",
"posts": [
{
"accountId": "acc_bluesky_321",
"caption": "Golden hour over the Dolomites. Sometimes you just have to put the camera down and breathe it in. 🏔️ #landscape #photography #travel"
}
]
}'/post/imagesMulti-ImagePublish 1 to 4 images in a single Bluesky post. Images are displayed in a grid alongside the caption text; a single image is posted as a normal image post.
Bluesky supports a maximum of 4 images per post. This is Bluesky's limit, also checked by Crosspostify: more than 4 mediaIds returns 400 "Too many images" with the message "Bluesky supports a maximum of 4 images per post".
Multi-Image Notes
- 1 to 4 images per post (Bluesky's limit, also checked by Crosspostify)
- Images are displayed in the order provided in
mediaIds - A single shared
captionappears with all images; it is cut to 300 characters when posting - Bluesky may crop images in the multi-image grid preview of the feed
- Images are posted without alt text and are not shrunk by Crosspostify; keep image files small
scheduledDateaccepts any ISO 8601 date-time.Zor an offset (e.g.+01:00) is converted to UTC; no offset means UTC. It must be in the future.
Request Body
mediaIdsarrayrequiredOrdered array of 1 to 4 image media IDs. Images appear in this order. More than 4 returns 400.
scheduledDatestringoptionalISO 8601 date-time in the future, e.g. 2030-01-15T10:00:00Z (offsets are converted to UTC). Omit to post immediately.
postsarrayrequiredArray of post objects. Include one object per Bluesky account.
posts[].accountIdstringrequiredThe Bluesky account ID. Retrieve from GET /social-accounts.
posts[].captionstringoptionalShared caption for all images. Cut to 300 characters when posting (no error). Hashtags, mentions and links stay plain text.
Response
202 Accepted. status is SCHEDULED when scheduledDate is set, otherwise QUEUED.
Code Example
curl -X POST "https://v1.api.crosspostify.com/post/images" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"mediaIds": ["img_001", "img_002", "img_003"],
"scheduledDate": "2030-01-15T10:00:00Z",
"posts": [
{
"accountId": "acc_bluesky_321",
"caption": "Three frames from the shoot this morning. The light was doing something special. 📷 #photography #filmlook"
}
]
}'/post/videoVideo PostPublish a video to Bluesky. The video is attached to the post together with the caption text; Bluesky decides how it is processed and played in the feed.
Video Post Notes
- Upload your video first via
POST /mediaand pass the returnedidasmediaId - Use MP4 files. MOV uploads are accepted but are sent to Bluesky as
video/mp4and may fail. - Caption appears alongside the video — captions over 300 characters are cut (no error)
- Videos are posted without alt text
- Bluesky's tech-forward audience responds well to demos, tutorials, and build-in-public content
scheduledDateaccepts any ISO 8601 date-time.Zor an offset (e.g.+01:00) is converted to UTC; no offset means UTC. It must be in the future.
Request Body
mediaIdstringrequiredThe ID of the uploaded video file. Obtain this from POST /media.
scheduledDatestringoptionalISO 8601 date-time in the future, e.g. 2030-01-15T10:00:00Z (offsets are converted to UTC). Omit to post immediately.
postsarrayrequiredArray of post objects. Include one object per Bluesky account.
posts[].accountIdstringrequiredThe Bluesky account ID. Retrieve from GET /social-accounts.
posts[].captionstringoptionalPost text alongside the video. Cut to 300 characters when posting (no error). Hashtags, mentions and links stay plain text.
Response
202 Accepted. status is SCHEDULED when scheduledDate is set, otherwise QUEUED. Bluesky may still reject the video after this; GET /posts then shows status FAILED with an error.
Code Example
curl -X POST "https://v1.api.crosspostify.com/post/video" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"mediaId": "vid_12345",
"scheduledDate": "2030-01-15T10:00:00Z",
"posts": [
{
"accountId": "acc_bluesky_321",
"caption": "Built a small tool this weekend that auto-generates alt text for images using a local model. Here is a quick demo. 🛠️ #buildinpublic #opensource #ai"
}
]
}'AT Protocol
Bluesky is built on the AT Protocol (atproto) — an open, federated protocol for social networking. Unlike centralized platforms, AT Protocol gives users portable identities, self-hosted data, and the ability to migrate between providers. Here's what that means for API users:
Decentralized Identity
- Bluesky handles are domain-based (e.g. @you.bsky.social)
- Custom domains work as handles (e.g. @yourname.com)
- DIDs (Decentralized Identifiers) are the underlying identity layer
- Crosspostify abstracts this — just use your accountId
Personal Data Servers (PDS)
- All posts are stored in a user-controlled Personal Data Server
- Posts are publicly accessible via the AT Protocol firehose
- Content is portable — users can migrate their data
- Crosspostify handles PDS interaction transparently
Rich Text Facets
- Bluesky uses "facets" for inline rich text (links, hashtags, mentions)
- Crosspostify does not create facets
- Hashtags, @mentions and URLs are posted as plain text
- Without facets they are not clickable in the Bluesky app
Open & Transparent
- All posts are public by default — no private posts via API
- Post records are cryptographically signed
- The AT Protocol spec is open source and community-governed
Crosspostify handles the AT Protocol work — DID resolution, PDS authentication and lexicon record creation — behind the scenes. You just send a standard Crosspostify API request and we take care of the rest.
Content Guidelines
Bluesky's audience is tech-forward, values-driven, and highly engaged. Content that is authentic, conversational, and community-native performs best. The platform skews toward developers, journalists, researchers, and open-web advocates.
Image Requirements
- Format: JPG, PNG or WEBP (converted to JPEG on upload)
- Crosspostify accepts up to 20MB but does not shrink images for Bluesky
- Bluesky has its own, much smaller image size limit — keep files small
- Images appear inline in the feed
- Max 4 images per post
- No alt text (not supported by the API)
Video Requirements
- Format: MP4 (MOV uploads are sent as video/mp4 and may fail)
- Max file size: 100MB via Crosspostify
- Bluesky limits video length and size; check the current Bluesky limits
Text & Caption Tips
- 300-character limit; longer text is cut (no error)
- Emoji can count as 2 or more characters toward the 300 cut
- URLs are posted as plain text and count toward the limit
- Short, conversational posts work best
Scheduling
- Omit scheduledDate to post immediately
- Any ISO 8601 time; offsets are converted to UTC, no offset = UTC
- Must be in the future (past = 400); no minimum lead time
- Published within about 1 minute of the scheduled time