Skip to Content
API ReferenceUpload MediaBulk Upload

Bulk Upload API

The Bulk Upload API allows you to upload a single video, image, or multi-image carousel to multiple social media accounts (TikTok, Instagram, YouTube, Facebook, LinkedIn, Pinterest, Bluesky, Threads) simultaneously with a single request.

Endpoint

POST /api/v1/upload/bulk

This endpoint accepts multipart/form-data requests.

Authentication

All requests require an API Token in the header: x-api-key: YOUR_API_TOKEN

Body Parameters (Multipart)

Core Parameters

ParameterTypeRequiredDescription
accountsstring (JSON Array)YesA JSON array of account IDs (strings). Example: '["cmf3k9x2a0001l408q7vbn2hd", "cmf3kb7re0004l408zt2m5wxc"]'
videoFileOne of video / photoThe video file to upload. Must be a file upload: unlike the single upload endpoint, a URL string is not accepted here.
photoFile or File[]One of video / photoUse instead of video for image uploads. Send the field more than once (or as an array) to publish a carousel / multi-image post — see the Carousels & Multi-Image Posts section below.
titlestringNo*The title of the post. Acts as caption for TikTok/Instagram. (*Required for TikTok if no description)
descriptionstringNoThe description/caption of the post.
schedule_datestring (ISO 8601)NoDate to schedule the post for (e.g., 2024-12-25T10:00:00Z).
statusstringNodraft or pending_approval: create the posts without publishing them. See Drafts & Approval.
overridesstring (JSON object)NoA different caption, title or thumbnail per network or per account. See Different Text per Network below.

Platform-Specific Parameters

Every platform parameter of the single upload endpoint is accepted here too, and is only applied to the platforms it belongs to. Values and limits are the same as on each platform’s page.

media_type is the one exception: it is a single field shared by the whole batch, so it applies to every platform that reads it (TikTok, Instagram, Facebook). To publish an Instagram story in a mixed batch, use instagram_as_story rather than media_type=STORIES.

TikTok (details)

  • privacy_level: PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY
  • disable_comment, disable_duet, disable_stitch: true/false
  • brand_content_toggle, brand_organic_toggle: true/false
  • is_aigc: true/false (self-disclose AI usage; one flag shared across TikTok, Instagram and Facebook)
  • post_mode: DIRECT_POST (default) or MEDIA_UPLOAD (sends the post to the creator’s TikTok inbox as a draft instead of publishing it)
  • video_cover_timestamp_ms: cover frame, in ms (video)
  • auto_add_music, photo_cover_index: photo posts only

Instagram (details)

  • caption: Instagram-only caption
  • cover_url, thumb_offset: Reel cover
  • share_to_feed: true/false (for Reels)
  • trial_reel: manual/auto: publish as a Trial Reel, shown to non-followers first (single-video Reels only; refused on a photo, carousel or story)
  • audio_name: Reel audio title
  • collaborators, user_tags: comma-separated usernames
  • location_id: Instagram location ID
  • is_aigc: true/false
  • instagram_as_story: true/false — publish to the Instagram accounts’ story instead of their feed. The other platforms of the batch publish normally. One JPEG (≤ 8 MB) or one MP4/MOV (3–60 s, ≤ 100 MB); no collaborators or location_id; the caption and the AI label are not sent. See Publish a Story.

YouTube (details)

  • youtube_title, youtube_description: YouTube-only title and description
  • tags: JSON array or comma-separated string
  • categoryId: YouTube category ID
  • privacyStatus: public, private, unlisted
  • scheduledDate: YouTube-side scheduled publish date (forces private until then)
  • thumbnail (file) or thumbnail_url
  • embeddable, publicStatsViewable, notifySubscribers: true/false
  • license: youtube or creativeCommon
  • selfDeclaredMadeForKids (sets the made-for-kids status), madeForKids
  • containsSyntheticMedia, hasPaidProductPlacement: true/false
  • defaultLanguage, defaultAudioLanguage: BCP-47 codes
  • allowedCountries or blockedCountries: comma-separated ISO country codes
  • recordingDate: ISO 8601

Facebook (details)

  • media_type: REELS or VIDEO (default VIDEO)
  • link: URL to attach to the post
  • is_aigc: true/false (photos, albums and Reels only — ignored on text posts and on media_type=VIDEO)

Threads (details)

  • threads_reply_control: everyone (default), accounts_you_follow, mentioned_only, parent_post_author_only, followers_only
  • threads_alt_text: alt text for images (max 1000 characters)

X (details)

  • One X account per batch: X bans identical content across accounts, so the other X accounts of the batch get a per-account error. An X post is also refused when the same text or media file went out on another of your X accounts in the last 30 days.
  • x_reply_settings: everyone (default), following, mentionedUsers, subscribers, verified
  • x_made_with_ai, x_paid_partnership, x_super_followers_only, x_premium: true/false
  • x_alt_text (max 1000 characters), x_tagged_users (up to 10, JSON array or comma-separated): images only
  • x_subtitles_url (SRT/VTT, max 1 MB), x_subtitles_language (2 letters, default EN): video only
  • x_poll_options (2–4 choices, JSON array or comma-separated), x_poll_duration_minutes (5–10080, default 1440): text-only posts
  • x_community_id, x_share_with_followers

