Skip to content
reelspanDocs

Publish

Post a video

Upload one video and publish it to any of 22 networks at once, now, at a set time, in the queue, or on repeat, with each network's own settings.

POSTapi.reelspan.com/v1/posts/videoTry it →

Send the video once as multipart/form-data. Name the profile in user and each network in a repeated platform[] field. Reelspan converts and uploads it to every network, then answers with one result per network.

A first video

curl -X POST https://api.reelspan.com/v1/posts/video \
  -H "Authorization: Bearer $REELSPAN_API_KEY" \
  -H "Idempotency-Key: launch-2026-12-01" \
  -F "user=my-brand" \
  -F "platform[]=tiktok" \
  -F "platform[]=youtube" \
  -F "platform[]=instagram" \
  -F "title=Launch day" \
  -F "[email protected]"

video can also be a public https URL instead of a file; Google Drive and Dropbox share links work. To post the same file more than once, upload it first with Upload media and send video_media_id.

The response

The request waits up to 59 seconds for every network. Most finish in that time and you get 200 with results, keyed by network:

{
  "success": true,
  "request_id": "9b1f6c2e8a3d4f50b7e1c9a2d6f3e8b1",
  "results": {
    "tiktok": { "success": true, "url": "https://www.tiktok.com/@mybrand/video/7441…", "post_id": "7441…" },
    "youtube": { "success": true, "url": "https://youtube.com/shorts/aB3…" },
    "instagram": { "success": false, "error": "Instagram is still processing the video", "error_code": "processing_timeout" }
  }
}

When a network takes longer, the answer is still 200, but without results: it has a message, the request_id and total_platforms, and the work carries on in the background. Read the outcome with Post status or a webhook. Send async_upload=true to get that answer straight away.

{
  "success": true,
  "message": "Upload initiated successfully in background.",
  "request_id": "9b1f6c2e8a3d4f50b7e1c9a2d6f3e8b1",
  "total_platforms": 3
}

A scheduled or queued post answers 202 with its job_id instead.

One network failing never fails the others. Retry only the failed ones with Retry: the video is not uploaded again.

Captions per network

title is the caption every network uses. To say something different on one network, send {network}_title (for example tiktok_title, instagram_title, x_title), and {network}_description where the network has a separate body (YouTube, LinkedIn, Reddit, WordPress…). YouTube needs a title; it uses youtube_title, else title.

Network settings

Each network's own options are plain form fields, named after the network: TikTok privacy and interactions, the YouTube category, tags and thumbnail, an Instagram Story instead of a Reel, a Pinterest board, a Reddit subreddit, and so on. The full list for each network, with what it does and its limits, is on that network's page under Networks.

curl -X POST https://api.reelspan.com/v1/posts/video \
  -H "Authorization: Bearer $REELSPAN_API_KEY" \
  -F "user=my-brand" \
  -F "platform[]=tiktok" -F "platform[]=youtube" \
  -F "title=How we ship" \
  -F "privacy_level=PUBLIC_TO_EVERYONE" \
  -F "youtube_title=How we ship, in 60 seconds" \
  -F "privacyStatus=public" \
  -F "[email protected]"

Later, queued or repeated

  • At a time: scheduled_date (ISO 8601) with an optional timezone. The answer is 202 with a job_id; see Scheduled posts.
  • In the queue: add_to_queue=true takes the profile's next free slot; see Queue.
  • On repeat: repeat_every (day, week, month) and repeat_times (2 to 30) schedule the whole series at once; see Scheduling and repeats.

Safe to retry

Send an Idempotency-Key header, or your own request_id. If the network drops after you sent the request, sending it again with the same key returns the first request's result instead of posting twice. See Retrying safely.

Limits

The video must be within your plan's upload size and each network's own limits (length, size, aspect ratio). Reelspan checks before uploading and says which network can't take it; see Video requirements.

Parameters

  • Idempotency-Keystring · header

    Makes retries safe: the same key within 24 hours returns the first answer (X-Idempotency-Key works too)

  • X-Request-Idstring · header

    Your own request_id (8-100 letters, digits, _ or -), and the idempotency key when none is sent

  • X-External-Idstring · header

    Same as the external_id field, for your own reference

Request body

