Skip to main content

TL;DR / Key Takeaways

  • Manual multi-platform posting caps at ~50 videos/day. Beyond that, you need automation.
  • bundle.social API: upload once, distribute to 14 platforms with one request.
  • No per-account pricing - connect 10 channels or 1,000, same flat rate.
  • Resumable uploads for videos up to 1GB - no restart on failure.
  • Real use cases: content agencies, multi-channel networks, UGC platforms, AI content generators.
You have 200 videos. You need them on TikTok, Instagram Reels, YouTube Shorts, LinkedIn, and Twitter. By tomorrow. Container ships at port - bulk content distribution One organization, thousands of accounts. Content distribution at scale. If you’re doing this manually, that’s 1,000 uploads. At 3 minutes each (login, upload, caption, hashtags, publish), you’re looking at 50 hours of work. For one batch. This is the problem bulk upload solves. Not “scheduling posts in advance” - actual programmatic content distribution at scale. I’ve seen teams try three approaches: manual grinding (burns out in weeks), building their own integrations (burns out in months when APIs change), or using a unified API that handles the complexity. Only one scales.

Who Actually Needs Bulk Upload

Before diving into architecture, let’s be clear about who this is for: Multi-Channel Networks (MCNs) You manage 50+ creator channels. Each creator produces 3-5 videos per week. You need to distribute across platforms with consistent branding but channel-specific customization. Content Agencies You run social media for 100+ clients. Each client has 5+ connected accounts. Content comes from various sources - client assets, UGC, AI generation - and needs to go everywhere, fast. UGC Platforms You aggregate user-generated content and redistribute it. Think compilation channels, reaction content, or curated feeds. Volume is measured in hundreds of pieces per day. AI Content Generators You’re building tools that generate videos - AI avatars, text-to-video, automated editing. The creation is automated. The distribution should be too. E-commerce at Scale You have thousands of products. Each product needs video content across social platforms. Manual posting isn’t an option when you’re adding 100 new SKUs per week. If none of these describe you, bulk upload might be overkill. If any of them do, keep reading.

The Architecture: One Upload, Many Destinations

Here’s how bulk distribution works with bundle.social:
[Your Content] → [Upload to CDN] → [Create Multi-Platform Post] → [14 Platforms]
                      ↓                        ↓
                 uploadId              Platform-specific data
Step 1: Upload your video once. We store it on our CDN and return an uploadId. Step 2: Create a post using that uploadId for as many platforms as you want. One API call. Step 3: We handle OAuth, rate limits, format requirements, and platform quirks. You get webhook notifications when posts publish.

The Code

Here’s what bulk distribution actually looks like:
import { BundleSocial } from 'bundlesocial';
import fs from 'fs';

const client = new BundleSocial({ apiKey: process.env.BUNDLE_API_KEY });

async function distributeVideo(videoPath: string, metadata: VideoMetadata) {
  // Step 1: Upload once
  const upload = await client.upload.create({
    teamId: metadata.teamId,
    file: fs.createReadStream(videoPath)
  });

  // Step 2: Distribute to all platforms
  const post = await client.post.create({
    teamId: metadata.teamId,
    title: metadata.title,
    postDate: metadata.scheduledTime,
    status: 'SCHEDULED',
    socialAccountTypes: ['TIKTOK', 'INSTAGRAM', 'YOUTUBE', 'LINKEDIN', 'TWITTER'],
    data: {
      TIKTOK: {
        type: 'VIDEO',
        text: `${metadata.caption} ${metadata.tiktokHashtags}`,
        uploadIds: [upload.id],
        privacy: 'PUBLIC_TO_EVERYONE',
        disableComments: false,
        disableDuet: false,
        disableStitch: false
      },
      INSTAGRAM: {
        type: 'REEL',
        text: `${metadata.caption} ${metadata.instagramHashtags}`,
        uploadIds: [upload.id],
        shareToFeed: true
      },
      YOUTUBE: {
        type: 'SHORT',
        text: metadata.youtubeTitle,
        description: metadata.youtubeDescription,
        uploadIds: [upload.id],
        privacy: 'public',
        madeForKids: false
      },
      LINKEDIN: {
        text: metadata.linkedinCaption,
        uploadIds: [upload.id]
      },
      TWITTER: {
        text: metadata.twitterCaption,
        uploadIds: [upload.id]
      }
    }
  });

  return post;
}
Two API calls. Five platforms. One video distributed everywhere with platform-specific captions and settings.

