Bluesky API

Use the Crosspostify API to publish image, multi-image, video, and text posts to Bluesky — the open, decentralized social network built on the AT Protocol.

Post Types
Image · Multi-image · Video · Text
Character Limit
300 characters
Protocol
AT Protocol (atproto)
Audience
Tech, open web, creators

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 all four post types: image, multi-image, video, and text. Bluesky posts are called skeets and have a 300-character limit. 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 (skeet). Max 300 characters. Hashtags and @mentions are parsed as rich text facets.
title✗ IgnoredNot rendered on Bluesky — omit this field. All content goes in caption.
boardId✗ IgnoredPinterest only — ignored for Bluesky.
is_aigc✗ IgnoredTikTok only — ignored for Bluesky.
facebookFirstComment✗ IgnoredFacebook only — ignored for Bluesky.
textFormatPresetId✗ IgnoredFacebook only — ignored for Bluesky.
privacy✗ IgnoredYouTube only — all Bluesky posts are public.
categoryId✗ IgnoredYouTube only — ignored for Bluesky.
tags✗ IgnoredYouTube only — use #hashtags inline in caption instead.
300-character limit

Bluesky enforces a strict 300-character limit per post. Crosspostify will return a validation error if your caption exceeds this limit. Plan your content accordingly — Bluesky rewards concise, punchy writing.

Rich text facets — hashtags & mentions

Bluesky uses a rich text system called facets to parse inline links, hashtags, and @mentions. Crosspostify automatically detects #hashtags and @handles in your caption and converts them to clickable AT Protocol facets — no extra configuration needed.

Supported Endpoints

Bluesky supports all four Crosspostify post types:

POST/post/imageImage Post

Publish 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 — caption counts toward the 300-character limit
  • Images are displayed inline in the feed with no cropping
  • Square (1:1) and landscape (16:9) images work best in the Bluesky feed
  • Upload your image first via POST /media and use the returned mediaId
  • All datetime values must be in UTC timezone (ISO 8601 format with Z suffix)
  • Omit scheduledDate to post immediately

Request Body

mediaIdstringrequired

The ID of the uploaded image file. Obtain this from POST /media.

scheduledDatestringoptional

Schedule the post for a future time (ISO 8601 with 'Z' suffix). Omit to post immediately.

postsarrayrequired

Array of post objects. Include one object per Bluesky account.

posts[].accountIdstringrequired

The Bluesky account ID. Retrieve from GET /social-accounts.

posts[].captionstringoptional

Post text. Max 300 characters. Hashtags and @mentions are auto-converted to rich text facets.

Response

{ "success": true, "data": { "postId": "post_bsky_001", "status": "SCHEDULED", "scheduledDate": "2025-06-01T14:00:00Z", "accounts": [ { "accountId": "acc_bluesky_321", "platform": "bluesky", "status": "SCHEDULED" } ] } }

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": "2025-06-01T14: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/post/imagesMulti-Image

Publish up to 4 images in a single Bluesky post. Images are displayed in a grid alongside the caption text.

Maximum 4 images per post

Bluesky supports a maximum of 4 images per post. If you provide more than 4 mediaIds, Crosspostify will return a validation error. This is a platform-level limit enforced by the AT Protocol.

Multi-Image Notes

  • Maximum 4 images per post — a hard AT Protocol limit
  • Images are displayed in the order provided in mediaIds
  • A single shared caption appears with all images
  • All images share the same 300-character caption limit
  • All datetime values must be in UTC timezone (ISO 8601 format with Z suffix)

Request Body

mediaIdsarrayrequired

Ordered array of image media IDs. Maximum 4 images. Images appear in this order.

scheduledDatestringoptional

Schedule the post for a future time (ISO 8601 with 'Z' suffix). Omit to post immediately.

postsarrayrequired

Array of post objects. Include one object per Bluesky account.

posts[].accountIdstringrequired

The Bluesky account ID. Retrieve from GET /social-accounts.

posts[].captionstringoptional

Shared caption for all images. Max 300 characters. Hashtags and @mentions auto-converted to facets.

Response

{ "success": true, "data": { "postId": "post_bsky_002", "status": "SCHEDULED", "scheduledDate": "2025-06-01T14:00:00Z", "mediaCount": 3, "accounts": [ { "accountId": "acc_bluesky_321", "platform": "bluesky", "status": "SCHEDULED" } ] } }

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": "2025-06-01T14:00:00Z",
    "posts": [
      {
        "accountId": "acc_bluesky_321",
        "caption": "Three frames from this morning\'s shoot. The light was doing something special. 📷 #photography #filmlook"
      }
    ]
  }'
