API and developers

Social Media API Bulk Posting Guide (2026)

Posting to dozens of clients across multiple platforms every day is painful when you're doing it manually. Here's how agencies automate bulk posting with the OmniSocials API.

Robert LigthartFounder of OmniSocialsPublished Updated 12 min read
Social Media API Bulk Posting Guide (2026)

If you're running social media for more than two or three clients, manual posting stops working fast. Clicking through dashboards, copy-pasting captions, switching between accounts. It adds up to hours every week that shouldn't exist.

On this page
  1. What Is Social Media API Bulk Posting?
  2. Why Native Platform APIs Don't Scale for Bulk Operations
  3. How to Bulk Post to Social Media via API
  4. Step 1: Build Your Posts Array
  5. Step 2: Send the Posts in a Loop
  6. Step 3: Python Version
  7. Step 4: Handle Responses and Errors
  8. Bulk Posting with Media Uploads
  9. Building an Agency Bulk Posting Pipeline
  10. API Comparison: Bulk Posting Across Providers
  11. Frequently Asked Questions
  12. Sources

Social media API bulk posting is how agencies solve this. One script. Hundreds of posts. Every platform covered.

This guide shows you exactly how to do it with the OmniSocials API, including working code examples you can drop into your own scripts today.


What Is Social Media API Bulk Posting?

Social media API bulk posting is the process of creating many social media posts programmatically, from a script or pipeline, rather than one at a time in a dashboard. Agencies use it to schedule weeks of content for multiple clients in minutes, without touching a dashboard.

The alternative, using native platform APIs directly, is painful. Instagram's Graph API requires separate OAuth tokens per user, Facebook has its own approval review process, LinkedIn enforces strict rate limits, and none of them share a common schema. Building a bulk posting system on top of raw platform APIs means maintaining integrations with 13 different systems, each with its own quirks and breaking changes.

OmniSocials solves this with a single unified API. Connect your accounts once in the dashboard, get one API key, and you're posting everywhere.


Why Native Platform APIs Don't Scale for Bulk Operations

Here's what bulk posting looks like if you try to build it yourself on native platform APIs:

TaskNative APIsOmniSocials API
Task: OAuth setupNative APIs: Separate OAuth flow per platformOmniSocials API: One API key, done
Task: Post to 5 platformsNative APIs: 5 separate API calls with different schemasOmniSocials API: 1 request, accounts array
Task: Schedule a postNative APIs: Platform-specific scheduling (Instagram: published_time, LinkedIn: specificContent)OmniSocials API: schedule_at field, same for all
Task: Handle mediaNative APIs: Upload to each platform separately, get different media IDsOmniSocials API: Upload once to OmniSocials, it handles the rest
Task: Bulk 50 postsNative APIs: 250 API calls (50 posts on 5 platforms), multiple auth tokensOmniSocials API: 50 requests to POST /v1/posts from one loop, one key per workspace
Task: Get statusNative APIs: Poll 5 different APIsOmniSocials API: Single GET /v1/posts/:id

I've worked with agencies that had custom scripts hitting native APIs directly. Every platform update broke something. With OmniSocials, that maintenance burden disappears.


How to Bulk Post to Social Media via API

You'll need an OmniSocials account with your social accounts connected, and your API key from Settings > API. The 14-day free trial gives you full API access.

Step 1: Build Your Posts Array

Each post in the array follows the POST /v1/posts schema: content, media_urls (optional, public image or video URLs), accounts, and schedule_at (optional: leave it out and the post is saved as a draft, it does not publish).

An API key belongs to one workspace, and a workspace holds one account per platform, so an agency gives each client its own workspace and API key. Tag each post with the client it belongs to:

const clientKeys = {
  clientA: process.env.OMNISOCIALS_KEY_CLIENT_A, // omsk_live_...
  clientB: process.env.OMNISOCIALS_KEY_CLIENT_B,
};

const posts = [
  {
    client: "clientA",
    content: "Client A, Monday post: New product launch! 🚀",
    media_urls: ["https://cdn.yoursite.com/client-a/product-launch.jpg"],
    accounts: ["instagram", "facebook", "linkedin"],
    schedule_at: "2027-03-01T09:00:00Z"
  },
  {
    client: "clientA",
    content: "Client A, Wednesday post: Behind the scenes.",
    media_urls: ["https://cdn.yoursite.com/client-a/behind-the-scenes.jpg"],
    accounts: ["instagram", "threads"],
    schedule_at: "2027-03-03T11:00:00Z"
  },
  {
    client: "clientB",
    content: "Client B, Monday post: Tips for Q2 planning.",
    accounts: ["linkedin", "bluesky"],
    schedule_at: "2027-03-01T08:00:00Z"
  }
];

