Zernio
APIFor you
Zernio
APIFor you
Platforms

Bluesky

Overview

Publish

Posts & ThreadsMedia Requirements

Engage

AnalyticsInbox

Reference

Limits & Errors
Dashboard
OverviewPlatformsAPI ReferenceResources
Bluesky

Posts & Threads

Publish a text, image, video or thread post to Bluesky, set the post language, and every platformSpecificData field.


A plain post becomes a text post. Media in mediaItems makes an image or video post, and threadItems in platformSpecificData on the Bluesky entry makes a thread.

Text post

import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: published } = await zernio.posts.createPost({
  body: {
    content: 'Shipped a new feature today. Notes at https://example.com/changelog',
    platforms: [
      { platform: 'bluesky', accountId: '66b2e19d8c3f5a7e9d0b1c2d' }
    ],
    publishNow: true
  }
});

console.log(published.post.platforms[0].platformPostUrl);

Response (201):

{
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "status": "published",
    "platforms": [
      {
        "platform": "bluesky",
        "status": "published",
        "platformPostUrl": "https://bsky.app/profile/yourhandle.bsky.social/post/..."
      }
    ]
  }
}

Every sample below changes only the mediaItems or the platforms entry of this request.

Zernio converts the text to AT Protocol facets on its own:

  • @handle.bsky.social becomes a profile link.
  • #hashtag becomes a clickable hashtag.
  • A URL becomes a clickable link with a preview card.

Bluesky builds the link card from the target page's Open Graph tags. Put the URL at the end of the post, and check that the page has them.

Image post

Up to 4 images. Alt text goes in altText on the media item:

"mediaItems": [
  { "type": "image", "url": "https://cdn.example.com/photo1.jpg", "altText": "The launch screen" },
  { "type": "image", "url": "https://cdn.example.com/photo2.jpg" },
  { "type": "image", "url": "https://cdn.example.com/photo3.jpg" },
  { "type": "image", "url": "https://cdn.example.com/photo4.jpg" }
]

Video post

One MP4 per post, at most 50 MB and 60 seconds:

"mediaItems": [
  { "type": "video", "url": "https://cdn.example.com/demo.mp4" }
]

Thread

threadItems publishes a chain of posts. The first item is the root post, and every later item replies to the one before it.

Each item has its own text and media, and its own 300-character limit.

When threadItems is set, the top-level content is used for display and search only and is not published. The first post goes in threadItems[0]:

{
  "platform": "bluesky",
  "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
  "platformSpecificData": {
    "threadItems": [
      {
        "content": "A thread about building APIs",
        "mediaItems": [{ "type": "image", "url": "https://cdn.example.com/api.jpg" }]
      },
      { "content": "First, design your endpoints around resources, not actions." },
      { "content": "Second, version your API from day one." },
      { "content": "Third, document everything. Your future self will thank you." }
    ]
  }
}

Post language

langs tags the post with 1 to 3 BCP-47 codes. Bluesky feed generators filter on this field, so a post without it never appears in language-scoped feeds.

It is set at creation only, because Bluesky has no post editing. Every item of a thread carries the same langs:

{
  "platform": "bluesky",
  "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
  "platformSpecificData": { "langs": ["pt", "en"] }
}

When a post has no langs, Zernio applies the account's default at publish time.

A per-post langs always wins. The default applies to posts published after the change.

Read the account default with Get Bluesky account settings and set it with Update Bluesky account settings:

await zernio.accounts.updateBlueskySettings({
  path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' },
  body: { defaultLangs: ['pt'] }
});

Response (200), the updated settings. GET /v1/accounts/{accountId}/bluesky-settings reads the same shape back:

{
  "defaultLangs": ["pt"]
}

Send "defaultLangs": null to clear the default.

Platform fields

All fields go in platformSpecificData on the Bluesky entry.

FieldTypeDefaultDescription
langsArray<string>account default1 to 3 BCP-47 codes (pt, en-US) written to the post's langs field. Absent when neither the post nor the account sets one.
threadItemsArray<{content, mediaItems?}>The whole thread, root post first. Top-level content is then display and search only.
Was this page helpful?

Bluesky

Publish text, image, video and thread posts to Bluesky with the Zernio API, connected with an app password instead of OAuth.

Media Requirements

Image and video requirements for Bluesky posts.

On this page

Text postImage postVideo postThreadPost languagePlatform fields