POST/post/videoVideo Post

Publish a native video to Bluesky. Videos are embedded inline in the post and play directly in the feed. Bluesky's video support is a newer feature with a growing audience.

Video Post Notes

  • Videos are embedded natively and play inline in the Bluesky feed
  • Upload your video first via POST /media and use the returned mediaId
  • Caption appears alongside the video — counts toward the 300-character limit
  • Bluesky's tech-forward audience responds well to demos, tutorials, and build-in-public content
  • All datetime values must be in UTC timezone (ISO 8601 format with Z suffix)

Request Body

mediaIdstringrequired

The ID of the uploaded video file. Obtain this from POST /media.

scheduledDatestringoptional

Schedule the post for a future time (ISO 8601 with 'Z' suffix). Omit to post immediately.

postsarrayrequired

Array of post objects. Include one object per Bluesky account.

posts[].accountIdstringrequired

The Bluesky account ID. Retrieve from GET /social-accounts.

posts[].captionstringoptional

Post text alongside the video. Max 300 characters. Hashtags and @mentions auto-converted to facets.

Response

{ "success": true, "data": { "postId": "post_bsky_003", "status": "PROCESSING", "scheduledDate": "2025-06-01T14:00:00Z", "videoDuration": 94, "accounts": [ { "accountId": "acc_bluesky_321", "platform": "bluesky", "status": "PROCESSING" } ] } }

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": "2025-06-01T14: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\'s a quick demo. 🛠️ #buildinpublic #opensource #ai"
      }
    ]
  }'
POST/post/textSkeet

Publish a text-only post (skeet) to Bluesky. Text posts are the primary format on Bluesky — concise, conversational, and highly shareable within the community.

Text Post (Skeet) Notes

  • Strict 300-character limit — plan your content carefully
  • Hashtags and @mentions are auto-converted to clickable AT Protocol facets
  • Line breaks are preserved in the rendered post
  • Bluesky's audience skews toward developers, journalists, and open-web advocates
  • Conversational, opinionated, and technical content performs well
  • Threads (reply chains) are not yet supported via the Crosspostify API

Request Body

scheduledDatestringoptional

Schedule the post for a future time (ISO 8601 with 'Z' suffix). Omit to post immediately.

postsarrayrequired

Array of post objects. Include one object per Bluesky account.

posts[].accountIdstringrequired

The Bluesky account ID. Retrieve from GET /social-accounts.

posts[].captionstringrequired

The skeet text. Max 300 characters. Required for text posts. Hashtags and @mentions auto-converted to facets.

Response

{ "success": true, "data": { "postId": "post_bsky_004", "status": "SCHEDULED", "scheduledDate": "2025-06-01T14:00:00Z", "accounts": [ { "accountId": "acc_bluesky_321", "platform": "bluesky", "status": "SCHEDULED" } ] } }

Code Example

curl -X POST "https://v1.api.crosspostify.com/post/text" \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "scheduledDate": "2025-06-01T14:00:00Z",
    "posts": [
      {
        "accountId": "acc_bluesky_321",
        "caption": "Hot take: the best API documentation is the one that shows you a real working example in the first 10 seconds.\n\nNot a diagram. Not a concept overview. A curl command that actually runs.\n\n#devex #apidocs #buildinpublic"
      }
    ]
  }'

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 auto-detects #hashtags and @mentions in captions
  • Facets are converted to clickable elements in the Bluesky UI
  • URLs in captions are also auto-linked as facets

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
  • No algorithmic feed manipulation — chronological by default
You don't need to understand AT Protocol to use the API

Crosspostify handles all AT Protocol complexity — DID resolution, PDS authentication, lexicon record creation, and facet parsing — 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 GIF
  • Max file size: 20MB per image
  • Images appear inline in the feed
  • Max 4 images per post

Video Requirements

  • Format: MP4 recommended
  • Max file size: 100MB via Crosspostify
  • Max duration: approx. 3 minutes
  • Videos autoplay in the feed

Text & Caption Tips

  • Strict 300-character limit
  • Emojis count as 1 character (usually)
  • URLs are auto-linked but count toward the limit
  • Short, conversational posts work best

Scheduling

  • Minimum lead time: ~10 minutes ahead
  • All times must be UTC (ISO 8601 + Z)
  • Omit scheduledDate to post immediately
  • Consistent posting builds algorithmic feed ranking