multipart/form-data

  • userstringrequired

    Profile username

  • platform[]string[]required

    Networks to publish to, repeated: tiktok, youtube, instagram, facebook, x, threads, linkedin, pinterest, bluesky, telegram, discord, mastodon, wordpress, reddit, google_business, slack, nostr, lemmy, devto, hashnode, whop, listmonk.

  • videostringrequired

    The video file (binary), or a public https URL

  • titlestring

    Default caption; required for YouTube

  • descriptionstring
  • async_uploadstring

    Return immediately with a request_id

    truefalse

  • scheduled_datestring

    ISO-8601; returns 202 with a job_id

  • timezonestring

    IANA zone for scheduled_date (default UTC)

  • repeat_everystring

    Post the same thing again every day, week or month. Every post of the series is scheduled now, keeps the same local time in timezone, and can be edited or cancelled on its own; the response lists them under repeat.

    dayweekmonth

  • repeat_timesstring

    How many posts in the series, 2 to 30, the first included. Each counts as an upload.

  • external_idstring
  • request_idstring

    Your own id, 8-100 characters, to poll even if the response is lost

  • first_commentstring

    Posted as the first comment (a reply on X, Bluesky and Threads) right after the post goes live. {platform}_first_comment overrides it for one platform. Where a platform has no comments API, or refuses it, the post still counts and its result carries first_comment and a warning.

  • video_media_idstring

    A video uploaded earlier with POST /v1/media/upload, instead of video

  • thumbnail_media_idstring

    An image uploaded earlier with POST /v1/media/upload?kind=photo, used as the YouTube thumbnail (or send thumbnail_url)

  • cover_media_idstring

    An image uploaded earlier, used as the TikTok cover (or send cover_url)

  • bluesky_compressstring

    Compress a video over 100 MB so Bluesky takes it (only when set)

    truefalse

  • privacy_levelstring

    TikTok: PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY

  • post_modestring

    TikTok: MEDIA_UPLOAD sends it to the TikTok inbox to finish in the app

    DIRECT_POSTMEDIA_UPLOAD

  • youtube_titlestring
  • youtube_descriptionstring
  • tags[]string[]
  • privacyStatusstring

    publicunlistedprivate

  • instagram_titlestring
  • facebook_titlestring
  • facebook_media_typestring

    Facebook: a Reel (default), a regular Page video (any shape, with a title) or a Page Story

    REELSVIDEOSTORIES

  • media_typestring

    Instagram: a Reel (default) or a Story

    REELSSTORIES

  • x_titlestring
  • tiktok_titlestring
  • hasPaidProductPlacementstring

    YouTube: the video has paid product placement

    truefalse

  • facebook_unpublished_content_typestring

    Facebook VIDEO: keep it off the Page as this kind

    SCHEDULEDDRAFTADS_POST

  • facebook_no_storystring

    Facebook: post without a feed story

    truefalse

  • facebook_place_idstring

    Facebook: tag a place (Page id of the place)

  • x_quote_tweet_idstring

    X: quote this post (also quote_tweet_id)

  • tagged_user_idsstring

    X: user ids tagged in the media, comma-separated (up to 10)

  • exclude_reply_user_idsstring

    X: with reply_to_id, leave these user ids out of the reply

  • geo_place_idstring

    X: a place id

  • for_super_followers_onlystring

    X: subscribers only

    truefalse

  • share_with_followersstring

    X: with community_id, also show it to followers

    truefalse

  • threads_reply_to_idstring

    Threads: reply to this post

  • threads_quote_post_idstring

    Threads: quote this post

  • bluesky_labelsstring

    Bluesky: content warnings, comma-separated: sexual, nudity, porn, graphic-media

  • bluesky_threadgatestring

    Bluesky: who may reply: nobody, or any of mention, following, followers (comma-separated)

  • bluesky_postgatestring

    Bluesky: nobody may quote it

    no_quotes

  • bluesky_quote_uristring

    Bluesky: quote this post (at:// URI)

  • bluesky_reply_to_idstring

    Bluesky: reply to this post (at:// URI)

  • discord_thread_idstring

    Discord: post into this thread

  • discord_thread_namestring

    Discord: in a forum channel, start a post with this title

  • discord_flagsstring

    Discord: suppress_embeds and/or suppress_notifications, comma-separated

  • discord_max_file_mbstring

    Discord: a boosted server’s file limit in MB (10 to 100, default 10)

  • telegram_parse_modestring

    Telegram: how the text is formatted

    HTMLMarkdownV2Markdown

  • telegram_has_spoilerstring

    Telegram: blur the media until tapped

    truefalse

  • telegram_as_documentstring

    Telegram: send the file uncompressed, as a document

    truefalse

  • telegram_disable_notificationstring

    Telegram: post silently

    truefalse

  • telegram_protect_contentstring

    Telegram: no forwarding or saving

    truefalse

  • wordpress_excerptstring
  • wordpress_slugstring

Responses

200 Finished (per-platform results) or moved to the background (request_id)

{
  "success": true,
  "request_id": "string",
  "results": {},
  "usage": {
    "count": 0,
    "limit": 0,
    "last_reset": "string"
  }
}

202 Scheduled

{
  "success": true,
  "job_id": "string",
  "request_id": "string",
  "scheduled_date": "string"
}

400 Invalid request

{
  "success": false,
  "message": "string",
  "error": "string",
  "error_code": "string"
}

Something unclear or missing? Write to [email protected] and we’ll fix the page.