Creator OS
automationbulk-schedulingcsvrest-apisocial-media-api

Bulk Schedule Social Posts With a REST API in 2026

Turn a CSV into queued posts across every account using one REST API call. See the exact schema, code, and error handling, then start scheduling without a UI.

Creator OS · November 1, 2026 · 10 min read

You bulk schedule social posts with a REST API by sending one POST request per row of your CSV to /v1/posts, passing content, platforms, schedule_at and timezone, with a Creator OS API key in the Authorization header. The API applies each platform’s rules, returns a post_ id per row, and reports any platforms you are not connected to in missing_platforms. Get the base URL from the docs.

  calendar.csv
      |
      v
  [ your script ]  -- reads row, builds JSON, adds header
      |
      v
  POST /v1/posts  x 400   (Authorization: Bearer cos_live_...)
      |
      +-- 200  post_abc123   queued for the time you set
      +-- 200  post_abc124   missing_platforms: ["tiktok"]
      +-- 4xx  { error: { code, message, status } }
      |
      v
  creatoros posts:list --status scheduled   (spot check)
CSV to queue: one request per row, one id back per request.

What does a social post API actually do that the UI cannot

The API lets a script create a queue that no dashboard was designed to hold, and it stays inside one workspace by design.

In a UI you build a month of posts by hand, form by form, account by account. Through the REST API you describe the same posts as data. That matters when the posts already live somewhere else: a content calendar in a spreadsheet, rows exported from a planning doc, or output from a script that drafts captions.

A few things only the API side gives you:

  • One workspace per key. Each Creator OS API key (cos_live_...) is pinned to one workspace, which is one set of socials. An agent or script holding one client’s key cannot post to another client. Agency plans let you create a scoped key per client workspace.
  • Advanced form per platform. Instead of one caption for everything, platforms can be an array of objects with platform, account_id, options and content, so the LinkedIn version and the Instagram version of the same idea travel in one request.
  • Media uploaded once. POST /v1/media returns a med_ id you reuse on every platform instead of re-uploading the file per network.
  • A machine-readable failure. Errors come back as { error: { code, message, status } }, which is far easier to log and retry than a red toast.

If you would rather do the whole month from a file without writing code, the CSV walkthrough in bulk schedule a month of social posts from a CSV covers the same job through the CLI.

Get started for $0

Bulk schedule social posts from a CSV: the 5 step workflow

Five steps: shape the CSV, upload media once, loop the rows, collect ids, then verify in the queue.

  1. Shape the CSV. One row per post, one column for each field the API expects. Keep times in ISO 8601 and put the timezone in its own column so you never guess.
  2. Upload media first. For every image or video that appears in the file, call POST /v1/media and record the returned med_ id. Now the row only carries an id, not a 40 MB file.
  3. Loop and POST. For each row, build the JSON body and send it to /v1/posts with your key in the header.
  4. Record what came back. Write the post_ id back into the CSV, plus any missing_platforms. That file becomes your audit trail.
  5. Verify. Run creatoros posts:list --status scheduled --from <ISO> --to <ISO> and compare the count to your CSV row count.

Step 3 is the only part that needs care. Steps 1 and 2 are what stop it from going wrong halfway through.

CSV columns to REST API payload: the exact schema to use

Keep the CSV narrow. Every column should map to a field the API already accepts, so your script is a rename job, not a translation job.

CSV column API field Notes
caption content The post text. Check length before sending; see below.
platforms platforms Pipe separated, e.g. instagram|linkedin|threads.
media_id media The med_ id from the media upload, or a public URL.
schedule_at schedule_at ISO 8601.
timezone timezone e.g. America/Toronto. Never leave this to the server.
post_type post_type Needed for Reels, Shorts or Stories.
draft draft Useful for a first pass you want a human to eyeball.
title title YouTube title, up to 100 characters.

Two practical notes. First, captions are not interchangeable across networks. Threads caps a post at 500 characters and allows 250 posts per 24 hours, YouTube titles cap at 100 characters, and a TikTok photo carousel description allows 4,000. Run each row through a length check before it enters the queue so a 300 character Facebook caption does not silently break the Threads row. Second, draft is your friend for the first import. Send the whole CSV as drafts, check them in the app, then update the scheduled times.

How to call the social media API for batch scheduling (working example)

The simplest form is a single JSON object per row, and it works for most of a calendar.

Take one line of the CSV:

caption,platforms,media_id,schedule_at,timezone,post_type
"Three things I learned shipping a course,instagram|linkedin|threads,med_9f2,2026-03-04T14:00:00Z,America/Toronto,

The body you send to POST /v1/posts:

{
  "content": "Three things I learned shipping a course.",
  "platforms": ["instagram", "linkedin", "threads"],
  "media": ["med_9f2"],
  "schedule_at": "2026-03-04T14:00:00Z",
  "timezone": "America/Toronto"
}

The response carries a post_ id. Write it into the row and move to the next line. When a platform in your list is not connected, the request still succeeds for the rest and names the gap in missing_platforms, so your log tells you exactly which connection to fix instead of failing the batch.

When one idea needs different copy per network, switch to the advanced form:

{
  "platforms": [
    { "platform": "linkedin",
      "content": "Long version with the full lesson." },
    { "platform": "threads",
      "content": "Short version. 500 chars max." }
  ],
  "schedule_at": "2026-03-04T14:00:00Z",
  "timezone": "America/Toronto"
}