You can mix clients, platforms, and schedules in a single array. Your script picks the right key per post.

Step 2: Send the Posts in a Loop

There is no bulk endpoint. Each POST /v1/posts request creates one post (for as many platforms as you list), so the script loops over the array. The rate limit is 100 requests per minute per API key: pace the loop, and on a 429 wait for the Retry-After seconds before trying again.

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function createPost(apiKey, post) {
  while (true) {
    const res = await fetch('https://api.omnisocials.com/v1/posts', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(post),
    });
    if (res.status === 429) {
      await sleep(Number(res.headers.get('Retry-After') || 1) * 1000);
      continue;
    }
    const body = await res.json();
    return res.ok ? { data: body.data } : { error: body.error };
  }
}

const results = [];
for (const { client, ...post } of posts) {
  results.push(await createPost(clientKeys[client], post));
  await sleep(700); // about 85 requests a minute, under the limit
}

That loop creates 3 posts on 7 platform/account combinations with 3 requests. With native APIs, that's 7 separate calls with different auth headers and schemas.

Step 3: Python Version

import os
import time
import requests

client_keys = {
    "client_a": os.environ["OMNISOCIALS_KEY_CLIENT_A"],
    "client_b": os.environ["OMNISOCIALS_KEY_CLIENT_B"],
}

posts = [
    {
        "client": "client_a",
        "content": "Client A, Monday post: New product launch!",
        "media_urls": ["https://cdn.yoursite.com/client-a/product-launch.jpg"],
        "accounts": ["instagram", "facebook", "linkedin"],
        "schedule_at": "2027-03-01T09:00:00Z"
    },
    {
        "client": "client_b",
        "content": "Client B, Tips for Q2 planning.",
        "accounts": ["linkedin", "bluesky"],
        "schedule_at": "2027-03-01T08:00:00Z"
    }
]

def create_post(api_key, post):
    while True:
        response = requests.post(
            "https://api.omnisocials.com/v1/posts",
            headers={"Authorization": f"Bearer {api_key}"},
            json=post,
        )
        if response.status_code == 429:
            time.sleep(int(response.headers.get("Retry-After", 1)))
            continue
        body = response.json()
        return body.get("data"), body.get("error")

for item in posts:
    post = {key: value for key, value in item.items() if key != "client"}
    data, error = create_post(client_keys[item["client"]], post)
    if error:
        print(f"Failed: {error['code']}: {error['message']}")
    else:
        print(f"Post {data['id']}: {data['status']}")
    time.sleep(0.7)  # stays under 100 requests per minute

Step 4: Handle Responses and Errors

Each request returns HTTP 201 with the created post in data (its id and status, scheduled when you sent schedule_at), or a 4xx with { "error": { "code", "message" } }, for example 400 validation_error for a schedule_at in the past or 400 platform_not_connected for a platform the workspace hasn't connected. The results array from Step 2 keeps your input order:

results.forEach((result, index) => {
  if (result.error) {
    console.error(`Post ${index} failed: ${result.error.code}: ${result.error.message}`);
  } else {
    console.log(`Post ${index} ${result.data.status}: ${result.data.id}`);
  }
});

Set up webhooks to get notified when posts publish or fail. Polling hundreds of post IDs isn't practical at scale. Create one with POST /v1/webhooks; it can subscribe to post.scheduled, post.published, post.failed, post.approved, and post.rejected, and each delivery lists the per-platform results in data.targets.


Bulk Posting with Media Uploads

If your images already have a public URL, pass it in media_urls and OmniSocials downloads it. If you're generating images dynamically (client reports, data visualizations, product shots), upload them first via /v1/media/upload, then reference the returned media ID in the media field of your posts.

import { readFile } from 'node:fs/promises';
import { basename } from 'node:path';

