This guide takes you from nothing to a published post. You need a Reelspan account; everything else happens here.
1. Create an API key
In the dashboard, open Developers → API keys and create a key. Copy it straight away: Reelspan keeps only a hash, so the key is shown once.
- A live key starts with
rs_live_and publishes for real. - A test key starts with
rs_test_. Every request goes through the same checks, but nothing reaches the networks and nothing counts against your plan (up to 500 test posts a day). Use one while you build. See Test mode.
Keep the key in an environment variable, never in code you commit. The SDKs read REELSPAN_API_KEY on their own:
export REELSPAN_API_KEY="rs_test_…"Check it works:
curl https://api.reelspan.com/v1/me \
-H "Authorization: Bearer $REELSPAN_API_KEY"import { Reelspan } from 'reelspan';
const reelspan = new Reelspan(); // reads REELSPAN_API_KEY
console.log(await reelspan.me());from reelspan import Reelspan
reelspan = Reelspan() # reads REELSPAN_API_KEY
print(reelspan.me())The answer names your plan and this month's usage. A 401 means the key is wrong, expired or revoked. The SDKs install with npm install reelspan and pip install reelspan; see SDKs.
2. Create a profile
A profile is one brand or client. It holds one connected account per network, and every post names it as user.
curl -X POST https://api.reelspan.com/v1/profiles \
-H "Authorization: Bearer $REELSPAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "my-brand", "display_name": "My Brand"}'await reelspan.profiles.create('my-brand', 'My Brand');reelspan.profiles.create("my-brand", "My Brand")The username can't be changed later; the display name can. See Profiles.
3. Connect an account
Pick whichever fits:
- In the dashboard: open Accounts, choose the profile and connect a network. This is the quickest way to connect your own accounts.
- With a connect link: for a client who should connect their own accounts without a Reelspan login. The link works for 48 hours. See White-label connect.
- With credentials: networks that use a token or webhook instead of a sign-in (Discord, Telegram, Slack, Mastodon, Bluesky and others) can be connected straight from your code. See Connect with credentials.
curl -X POST https://api.reelspan.com/v1/connect-links \
-H "Authorization: Bearer $REELSPAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "my-brand"}'const { access_url } = await reelspan.connect.link('my-brand');access_url = reelspan.connect.link("my-brand")["access_url"]Send access_url to whoever owns the accounts. Connecting accounts covers every option.
No account connected yet? Post to
sandboxinstead of a real network. It needs no connection, reads the media like a real network would and answers with a pretend URL, so you can try the whole flow first. With a live key a sandbox post still counts as an upload.
4. Publish your first post
Send the video once, with the profile in user and one platform[] field per network. Reelspan uploads it to each network in the format that network wants.
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 "platform[]=instagram" \
-F "title=Golden hour, no filter" \
-F "[email protected]"import { Reelspan } from 'reelspan';
import { readFile } from 'node:fs/promises';
const reelspan = new Reelspan();
const post = await reelspan.posts.video({
user: 'my-brand',
platforms: ['tiktok', 'youtube', 'instagram'],
title: 'Golden hour, no filter',
video: { data: await readFile('launch.mp4'), name: 'launch.mp4', type: 'video/mp4' },
});from reelspan import Reelspan
reelspan = Reelspan()
post = reelspan.posts.video("my-brand", ["tiktok", "youtube", "instagram"], "launch.mp4", "Golden hour, no filter")The fields you'll use most:
| Field | What it does |
|---|---|
user |
The profile's username. Required. |
platform[] |
One per network. Required. |
video |
The file, or a public https URL (Google Drive and Dropbox share links work). |
title |
The caption. {network}_title (for example youtube_title) overrides it on one network. YouTube needs a title. |
description |
The longer text, where a network has one. |
first_comment |
Posted as the first comment right after the post goes live. |
scheduled_date |
An ISO 8601 time up to 365 days ahead, with timezone (IANA, default UTC). |
add_to_queue |
true takes the profile's next free queue slot. |
Photos, text and LinkedIn documents work the same way: Post photos, Post text, Post a document. Every field for videos is on Post a video.
5. Read the result
The request waits up to 59 seconds for the networks and answers with one result each. One network failing never stops the others:
{
"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…", "post_id": "aB3…" },
"instagram": {
"success": false,
"skipped": true,
"skip_reason": "profile_platform_not_configured",
"failure_stage": "profile_platform_validation",
"error": "Profile my-brand has no Instagram account configured",
"error_code": "profile_platform_mapping_invalid"
}
},
"usage": { "count": 2, "limit": null, "last_reset": "2026-11-01T00:00:00.000Z" }
}Here Instagram was skipped because the profile has no Instagram account; a skipped network isn't charged.
If a network takes longer, or you sent async_upload=true, the answer has a message and the request_id instead of results, and the work carries on in the background. Poll the status every 5 seconds or more:
curl "https://api.reelspan.com/v1/posts/status?request_id=9b1f6c2e8a3d4f50b7e1c9a2d6f3e8b1" \
-H "Authorization: Bearer $REELSPAN_API_KEY"const status = await reelspan.posts.wait(post.request_id); // polls until every network has finishedstatus = reelspan.posts.wait(post["request_id"]) # polls until every network has finishedOr skip polling and let a webhook tell your server as each network finishes. If something failed, Errors explains the code, and Retry sends only the failed networks again without uploading the video twice.
Next steps
- API overview: base URL, errors, rate limits, idempotency and ids.
- Scheduling and repeats, then Scheduled posts to edit or cancel them.
- Webhooks for results without polling.
- Upload media to send a file once and post it many times, or files over 100 MB.
- The MCP server if you'd rather have an AI agent do the calling.