Platforms
Bluesky API
Post to Bluesky with Late API - text posts, images, and videos
Quick Start
Post to Bluesky in under 60 seconds:
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Hello from Late API! 🦋",
"platforms": [
{"platform": "bluesky", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Hello from Late API! 🦋',
platforms: [
{ platform: 'bluesky', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
})
});
const { post } = await response.json();
console.log('Posted to Bluesky!', post._id);import requests
response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': 'Hello from Late API! 🦋',
'platforms': [
{'platform': 'bluesky', 'accountId': 'YOUR_ACCOUNT_ID'}
],
'publishNow': True
}
)
post = response.json()
print(f"Posted to Bluesky! {post['_id']}")Authentication
Bluesky uses App Passwords instead of OAuth. To connect a Bluesky account:
- Go to your Bluesky Settings > App Passwords
- Create a new App Password
- Use the connect endpoint with your handle and app password
curl -X POST https://getlate.dev/api/v1/connect/bluesky \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "YOUR_PROFILE_ID",
"handle": "yourhandle.bsky.social",
"appPassword": "xxxx-xxxx-xxxx-xxxx"
}'const response = await fetch('https://getlate.dev/api/v1/connect/bluesky', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
profileId: 'YOUR_PROFILE_ID',
handle: 'yourhandle.bsky.social',
appPassword: 'xxxx-xxxx-xxxx-xxxx'
})
});
const account = await response.json();
console.log('Connected:', account._id);response = requests.post(
'https://getlate.dev/api/v1/connect/bluesky',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'profileId': 'YOUR_PROFILE_ID',
'handle': 'yourhandle.bsky.social',
'appPassword': 'xxxx-xxxx-xxxx-xxxx'
}
)
account = response.json()
print(f"Connected: {account['_id']}")Image Requirements
| Property | Requirement |
|---|---|
| Max Images | 4 per post |
| Formats | JPEG, PNG, WebP, GIF |
| Max File Size | 1 MB per image |
| Max Dimensions | 2000 × 2000 px |
| Recommended | 1200 × 675 px (16:9) |
Aspect Ratios
| Type | Ratio | Dimensions |
|---|---|---|
| Landscape | 16:9 | 1200 × 675 px |
| Square | 1:1 | 1000 × 1000 px |
| Portrait | 4:5 | 800 × 1000 px |
Post with Image
curl -X POST https://getlate.dev/api/v1/posts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Check out this photo! 📸",
"mediaItems": [
{"url": "https://example.com/photo.jpg"}
],
"platforms": [
{"platform": "bluesky", "accountId": "YOUR_ACCOUNT_ID"}
],
"publishNow": true
}'const response = await fetch('https://getlate.dev/api/v1/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Check out this photo! 📸',
mediaItems: [
{ url: 'https://example.com/photo.jpg' }
],
platforms: [
{ platform: 'bluesky', accountId: 'YOUR_ACCOUNT_ID' }
],
publishNow: true
})
});response = requests.post(
'https://getlate.dev/api/v1/posts',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'content': 'Check out this photo! 📸',
'mediaItems': [
{'url': 'https://example.com/photo.jpg'}
],
'platforms': [
{'platform': 'bluesky', 'accountId': 'YOUR_ACCOUNT_ID'}
],
'publishNow': True
}
)Video Requirements
| Property | Requirement |
|---|---|
| Max Videos | 1 per post |
| Formats | MP4 |
| Max File Size | 50 MB |
| Max Duration | 60 seconds |
| Max Dimensions | 1920 × 1080 px |
| Frame Rate | 30 fps recommended |
Recommended Video Specs
| Property | Recommended |
|---|---|
| Resolution | 1280 × 720 px (720p) |
| Aspect Ratio | 16:9 (landscape) or 1:1 (square) |
| Frame Rate | 30 fps |
| Codec | H.264 |
| Audio | AAC |
Character Limits
- Post text: 300 characters
- Alt text for images: 1000 characters
Bluesky automatically detects and renders:
- URLs as link cards
- Mentions (@handle.bsky.social)
- Hashtags
Link Cards
When your post contains a URL, Bluesky automatically generates a link card preview. For best results:
- Place the URL at the end of your post
- Ensure the target page has proper Open Graph meta tags
- The link card includes title, description, and thumbnail
Common Issues
Image Too Large
Bluesky has a strict 1 MB limit per image. Compress images before upload or Late will attempt automatic compression.
App Password Invalid
- Ensure you're using an App Password, not your main account password
- App Passwords are formatted as
xxxx-xxxx-xxxx-xxxx - Create a new App Password if the current one isn't working
Post Too Long
Bluesky has a 300 character limit. If your post exceeds this, consider:
- Shortening URLs (Bluesky shows link cards anyway)
- Splitting into multiple posts
- Moving detailed content to a linked page
Related API Endpoints
- Connect Bluesky Account — App Password authentication
- Create Post — Post creation and scheduling
- Upload Media — Image and video uploads