LinkedIn

  • visibility: PUBLIC or CONNECTIONS

Pinterest

  • pinterest_board_id or board_id: The board to pin to (required for Pinterest accounts)
  • link: URL to attach to the pin

Bluesky

  • bluesky_langs: Comma-separated language codes (e.g. en,fr) or JSON array
  • bluesky_labels: Content warnings. Values: sexual, nudity, porn, graphic-media, !no-unauthenticated

Different Text per Network

One caption rarely fits every network: Bluesky stops at 300 characters where YouTube takes 5,000. Send the shared text in description / title, and the exceptions in overrides, a JSON object:

  • Keys are a platform name (tiktok, instagram, facebook, youtube, linkedin, pinterest, bluesky, threads) or one of the post’s account ids.
  • Values take caption, title and thumbnail_url, all optional.
  • An account key wins over its platform key, which wins over the shared text — field by field.
  • Each account is checked against its own text, so the shared caption no longer has to fit the shortest network.
  • An unknown key (a typo like blusky, or an account that is not in accounts) rejects the request with 400 rather than publishing the shared text.
curl -X POST https://api.multi-upload-tool.com/api/v1/upload/bulk \ -H "x-api-key: YOUR_API_TOKEN" \ -F 'accounts=["cmf3k9x2a0001l408q7vbn2hd", "cmf3kb7re0004l408zt2m5wxc", "cmf3kc1dy0007l408j9h6pqsa"]' \ -F "video=@/path/to/video.mp4" \ -F "title=How we shipped v2" \ -F "description=The full story behind v2, with every step and the numbers..." \ -F 'overrides={"bluesky": {"caption": "v2 is out. The story: https://example.com/v2"}, "cmf3kc1dy0007l408j9h6pqsa": {"title": "v2 — behind the scenes"}}'

In the dashboard composer, the same thing is Different caption per network under the caption field, when the post targets two networks or more.

Carousels & Multi-Image Posts

Send the photo field more than once (or as an array) to publish a carousel / multi-image post. The same set of images is sent to every targeted account; each platform caps the number of images it will accept:

PlatformMax images per post
TikTok35
LinkedIn20
Instagram10
Facebook10
Threads10
Pinterest5
Bluesky4

A single photo is published as a normal single-image post. YouTube is video-only and ignores image input.

curl -X POST https://api.yourdomain.com/api/v1/upload/bulk \ -H "x-api-key: YOUR_API_TOKEN" \ -F 'accounts=["cmf3k9x2a0001l408q7vbn2hd", "cmf3kb7re0004l408zt2m5wxc"]' \ -F "photo=@/path/to/image1.jpg" \ -F "photo=@/path/to/image2.jpg" \ -F "photo=@/path/to/image3.jpg" \ -F "title=My carousel post" \ -F "description=Swipe through! #carousel"

Response

The API returns a summary of the operations. It handles potential errors for individual accounts gracefully (e.g., if one account hits a limit, others may still succeed).

{ "success": true, "message": "Bulk upload processing finished", "results": [ { "accountId": "cmf3k9x2a0001l408q7vbn2hd", "platform": "tiktok", "success": true, "uploadId": 501, "jobId": "1024", "scheduledFor": null }, { "accountId": "cmf3kb7re0004l408zt2m5wxc", "platform": "instagram", "success": true, "uploadId": 502, "jobId": "1025", "scheduledFor": "2024-12-25T10:00:00.000Z" }, { "accountId": "cmf3kc1dy0007l408j9h6pqsa", "success": false, "error": "Daily upload limit reached. Reset at 2024-12-24T00:00:00.000Z" } ] }

Example Request

curl -X POST https://api.yourdomain.com/api/v1/upload/bulk \ -H "x-api-key: YOUR_API_TOKEN" \ -F 'accounts=["cmf3k9x2a0001l408q7vbn2hd", "cmf3kb7re0004l408zt2m5wxc", "cmf3kc1dy0007l408j9h6pqsa"]' \ -F "video=@/path/to/video.mp4" \ -F "title=My Amazing Video" \ -F "description=Check this out! #viral #fyp" \ -F "disable_comment=true" \ -F "youtube_title=Exclusive: My Amazing Video (4K)"

Notes

  • One File Source: The file is uploaded once to storage and then processed for each platform locally.
  • De-duplication: If you provide the same account ID multiple times in the accounts array, it will only be processed once.
  • Limits: Each account is checked against its own daily upload limits.
  • Check first: POST /upload/validate takes the same body and reports every error without publishing.
  • Safe retries: Send an Idempotency-Key header so retrying after a timeout never publishes twice. See Idempotency.