// Upload media first (multipart field "file")
async function uploadMedia(apiKey, filePath) {
  const formData = new FormData();
  const file = new Blob([await readFile(filePath)], { type: 'image/png' });
  formData.append('file', file, basename(filePath));

  const res = await fetch('https://api.omnisocials.com/v1/media/upload', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${apiKey}` },
    body: formData,
  });

  const { data } = await res.json();
  return data.id; // Media ID for the `media` field
}

// Then use it in your posts
const mediaId = await uploadMedia(clientKeys.clientA, './client-report-week14.png');

const posts = [
  {
    client: "clientA",
    content: "Weekly performance report, week 14.",
    media: [mediaId],
    accounts: ["linkedin"],
    schedule_at: "2027-03-01T08:00:00Z"
  }
];

Upload with the same client's key you post with: media lives in that workspace's library.

OmniSocials resizes and reformats uploaded media per each platform's requirements. You upload once; it handles Instagram's 1:1 crop, LinkedIn's aspect ratio requirements, and Twitter's compression separately.


Building an Agency Bulk Posting Pipeline

Here's a pattern I've seen work well for agencies managing 10+ clients:

  1. Maintain a content calendar in a spreadsheet or CMS. Rows contain: client ID, copy, platform list, scheduled date, image path
  2. Run a script weekly that reads the calendar, uploads any new images, and loops over the full week's posts with one POST /v1/posts per post, using each client's API key
  3. Use webhooks to log results: write published/failed status back to your CMS or Slack
  4. Query /v1/analytics/overview with each client's key at the end of each month to pull engagement data per client

This reduces content scheduling from hours of dashboard work to a single script execution. The rate limit is 100 requests per minute per API key, and each request creates one post for several platforms, so a week of posts for a client goes out in a minute or two.


API Comparison: Bulk Posting Across Providers

Prices checked October 18, 2026, on each vendor's pricing page.

FeatureOmniSocials APIAyrshareBuffer APIPubler API
Feature: Price (billed yearly)OmniSocials API: $10 per seat, API includedAyrshare: Premium $129/mo ($149 billed monthly)Buffer API: $5 per channel (Essentials)Publer API: Business plan, from $8 per account
Feature: Pricing unitOmniSocials API: Seat (one per brand or extra teammate)Ayrshare: Social profileBuffer API: ChannelPubler API: Social account
Feature: PlatformsOmniSocials API: 13Ayrshare: 14, no MastodonBuffer API: 12Publer API: 12, plus WordPress
Feature: Multi-platform per postOmniSocials API: Yes (accounts array)Ayrshare: YesBuffer API: YesPubler API: Yes
Feature: Hosted MCP serverOmniSocials API: Yes, 56 toolsAyrshare: Yes, 33 toolsBuffer API: YesPubler API: Not checked

OmniSocials has no bulk endpoint either: scheduling 200 posts means 200 requests, which one loop sends in a little over two minutes at 100 requests per minute. What saves calls is that each request can target several platforms at once.


Frequently Asked Questions

What is social media API bulk posting?

Social media API bulk posting is the process of creating many social media posts programmatically, from a script or pipeline, rather than one at a time in a dashboard. Agencies use it to schedule weeks of content for multiple clients in minutes, without touching a dashboard.

Can I post to multiple platforms at once with an API?

Yes. The OmniSocials API accepts an accounts array in each post request, letting you publish to Instagram, LinkedIn, Facebook, TikTok, and 8 other platforms in one call. There is no bulk endpoint: for bulk operations, send one POST /v1/posts request per post from a loop.

How many posts can I bulk schedule at once?

Each OmniSocials API request creates one post, which can target several platforms at once. The rate limit is 100 requests per minute per API key, so one key can schedule up to 100 posts a minute, enough for most agency workflows.

Is there a free way to test social media bulk posting via API?

OmniSocials offers a 14-day free trial. The API is included in every plan. There's no separate developer tier. You get full API access from day one. There are no test keys, so test with drafts: leave out schedule_at and a post is saved as a draft that never publishes on its own.

What's the difference between bulk posting and normal API posting?

With OmniSocials there is no separate bulk endpoint: bulk posting is normal API posting run from a loop. Each POST /v1/posts request creates one post for one or more platforms, and a script sends as many as you need while staying under 100 requests per minute.


If you're building an agency workflow or automating content at scale, the full API reference is at docs.omnisocials.com. The 14-day trial gives you a real API key and full API access from the start.


Sources

Written byRobert LigthartFounder of OmniSocials

Try OmniSocials for 14 days

Plan, schedule and answer on 13 platforms from one calendar, or let your AI agent do it. The first charge comes when the trial ends.