# Bluesky (/docs/platforms/bluesky)




What you can post [#what-you-can-post]

| Type       | Supported | Max media | Notes                                 |
| ---------- | --------- | --------- | ------------------------------------- |
| Text only  | ✅         | 0         | Up to 300 characters                  |
| Image post | ✅         | 4         | JPEG, PNG, WebP. Max 1 MB per image   |
| Video      | ✅         | 1         | MP4 only. Max 100 MB, up to 3 minutes |

<Callout title="Limitations" type="warn">
  * **300 character limit.** Emojis count as their UTF-8 byte length, not as 1 character. A single emoji like 🚀 uses 4 bytes, so heavy emoji use reduces your effective limit.
  * **1 MB per image.** Images larger than 1 MB will be rejected by Bluesky.
  * **GIF files are not supported.** Bluesky only displays the first frame of a GIF as a static image. Convert animated GIFs to MP4 and post as video instead.
  * **You cannot mix images and video in the same post.** A post is either up to 4 images or a single video.
</Callout>

<Callout title="Posting to Bluesky alongside other platforms" type="info">
  Bluesky has a **300-character limit**, but platforms like Facebook or LinkedIn allow much longer content. You don't need to create a separate post — use `postOverrides` on the Bluesky account entry to provide shorter content just for Bluesky while keeping the full text for other platforms.

  ```json
  {
    "content": "Your long-form content for Facebook, LinkedIn, etc...",
    "accounts": [
      { "accountId": "facebook-account-id" },
      { "accountId": "linkedin-account-id" },
      {
        "accountId": "bluesky-account-id",
        "postOverrides": {
          "content": "Short version for Bluesky 🦋"
        }
      }
    ],
    "publishNow": true
  }
  ```
</Callout>

Quick start [#quick-start]

Post text [#post-text]

Mentions (`@handle.bsky.social`), links, and hashtags in your content are detected and rendered as rich text automatically. If you mention a handle that doesn't exist, it renders as plain text — no error.

<Tabs items="['cURL']">
  <Tab value="cURL">
    ```bash
    curl -X POST https://publishq.com/api/v1/posts \
      -H "Authorization: Bearer pq_live_..." \
      -H "Content-Type: application/json" \
      -d '{
        "content": "Hello from PublishQ! Check out @bsky.app 🦋",
        "accounts": [
          { "accountId": "your-bluesky-account-id-on-publishq" }
        ],
        "publishNow": true
      }'
    ```
  </Tab>
</Tabs>

Post an image [#post-an-image]

Up to 4 images per post. Text is optional — you can post images with no caption.

<div className="fd-steps">
  <div className="fd-step">
    Get your media ready [#1-get-your-media-ready]

    <Tabs items="['Upload new media', 'Use existing media']">
      <Tab value="Upload new media">
        ```bash
        curl -X POST https://publishq.com/api/v1/media \
          -H "Authorization: Bearer pq_live_..." \
          -F "file=@photo1.jpg"
        ```

        Repeat for each file (up to 4 images).
      </Tab>

      <Tab value="Use existing media">
        ```bash
        curl https://publishq.com/api/v1/media \
          -H "Authorization: Bearer pq_live_..."
        ```

        Pick the `id` of each media item you want to use from the response.
      </Tab>
    </Tabs>
  </div>

  <div className="fd-step">
    Create the post [#2-create-the-post]

    <Tabs items="['cURL']">
      <Tab value="cURL">
        ```bash
        curl -X POST https://publishq.com/api/v1/posts \
          -H "Authorization: Bearer pq_live_..." \
          -H "Content-Type: application/json" \
          -d '{
            "content": "Photo dump from the weekend 📸",
            "mediaIds": ["media-id-1", "media-id-2", "media-id-3", "media-id-4"],
            "accounts": [
              { "accountId": "your-bluesky-account-id-on-publishq" }
            ],
            "publishNow": true
          }'
        ```
      </Tab>
    </Tabs>
  </div>
</div>

Post a video [#post-a-video]

A single MP4 video per post. Text is optional. The video is preprocessed by Bluesky's video service before the post goes live, so there's no loading delay for your followers — the video is ready to play immediately.

<Callout type="info">
  Bluesky requires email verification on the connected account before video uploads.
</Callout>

<div className="fd-steps">
  <div className="fd-step">
    Get your media ready [#1-get-your-media-ready-1]

    <Tabs items="['Upload new media', 'Use existing media']">
      <Tab value="Upload new media">
        ```bash
        curl -X POST https://publishq.com/api/v1/media \
          -H "Authorization: Bearer pq_live_..." \
          -F "file=@video.mp4"
        ```
      </Tab>

      <Tab value="Use existing media">
        ```bash
        curl https://publishq.com/api/v1/media \
          -H "Authorization: Bearer pq_live_..."
        ```

        Pick the `id` of the video you want to use from the response.
      </Tab>
    </Tabs>
  </div>

  <div className="fd-step">
    Create the post [#2-create-the-post-1]

    <Tabs items="['cURL']">
      <Tab value="cURL">
        ```bash
        curl -X POST https://publishq.com/api/v1/posts \
          -H "Authorization: Bearer pq_live_..." \
          -H "Content-Type: application/json" \
          -d '{
            "content": "Check this out 🎬",
            "mediaIds": ["media-id-from-step-1"],
            "accounts": [
              { "accountId": "your-bluesky-account-id-on-publishq" }
            ],
            "publishNow": true
          }'
        ```
      </Tab>
    </Tabs>
  </div>
</div>

Media specs [#media-specs]

Images [#images]

| Spec          | Value           |
| ------------- | --------------- |
| Formats       | JPEG, PNG, WebP |
| Max file size | 1 MB per image  |
| Max per post  | 4               |

Aspect ratios are detected automatically and embedded in the post record.

Video [#video]

| Spec          | Value                                 |
| ------------- | ------------------------------------- |
| Format        | MP4                                   |
| Max file size | 100 MB (increased from 50 MB)         |
| Max duration  | 3 minutes (increased from 60 seconds) |

Not yet supported [#not-yet-supported]

* **GIF attachments** — Bluesky renders GIFs as static images. Convert to MP4 for animation.
* **Video captions** — VTT subtitle files for accessibility.