Processing 1,000 Videos: The Batch Approach

For serious volume, you need batch processing. Here’s a production-ready pattern:
import pLimit from 'p-limit';

const limit = pLimit(10); // 10 concurrent uploads

async function bulkDistribute(videos: VideoJob[]) {
  const results = await Promise.all(
    videos.map(video => 
      limit(async () => {
        try {
          const post = await distributeVideo(video.path, video.metadata);
          return { success: true, videoId: video.id, postId: post.id };
        } catch (error) {
          return { success: false, videoId: video.id, error: error.message };
        }
      })
    )
  );

  const succeeded = results.filter(r => r.success).length;
  const failed = results.filter(r => !r.success);
  
  console.log(`Distributed ${succeeded}/${videos.length} videos`);
  if (failed.length > 0) {
    console.log('Failed:', failed);
  }
  
  return results;
}
Key points:
  • Concurrency limiting prevents overwhelming the API
  • Error isolation means one failure doesn’t stop the batch
  • Result tracking tells you exactly what succeeded and what needs retry

Handling Large Files: Resumable Uploads

Videos fail mid-upload. Networks drop. Servers time out. When you’re uploading a 500MB file, you don’t want to start over at 99%. bundle.social supports resumable uploads for videos up to 1GB:
async function uploadLargeVideo(filePath: string, teamId: string) {
  // Step 1: Initialize upload
  const init = await client.upload.init({
    fileName: path.basename(filePath),
    mimeType: 'video/mp4',
    teamId
  });

  // Step 2: Upload to pre-signed URL
  const fileBuffer = fs.readFileSync(filePath);
  await fetch(init.url, {
    method: 'PUT',
    body: fileBuffer,
    headers: { 'Content-Type': 'video/mp4' }
  });

  // Step 3: Finalize
  const upload = await client.upload.finalize({
    path: init.path,
    teamId
  });

  return upload.id;
}
If the upload fails at step 2, you can resume from where you left off. No re-uploading 400MB because the last 100MB timed out.

Platform Rate Limits: What You Can Actually Post

Each platform has daily limits. Here’s the reality at bundle.social’s Business tier:
PlatformDaily Limit Per Account
TikTok15 videos
Instagram25 posts
YouTube15 videos
LinkedIn24 posts
Twitter/X15 posts
Facebook36 posts
Threads250 posts
Pinterest36 pins
The math for multi-channel operations: If you manage 100 TikTok channels at 15 posts/day each, that’s 1,500 TikTok posts daily. Same video can go to each channel with one upload. If each video also goes to Instagram, YouTube, LinkedIn, and Twitter, you’re distributing 7,500 posts per day from 1,500 source videos.
Important: These are per-account limits, not API limits. Connect unlimited accounts - we don’t charge per connection. Check rate limits for current numbers.

Webhook Integration: Know When Posts Publish

At scale, you can’t manually check each post. Use webhooks for real-time status:
// Your webhook endpoint
app.post('/webhooks/bundle', (req, res) => {
  const event = req.body;

  switch (event.type) {
    case 'post.published':
      // Post went live
      await db.posts.update(event.postId, {
        status: 'published',
        permalink: event.permalink,
        externalId: event.externalId,
        publishedAt: event.publishedAt
      });
      break;

    case 'post.failed':
      // Something went wrong
      await db.posts.update(event.postId, {
        status: 'failed',
        error: event.error
      });
      await alertOps(`Post ${event.postId} failed: ${event.error}`);
      break;
  }

  res.status(200).send('OK');
});
Every post success and failure gets pushed to your system. Build dashboards, trigger alerts, update your database - all automatically.

The Economics: Build vs. Buy vs. API

Let’s do the math on three approaches:

Option 1: Build Your Own Integrations

Initial development:
  • TikTok API integration: 60-80 hours
  • Instagram Graph API: 40-60 hours
  • YouTube Data API: 40-60 hours
  • LinkedIn API: 30-40 hours
  • Twitter API: 20-30 hours
Total: 190-270 hours × 100/hour=100/hour = **19,000 - $27,000** initial build Ongoing maintenance:
  • API changes, token refresh, rate limit handling
  • Estimate: 10-20 hours/month × 100=100 = **1,000 - $2,000/month**
Timeline: 3-6 months to production-ready

Option 2: Use 5 Different SaaS Tools

  • TikTok scheduler: $50-200/month
  • Instagram tool: $50-200/month
  • YouTube tool: $50-200/month
  • LinkedIn tool: $50-100/month
  • Twitter tool: $50-100/month
