JavaScript Media Upload Examples

View as Markdown

Upload images and videos to Publora using the pre-signed URL workflow.

How Media Upload Works

  1. Create the post first using POST /create-post (returns a postGroupId)
  2. Get a pre-signed upload URL using POST /get-upload-url with the postGroupId
  3. Upload the file directly to the returned S3 URL
  4. Media is automatically attached to the post via the postGroupId
  5. 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'
);
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