Bluesky

Bluesky Image Upload API

How to upload and attach images to a Bluesky post via API.

Quick answer

Pass an image URL in the post's media array on POST /api/posts, or pre-upload with POST /api/media and reference the returned med_ ID — PostGate handles the AT Protocol's two-step blob upload for you. Bluesky's limits: up to 4 images per post, 1 MB each, standard image formats (JPEG, PNG, GIF, WebP), and no video yet.

On the AT Protocol, an image isn't attached to a post — it's uploaded as a standalone blob first, and only its reference gets embedded in the post record afterward. PostGate hides that two-step choreography behind the same media field used for every other platform, but the underlying limits are Bluesky's own, and worth knowing precisely before you hit them.

01

Attach an image the same way as any other platform

Pass an image URL directly in the post's media array, or pre-upload it with POST /api/media and reference the returned med_… ID. Pre-uploading is worth it for anything you'll reuse across multiple posts or platforms, since PostGate validates and caches it once instead of re-fetching the source URL on every publish.
Publish with an imagePostGate API
curl -X POST "https://postgate.studio/api/posts?profile_group_id=grp_acme123" \
  -H "Authorization: Bearer $POSTGATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "post": { "body": "New shot from the studio." },
    "profiles": ["pro_bluesky123"],
    "media": ["https://cdn.example.com/studio.jpg"]
  }'
02

Why pre-upload for validation before it reaches Bluesky?

POST /api/media ingests the file, probes its format and dimensions, and gives you a stable ID you can reuse. That validation happens before the AT Protocol blob upload, so a rejected image fails fast against your own request instead of surfacing as a delivery-time error on the Bluesky target.
Pre-upload an imagePostGate API
curl -X POST https://postgate.studio/api/media \
  -H "Authorization: Bearer $POSTGATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://cdn.example.com/studio.jpg", "filename": "studio.jpg" }'
03

What are Bluesky’s real image limits?

Up to 4 images per post, each capped at 1 MB — small compared to most other platforms in this catalog, since the AT Protocol's blob store is designed to keep repositories light. Accepted formats are standard image MIME types (JPEG, PNG, GIF, WebP).

There is no video support in this API yet: Bluesky's own video pipeline exists in the app, but it isn't exposed through the publishing surface PostGate integrates with. Only image posts.

04

Why isn’t alt text optional in the underlying record?

Every image in an AT Protocol post embed carries an alt-text field as part of the record schema. If your integration collects captions or descriptions from the customer, pass them through — it's both an accessibility requirement and a real field Bluesky clients render, not a cosmetic extra.
05

What happens over the limit?

A 5th image or an oversized file is rejected before publish, not silently dropped. Check the post's target status for a validation error rather than assuming a partial image set went out — see Delivery lifecycle for how partial results are reported across all platforms.

Frequently asked questions

How many images can a Bluesky post have?

Up to 4 images per post, each capped at 1 MB — small compared to most other platforms, since the AT Protocol’s blob store is designed to keep repositories light. Accepted formats are standard image MIME types: JPEG, PNG, GIF, and WebP.

Can I upload video to Bluesky through this API?

No — there is no video support in this API yet. Bluesky’s own video pipeline exists in the app, but it isn’t exposed through the publishing surface PostGate integrates with. Only image posts are supported today.

Should I pass an image URL or pre-upload the media first?

Either works: pass a URL directly in the post’s media array, or pre-upload with POST /api/media and reference the returned med_ ID. Pre-uploading is worth it for anything reused across multiple posts or platforms — PostGate validates and caches it once, and because that validation happens before the AT Protocol blob upload, a rejected image fails fast against your own request instead of surfacing as a delivery-time error.

Is alt text required for Bluesky images?

Every image in an AT Protocol post embed carries an alt-text field as part of the record schema. If your integration collects captions or descriptions from the customer, pass them through — it is both an accessibility requirement and a real field Bluesky clients render, not a cosmetic extra.

What happens if I attach a fifth image or an oversized file?

It is rejected before publish, not silently dropped. Check the post’s target status for a validation error rather than assuming a partial image set went out — partial results are reported per target through the delivery lifecycle, the same as on every other platform.

Build it with PostGate

One API for every social platform your customers care about — sandboxed, documented, and ready today.

Start building Read the docs