Guides
Media Uploads
How to upload images, videos, and documents for use in posts
Posts with media perform better on every platform. Late uses presigned URLs for fast, direct uploads up to 5GB.
Upload Flow
- Request a presigned URL from
POST /v1/media/presign - Upload the file directly to the returned
uploadUrlusing a PUT request - Use the
publicUrlin your post'smediaItemsarray
Step 1: Get a Presigned URL
const { uploadUrl, publicUrl } = await late.media.getMediaPresignedUrl({
fileName: 'photo.jpg',
fileType: 'image/jpeg'
});result = client.media.get_presigned_url(
file_name="photo.jpg",
file_type="image/jpeg"
)
upload_url = result.upload_url
public_url = result.public_urlcurl -X POST "https://getlate.dev/api/v1/media/presign" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fileName": "photo.jpg",
"fileType": "image/jpeg"
}'Response:
{
"uploadUrl": "https://storage.googleapis.com/...",
"publicUrl": "https://storage.googleapis.com/...",
"expires": "2024-01-15T11:00:00.000Z"
}Step 2: Upload the File
Upload directly to the presigned URL (no auth header needed):
// Upload directly to the presigned URL (no auth needed)
await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': 'image/jpeg' },
body: fileBuffer
});import httpx
# Upload directly to the presigned URL (no auth needed)
with open("photo.jpg", "rb") as f:
httpx.put(upload_url, content=f.read(), headers={"Content-Type": "image/jpeg"})curl -X PUT "UPLOAD_URL_FROM_STEP_1" \
-H "Content-Type: image/jpeg" \
--data-binary @photo.jpgStep 3: Use in a Post
Include the publicUrl in your post:
const { post } = await late.posts.createPost({
content: 'Check out this photo!',
mediaItems: [
{ url: publicUrl, type: 'image' }
],
platforms: [
{ platform: 'twitter', accountId: 'acc_xyz789' }
]
});result = client.posts.create(
content="Check out this photo!",
media_items=[
{"url": public_url, "type": "image"}
],
platforms=[
{"platform": "twitter", "accountId": "acc_xyz789"}
]
)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": "PUBLIC_URL_FROM_STEP_1", "type": "image" }
],
"platforms": [
{ "platform": "twitter", "accountId": "acc_xyz789" }
]
}'See the Presigned Upload endpoint for full parameter details.
Supported Formats
| Type | Formats | Max Size |
|---|---|---|
| Images | JPG, PNG, GIF, WebP | 5 GB |
| Videos | MP4, MOV, AVI, WebM | 5 GB |
| Documents | PDF (LinkedIn only) | 100 MB |
Platform-Specific Media Rules
Each platform has its own requirements for media. Here's a quick reference:
| Platform | Max Images | Max Videos | Notes |
|---|---|---|---|
| 4 | 1 | No mixing images and videos | |
| 10 (carousel) | 1 (Reel) | Stories: single image/video | |
| Multiple | 1 | Stories: single image/video | |
| 20 images | 1 | Single PDF supported (max 300 pages) | |
| TikTok | 35 photos | 1 | No mixing photos and videos |
| YouTube | - | 1 (required) | Optional custom thumbnail |
| 1 | 1 | One image or one video per Pin | |
| Bluesky | 4 | 1 | Images auto-compressed to ~1MB |
| Threads | 10 images | 1 | No video carousels |
| Snapchat | 1 | 1 | Required for all post types |
For detailed platform requirements, see the Platforms section or the Create Post endpoint.
Auto-Compression
Some platforms have strict file size limits. Late handles this automatically:
- Bluesky: Images are automatically recompressed to stay under Bluesky's ~1MB blob limit
- YouTube Thumbnails: Custom thumbnails via
MediaItem.thumbnailare processed to meet YouTube's requirements
Custom Media Per Platform
You can use different media for different platforms in the same post using customMedia in the platform entry:
{
"content": "Same text, different media per platform",
"mediaItems": [{ "url": "default-image.jpg", "type": "image" }],
"platforms": [
{ "platform": "twitter", "accountId": "acc_1" },
{
"platform": "instagram",
"accountId": "acc_2",
"customMedia": [{ "url": "square-image.jpg", "type": "image" }]
}
]
}