JavaScript Media Upload Examples
Upload images and videos to Publora using the pre-signed URL workflow.
How Media Upload Works
- Create the post first using
POST /create-post(returns apostGroupId) - Get a pre-signed upload URL using
POST /get-upload-urlwith thepostGroupId - Upload the file directly to the returned S3 URL
- Media is automatically attached to the post via the
postGroupId - Schedule the draft with
PUT /update-post/{postGroupId}after uploading. Attaching media to an already-scheduled post demotes it to draft (postGroupDemoted: true), so schedule it again after the final attachment.
Upload a Single Image
const PUBLORA_API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api.publora.com/api/v1';
const fs = require('fs');
async function scheduleUploadedPost(postGroupId, headers) {
const response = await fetch(`${BASE_URL}/update-post/${postGroupId}`, {
method: 'PUT',
headers,
body: JSON.stringify({
status: 'scheduled',
scheduledTime: new Date(Date.now() + 5 * 60 * 1000).toISOString()
})
});
if (!response.ok) throw new Error(`Scheduling failed: ${await response.text()}`);
}
async function createPostWithImage(content, platforms, imagePath, fileName, contentType) {
const headers = {
'Content-Type': 'application/json',
'x-publora-key': PUBLORA_API_KEY
};
// Step 1: Create the post
const postResponse = await fetch(`${BASE_URL}/create-post`, {
method: 'POST',
headers,
body: JSON.stringify({ content, platforms })
});
const { postGroupId } = await postResponse.json();
console.log('Post created:', postGroupId);
// Step 2: Get pre-signed upload URL
const urlResponse = await fetch(`${BASE_URL}/get-upload-url`, {
method: 'POST',
headers,
body: JSON.stringify({ fileName, contentType, postGroupId })
});
const { uploadUrl, fileUrl, mediaId } = await urlResponse.json();
// Step 3: Upload file to S3
const fileBuffer = await fs.promises.readFile(imagePath);
await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': contentType },
body: fileBuffer
});
console.log('Uploaded! File URL:', fileUrl);
console.log('Media ID:', mediaId);
// Step 4: Schedule after the media attachment is complete
await scheduleUploadedPost(postGroupId, headers);
return { postGroupId, fileUrl, mediaId };
}
// Usage
await createPostWithImage(
'Check out our new feature!',
['twitter-123456', 'linkedin-ABC123'],
'./product-screenshot.png',
'product-screenshot.png',
'image/png'
);Upload Multiple Images (Carousel)
async function createCarouselPost(content, platforms, images) {
const headers = {
'Content-Type': 'application/json',
'x-publora-key': PUBLORA_API_KEY
};
// Step 1: Create the post
const postResponse = await fetch(`${BASE_URL}/create-post`, {
method: 'POST',
headers,
body: JSON.stringify({ content, platforms })
});
const { postGroupId } = await postResponse.json();
console.log('Post created:', postGroupId);
// Step 2: Upload each image to the same postGroupId
for (const image of images) {
const urlResponse = await fetch(`${BASE_URL}/get-upload-url`, {
method: 'POST',
headers,
body: JSON.stringify({
fileName: image.name,
contentType: image.contentType,
postGroupId
})
});
const { uploadUrl, mediaId } = await urlResponse.json();
const fileBuffer = await fs.promises.readFile(image.path);
await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': image.contentType },
body: fileBuffer
});
console.log(`Uploaded ${image.name} (mediaId: ${mediaId})`);
}
// Step 3: Schedule only after the final image is attached
await scheduleUploadedPost(postGroupId, headers);
return postGroupId;
}
// Usage: Post a carousel to Instagram
await createCarouselPost(
'5 tips for better productivity. Swipe through!',
['instagram-789012'],
[
{ path: './tip1.jpg', name: 'tip1.jpg', contentType: 'image/jpeg' },
{ path: './tip2.jpg', name: 'tip2.jpg', contentType: 'image/jpeg' },
{ path: './tip3.jpg', name: 'tip3.jpg', contentType: 'image/jpeg' },
{ path: './tip4.jpg', name: 'tip4.jpg', contentType: 'image/jpeg' },
{ path: './tip5.jpg', name: 'tip5.jpg', contentType: 'image/jpeg' }
]
);Upload Video
async function createVideoPost(content, platforms, videoPath, fileName) {
const headers = {
'Content-Type': 'application/json',
'x-publora-key': PUBLORA_API_KEY
};
// Step 1: Create the post
const postResponse = await fetch(`${BASE_URL}/create-post`, {
method: 'POST',
headers,
body: JSON.stringify({ content, platforms })
});
const { postGroupId } = await postResponse.json();
// Step 2: Get upload URL for video
const urlResponse = await fetch(`${BASE_URL}/get-upload-url`, {
method: 'POST',
headers,
body: JSON.stringify({
fileName,
contentType: 'video/mp4',
postGroupId
})
});
const { uploadUrl, fileUrl, mediaId } = await urlResponse.json();
// Step 3: Upload video file
const videoBuffer = await fs.promises.readFile(videoPath);
await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': 'video/mp4' },
body: videoBuffer
});
console.log('Video uploaded:', fileUrl);
// Step 4: Schedule only after the video is attached
await scheduleUploadedPost(postGroupId, headers);
return { postGroupId, fileUrl, mediaId };
}
// Post video as Instagram Reel
await createVideoPost(
'Behind the scenes of building our product! #buildinpublic',
['instagram-789012'],
'./bts-video.mp4',
'bts-video.mp4'
);
// Post video to TikTok
await createVideoPost(
'Quick tip for developers #coding #devtips',
['tiktok-345678'],
'./tip-video.mp4',
'tip-video.mp4'
);Upload with Progress (Node.js)
const fs = require('fs');
async function uploadWithProgress(postGroupId, filePath, fileName, contentType) {
const headers = {
'Content-Type': 'application/json',
'x-publora-key': PUBLORA_API_KEY
};
// Get upload URL
const urlResponse = await fetch(`${BASE_URL}/get-upload-url`, {
method: 'POST',
headers,
body: JSON.stringify({ fileName, contentType, postGroupId })
});
const { uploadUrl, mediaId } = await urlResponse.json();
// Get file stats for progress tracking
const stats = fs.statSync(filePath);
const fileSize = stats.size;
let uploaded = 0;
// Create read stream
const fileStream = fs.createReadStream(filePath);
fileStream.on('data', (chunk) => {
uploaded += chunk.length;
const percent = Math.round((uploaded / fileSize) * 100);
process.stdout.write(`\rUploading: ${percent}%`);
});
// Upload with stream
const fileBuffer = await fs.promises.readFile(filePath);
await fetch(uploadUrl, {
method: 'PUT',
headers: {
'Content-Type': contentType,
'Content-Length': fileSize.toString()
},
body: fileBuffer
});
console.log('\nUpload complete!');
return mediaId;
}Publora — Social media API with free tier, paid plans from $2.99/account