Total: 250250 - 800/month + manual coordination overhead Problem: No unified API. You’re still managing 5 dashboards, 5 auth systems, 5 billing relationships.

Option 3: Unified API (bundle.social)

  • One integration
  • One billing relationship
  • 14 platforms
  • No per-account fees
  • Flat monthly pricing based on usage
Pricing:
  • Pro: $100/month for 1,000 posts
  • Business: $400/month for 100,000 posts
  • Enterprise: Custom pricing
ROI calculation: If you’re posting 1,000 videos/month across 5 platforms (5,000 posts), and manual posting takes 3 minutes each:
  • Manual time: 250 hours/month
  • At 50/hourlabor:50/hour labor: 12,500/month in posting labor alone
API cost is a fraction of that, and scales without adding headcount.

Real Architecture: Content Distribution Center

Here’s how a production content distribution system works: Content Sources feed into your system - client assets, UGC platforms, AI generators, agency editors. All content flows into a central Content Queue where each job contains video URL, metadata, target platforms, and schedule. A Distribution Worker processes the queue: fetches video from source, uploads to bundle.social CDN, creates multi-platform post, stores post IDs for tracking. The bundle.social API handles the platform complexity - TikTok, Instagram, YouTube, LinkedIn, and 10 more platforms. Each with different auth, formats, and quirks. Webhooks close the loop: post.published updates your database and notifies clients, post.failed triggers retry queue and alerts ops. This architecture handles thousands of posts daily. The complexity is in your business logic (what goes where, when, with what captions). The platform complexity is bundle.social’s problem.

Channel Management at Scale

When you’re managing hundreds of channels, organization matters:
// Structure: Organization → Teams → Social Accounts
const org = {
  id: 'org_mcn_network',
  teams: [
  {
    id: 'team_creator_alice',
    name: 'Alice (Gaming)',
    socialAccounts: [
      { platform: 'TIKTOK', username: 'alicegaming' },
      { platform: 'YOUTUBE', channelId: 'UC_AliceGamingTV' },
      { platform: 'INSTAGRAM', username: 'alice.games' }
    ]
  },
  {
    id: 'team_creator_bob',
    name: 'Bob (Fitness)',
    socialAccounts: [
      { platform: 'TIKTOK', username: 'bobfitness' },
      { platform: 'YOUTUBE', channelId: 'UC_BobFitnessOfficial' },
      { platform: 'INSTAGRAM', username: 'bob.fitness' }
    ]
  }
    // ... 200 more creators
  ]
};
Each team has isolated social accounts. One API key for your organization, granular control per creator.

Common Patterns

Pattern 1: Same Video, Different Captions

const platformCaptions = {
  TIKTOK: `${baseCaption} #fyp #viral #trending`,
  INSTAGRAM: `${baseCaption} #reels #instagram #explore`,
  YOUTUBE: baseCaption, // YouTube doesn't do hashtags in descriptions the same way
  LINKEDIN: `${professionalCaption}\n\n#business #growth`,
  TWITTER: shortCaption // 280 char limit
};

Pattern 2: Staggered Posting

Don’t post to all platforms at the exact same time. Stagger for maximum reach:
const staggeredSchedule = {
  TIKTOK: baseTime, // Primary platform first
  INSTAGRAM: addMinutes(baseTime, 30),
  YOUTUBE: addHours(baseTime, 2),
  LINKEDIN: addHours(baseTime, 4), // Business hours matter
  TWITTER: addMinutes(baseTime, 15)
};

Pattern 3: A/B Testing Captions

const variants = [
  { caption: 'Question hook: Did you know...?', percentage: 50 },
  { caption: 'Statement hook: This changed everything.', percentage: 50 }
];

// Track which variant performs better per platform

Getting Started

If you’re ready to build a content distribution engine:
  1. Start small. Pick your highest-volume platform (usually TikTok or Instagram). Get that working first.
  2. Add platforms incrementally. Once one works, adding others is just expanding the data object.
  3. Build monitoring from day one. Webhook integration, error tracking, success rates. You need visibility.
  4. Plan for failure. Retry queues, error alerting, manual fallback for critical posts.

API Documentation

Full API reference with all endpoints

Upload Guide

Simple and resumable upload methods

Platform Specs

Requirements for each of 14 platforms

SDK

TypeScript SDK for faster development

Questions about bulk upload architecture? Building something at scale? Check the docs or reach out. We’ve helped teams go from zero to thousands of posts per day.