Skip to content
reelspanDocs

Guides

Large files

Send videos over 100 MB with a direct multipart upload to storage, know your plan's file size limit, and post from public URLs, Google Drive or Dropbox links.

A video can reach Reelspan three ways: in the post request itself, as a public URL Reelspan downloads, or uploaded first and referred to by media_id. For anything over 100 MB, upload it first with a direct upload: the file goes straight to storage in parts, and the post request stays small.

Size limits

Plan Largest video
Free 0.5 GB
Starter 1 GB
Pro 2 GB
Agency 4 GB
Scale 8 GB

The plan limit applies to videos however they arrive (file, URL or direct upload). Other files have fixed limits: photos up to 20 MB each and PDF documents up to 100 MB. A file over the limit is refused with 413 and error_code: FILE_TOO_LARGE.

Separately, a single request sent to the API can carry at most 100 MB, so a file bigger than that must use a direct upload (or a URL). Each network also has its own limits, checked when Reelspan publishes there; see Video requirements.

With the SDKs: nothing to do

The Node.js and Python SDKs switch to a direct upload on their own for files of 90 MB or more, upload the parts three at a time with retries per part, then post with the resulting media_id. You pass the file as usual:

import { Reelspan } from 'reelspan';
import { readFile } from 'node:fs/promises';

const reelspan = new Reelspan();
await reelspan.posts.video({
  user: 'my-brand',
  platforms: ['youtube'],
  title: 'Full keynote',
  video: { data: await readFile('keynote.mp4'), name: 'keynote.mp4', type: 'video/mp4' },
});

To upload once and post later (or several times), call media.upload and keep the media_id. It takes an onProgress (Node) / on_progress (Python) callback for a progress bar:

const media = await reelspan.media.upload(
  { data: await readFile('keynote.mp4'), name: 'keynote.mp4', type: 'video/mp4' },
  { kind: 'video', onProgress: (sent, total) => console.log(`${Math.round((sent / total) * 100)}%`) },
);
await reelspan.posts.video({ user: 'my-brand', platforms: ['youtube'], title: 'Full keynote', videoMediaId: media.media_id });

The 90 MB threshold can be changed with directUploadBytes (Node) or direct_upload_bytes (Python) when you create the client.

Direct upload by hand

Three steps: start, send the parts, complete.

1. Start

Tell Reelspan the kind and size of the file. It checks the size against your plan and answers with one signed URL per part.

curl -X POST https://api.reelspan.com/v1/media/uploads \
  -H "Authorization: Bearer $REELSPAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind": "video", "size_bytes": 734003200, "filename": "keynote.mp4"}'
{
  "success": true,
  "kind": "video",
  "media_id": "med_…",
  "part_size": 8388608,
  "parts": [
    { "part_number": 1, "url": "https://…" },
    { "part_number": 2, "url": "https://…" }
  ],
  "expires_in": 3600
}

Parts are at least 8 MB, and there are never more than 1,000 of them. The part URLs are valid for an hour (expires_in, in seconds), and an upload that is not completed within a day is forgotten.

2. Send the parts

PUT each part's bytes to its URL: part n is the bytes from (n - 1) × part_size, part_size long (the last part is whatever is left). Each URL is signed for its part's exact length, so a body of any other size is refused. Keep the ETag header of every response.

# part 1 of a file split with: split -b 8388608 -d keynote.mp4 part_
curl -X PUT --upload-file part_00 "$PART_1_URL" -D - | grep -i etag

Parts can go in parallel and in any order. If one fails, send that part again to the same URL.

3. Complete

Send every part number with its ETag. Reelspan then reads the stored file's real size and first bytes, refuses it if it is not the kind you declared or is over your limit, and returns the media_id ready to use.

curl -X POST https://api.reelspan.com/v1/media/uploads/med_…/complete \
  -H "Authorization: Bearer $REELSPAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"parts": [{"part_number": 1, "etag": "\"9b2cf5…\""}, {"part_number": 2, "etag": "\"41d8cd…\""}]}'
{ "success": true, "media_id": "med_…", "kind": "video", "content_type": "video/mp4", "size_bytes": 734003200, "duration_seconds": 1834.2 }

Then post it with video_media_id (or photo_media_ids[] for photos, document_media_id for a PDF):

curl -X POST https://api.reelspan.com/v1/posts/video \
  -H "Authorization: Bearer $REELSPAN_API_KEY" \
  -F "user=my-brand" \
  -F "platform[]=youtube" \
  -F "title=Full keynote" \
  -F "video_media_id=med_…"

To give up on an upload part-way, POST /v1/media/uploads/{media_id}/abort discards the parts already sent.

Error When
FILE_TOO_LARGE (413) size_bytes, or the stored file, is over your plan's limit.
EMPTY_FILE (400) size_bytes is missing or zero, or nothing was stored.
PARTS_REQUIRED (400) The complete call had no valid parts.
UNSUPPORTED_MEDIA / WRONG_MEDIA_KIND (400) The stored file isn't a supported type, or isn't the kind you declared.
UPLOAD_NOT_FOUND (404) The media_id is unknown or the upload expired. Start again.

Small files: one request

Under 100 MB you can send the file in the post itself ([email protected]), or upload it once with POST /v1/media/upload (a multipart field named file, and ?kind=video, photo or document) to get a media_id. See Upload media.

Public URLs, Google Drive and Dropbox

Any video, photos[] or document value can be a public https:// URL instead of a file. Reelspan downloads it before the post is accepted, so the same size limits apply and a failed download refuses the request with URL_FETCH_FAILED.

  • Only https:// URLs are fetched, with up to three redirects. Addresses on private networks are refused.
  • Google Drive share links (drive.google.com/file/d/… and drive.google.com/open?id=…) and Dropbox share links are turned into direct downloads for you. The file must be shared so that anyone with the link can open it.
  • One request can fetch at most 100 media URLs in total (TOO_MANY_MEDIA_URLS).

Posting the same large video to several profiles, or again later? Upload it once and reuse the media_id. Media that no post uses is deleted after a day, and the media of a finished post after a retention period, unless you keep it in the media library.

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