Skip to main content
Got 200 posts to schedule? Upload a CSV instead of creating every post with a separate POST /post request. Each row in the CSV becomes one post. Multiple platforms per row, multiple teams, media referenced by URL - one bad row won’t block the rest.

How it works

This is an asynchronous job, same pattern as Import Post History.
  1. Upload: You send us a CSV file.
  2. Wait: We process each row in the background.
  3. Poll: You check if we’re done.
  4. Results: You get per-row success/failure details.

Step 1: Upload the CSV

Endpoint: POST /api/v1/post-csv-import
The file must be .csv and under 100 MB. Response (201):
Every row in the CSV must include a teamId, and all referenced teams must belong to the same organization that owns your API key.

Step 2: Poll Progress

You have two options: lightweight status or full import details.

Status (lightweight)

Endpoint: GET /api/v1/post-csv-import/{importId}/status

Full import details

Endpoint: GET /api/v1/post-csv-import/{importId} Returns the full import object (same shape as the POST response).

Import history

Endpoint: GET /api/v1/post-csv-import Returns { items, total }, newest first.

Step 3: Row Results

Once the import finishes (or while it’s running), you can inspect individual rows. Endpoint: GET /api/v1/post-csv-import/{importId}/rows
Successful rows include the postId so you can track the created post:

Import Statuses


CSV Format

Each row is one post. Column names map to post fields: Media is referenced by URL - we download and process it for you. No need to upload files first with /upload. Use # to separate multiple media URLs or list-like values inside one cell:

Platform Media URL Columns

Common Platform Columns

Boolean columns must be TRUE or FALSE. Enum-like columns are case-insensitive in practice because we normalize them to uppercase before validation.
Download a CSV template from the bundle.social dashboard under the Bulk Post section to see all available columns and example values.

Handling Rate Limits

If the import status goes to RATE_LIMITED, don’t panic. We hit a platform’s posting cap.
  • The import pauses automatically.
  • Check rateLimitResetAt for when it can resume.
  • We retry automatically - just keep polling.
This works the same as Import Post History rate limiting.

See also

  • Import Post History - same async pattern, but for pulling existing posts from platforms.
  • Media Upload - if you prefer uploading media separately before creating posts.
  • Rate Limits - posting limits per platform and plan.