Platform-specific settings go in each object’s options, TikTok settings sit in a top-level tiktok object, and account_ids narrows a post to specific accounts when a workspace holds more than one account on a platform. The posts API reference lists every field.

Prefer a CLI? The same job runs from a calendar file with creatoros posts:bulk-upload --file calendar.json --dryRun first, then without --dryRun once the preview looks right. More on that in scheduling client posts with the Creator OS CLI.

Can you schedule Instagram posts, threads posts, and LinkedIn posts in the same batch

Yes. One request can name Instagram, LinkedIn, Threads and other posting networks together, and Creator OS applies each network’s rules to its own copy.

Posting networks are Instagram, TikTok, YouTube, X, Threads, Facebook, LinkedIn, Reddit, Discord and Slack. WhatsApp is for messages rather than posts, and a Shopify store has a blog and a catalog, so it is not a posting target.

The rules that trip people up in a mixed batch:

  • Instagram: Reels, feed images, carousels and Stories need a Business or Creator account. Reels get a custom cover or a frame offset. Captions have no clickable links, so put the link in firstComment.
  • Threads: multi-post threads via threadItems, 500 characters per post, and topic_tag must be 1 to 50 characters with no periods or ampersands.
  • LinkedIn: text, images, video, polls, reposts and company pages via organizationUrn. Check the post options reference for the format each network supports before you add a column to the CSV.

For a thread row, the details of how Threads chains posts are in the Threads scheduler walkthrough. For the Instagram side, including Trial Reels, see scheduling Instagram posts with Claude.

Idempotency, retries, and partial failures when your CSV has 400 rows

Design the script so a rerun is safe and a single bad row cannot sink the batch.

A 400 row import will hit at least one failure. The API’s error shape is consistent: { error: { code, message, status } }. Log the row number, the code and the message, then continue to the next row rather than aborting. At the end, print a failure list and re-send only those rows.

Make reruns safe by keeping the returned post_ id in the CSV. If the column is already filled, skip the row. That way a crash at row 250 does not create 250 duplicates when you restart.

For scheduling, the API is the source of truth, not your loop. A row that returns successfully is queued whether or not your script finishes. Before you trust the batch, check creatoros accounts:health to catch an expired connection, and after the batch run creatoros posts:list --status failed --from <ISO> --to <ISO> to sweep up anything that broke later. creatoros posts:retry <postId> resends a failed one.

If you want the API to push results at you instead of polling, webhooks cover events like post.published and post.failed, signed with X-CreatorOS-Signature. Wire that into your ops channel and the CSV becomes a monitoring surface, not just an input. Details in the webhooks docs.

Why ops teams pick a REST API over Make scenarios

Ops teams pick an API when the work is a loop over data they already control, not a chain of app triggers.

A visual automation tool like Make is a workflow canvas: you connect an app, add a trigger, and build steps by clicking. That is a good fit for reactive work such as “when a form is submitted, post an announcement.” For a 400 row import, you are running the same fixed shape hundreds of times, and that shape is easier to express as a loop you can read in a diff and put under version control than as a canvas you click through.

What Creator OS does differently for this job:

  • A REST API with one documented request shape for posts, media and analytics.
  • A hosted MCP server at https://mcp.creatoros.ca/mcp and a CLI (@creatoros/cli), so an AI agent or a script can run the same accounts. No install for the MCP server.
  • Workspace-pinned keys, so an agency script for client A physically cannot write to client B.
  • Advanced form posts, so one CSV row can carry different copy per platform.
  • Webhooks for post.published, post.failed, comment.received and more.

If you want a side-by-side on the visual-tool comparison specifically, read Creator OS vs Make for social media. You can also watch the walkthroughs on the KevBuildsApps YouTube channel, or start with the launch video.

FAQ

How do I bulk schedule social posts using a REST API?

Create an API key in Settings, upload your media once to get med_ ids, then send one POST /v1/posts per CSV row with content, platforms, schedule_at and timezone. Save each returned post_ id back into the file so reruns skip rows already queued. The base URL and full field list live at https://www.creatoros.ca/docs.

Does the API let me schedule LinkedIn posts and Instagram posts at the same time?

Yes. Put both platform names in the same platforms array and Creator OS applies each network’s rules to its own copy. If one platform is not connected, the request still goes out to the rest and lists the gap in missing_platforms. Check connections first with creatoros accounts:health.

What CSV format should I use for bulk scheduling?

One row per post, with a column for caption, platforms, media id, schedule time, timezone and optionally post type and draft. Keep times in ISO 8601 and put the timezone in its own column. Map each column straight to an API field so your script is a rename, not a translation.

What happens if some rows in my batch fail?

Each request fails independently and returns { error: { code, message, status } }, so log the row and continue instead of aborting. At the end, re-send only the failed rows. Failed posts can also be resent later with creatoros posts:retry <postId>.

Can I schedule threads posts through the same social network API?

Yes. Threads is a posting network, and multi-post threads are built with threadItems in the same request. Each Threads post allows 500 characters and an account can publish up to 250 posts per 24 hours, so check long captions before they enter the queue.

Get started

Creator OS plans start at $14.99/month for 11 platforms plus Skool, with the API, the MCP server and the CLI included. Create a key, point your script at /v1/posts, and your spreadsheet becomes the calendar.

Sign up at https://www.creatoros.ca/sign-up, then read the authentication notes at https://www.creatoros.ca/docs/authentication before your first import.

Get started for $0
Free trial for new subscribers. Cancel anytime.

Keep reading