Storyload
REST API v1

Storyload API

The Storyload API lets you publish videos to TikTok and YouTube programmatically — from your own code, bots, or automations. No manual uploads, no browser required.

Base URL:

base url
https://storyload.io/v1

All requests and responses use JSON. Video uploads use multipart/form-data.

Authentication

Every API request must include your API key in the Authorization header as a Bearer token. Generate your keys on the API Keys page.

header
Authorization: Bearer sl_live_your_api_key_here
Keep your API key secret. Never include it in client-side JavaScript or public repositories. If a key is compromised, revoke it immediately on the API Keys page.

Errors

The API uses standard HTTP status codes. All error responses return a JSON object with an error code and a message field.

error response
{
  "error": "channel_not_connected",
  "message": "tiktok channel is not connected to this profile"
}
StatusError codeDescription
401unauthorizedMissing or invalid API key
400bad_requestMissing required fields
404not_foundProfile not found or doesn't belong to you
400channel_not_connectedThe requested platform is not connected to this profile
400tiktok_errorTikTok API rejected the upload
500server_errorInternal server error

List profiles

Returns all profiles in your account, including connected channels for each.

GET /v1/profiles No request body
Response
{
  "profiles": [
    {
      "id": "abc123",
      "name": "Travel Vlogs",
      "created_at": "2026-03-01T12:00:00.000Z",
      "channels": [
        {
          "platform": "tiktok",
          "account_name": "my_travel_acc",
          "account_id": "tt_open_id_xxx",
          "connected_at": "2026-03-02T10:00:00.000Z"
        }
      ]
    }
  ],
  "total": 1
}

Get profile

Returns a single profile by ID with its connected channels.

GET /v1/profiles/:id
Path parameters
ParameterTypeDescription
idrequiredstringProfile ID (from List profiles)

Publish video

Upload and publish a video to a TikTok or YouTube channel connected to a profile. The request must be multipart/form-data.

POST /v1/profiles/:id/publish
Path parameters
ParameterTypeDescription
idrequiredstringProfile ID
Body (multipart/form-data)
FieldTypeDescription
videorequiredfileVideo file to publish. MP4, MOV, AVI. Max 500 MB.
platformrequiredstringtiktok or youtube
titleoptionalstringVideo title. Max 150 characters. Default: file name.
Response
{
  "ok": true,
  "publish_id": "v_pub_xxxxxxxxxxxx",
  "platform": "tiktok",
  "message": "Video submitted to TikTok for processing"
}
Storyload is currently in TikTok Sandbox mode. Published videos are visible only to your TikTok test account and won't appear publicly until production API access is approved.

List publications

Returns all your publications with optional filters. Results are sorted newest first (max 100 per page).

GET /v1/publications Query params optional
Query parameters
ParamTypeDescription
profile_idoptstringFilter by profile UUID
platformoptstringyoutube or tiktok
statusoptstringpending · processing · published · failed
limitoptinteger1–100, default 20
offsetoptintegerPagination offset, default 0
Response
{
  "publications": [ /* Publication objects */ ],
  "total": 42,
  "limit": 20,
  "offset": 0
}

Create publication

Create a publication and start publishing asynchronously. The server always responds 202 Accepted immediately — the upload happens in the background. Poll GET /v1/publications/:id until status is published or failed.

Use status=draft to save metadata without triggering a publish.

POST /v1/publications multipart or JSON with video_url
Two ways to provide a video: upload a file (multipart/form-data) or pass a public URL (application/json with video_url). The server downloads the URL automatically — no large file transfer needed.
Option A — Upload file (multipart/form-data)
FieldTypeDescription
platform*stringyoutube or tiktok
video*fileVideo file (mp4 recommended). Required unless status=draft
titleoptstringTitle (max 100 chars for YouTube, 150 for TikTok)
descriptionoptstringDescription (YouTube only, max 5000 chars)
privacyoptstringYouTube: public · private · unlisted. Default: private
profile_idoptstringProfile UUID. Omit to use the default channel
statusoptstringpublished (default) or draft
Option B — Video URL (application/json)
FieldTypeDescription
platform*stringyoutube or tiktok
video_url*stringPublic HTTP/HTTPS URL to the video. Server downloads it. Required unless status=draft
titleoptstringTitle (max 100 chars for YouTube, 150 for TikTok)
descriptionoptstringDescription (YouTube only, max 5000 chars)
privacyoptstringYouTube: public · private · unlisted. Default: private
profile_idoptstringProfile UUID. Omit to use the default channel
statusoptstringpublished (default) or draft
Example — publish by URL
curl
curl -X POST https://storyload.io/v1/publications \
  -H "Authorization: Bearer sl_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "youtube",
    "video_url": "https://example.com/my-short.mp4",
    "title": "My YouTube Short",
    "description": "Auto-published via API",
    "privacy": "public"
  }'
Response — 202 Accepted (always immediate)
{
  "publication": {
    "id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
    "platform": "youtube",
    "status": "processing",
    "video_id": null,
    "video_url": null,
    /* ... rest of fields */
  },
  "message": "Publication created. Poll GET /v1/publications/9b1deb... for status."
}
Poll until published
curl — polling
# Poll every 5s until status is "published" or "failed"
curl https://storyload.io/v1/publications/PUBLICATION_ID \
  -H "Authorization: Bearer sl_live_..."
Published response (after processing)
{
  "publication": {
    "id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
    "platform": "youtube",
    "status": "published",
    "video_id": "dQw4w9WgXcQ",
    "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "privacy": "public",
    "published_at": "2026-03-30T12:00:00.000Z"
  }
}

Get publication

Retrieve a single publication by its ID.

GET /v1/publications/:id No request body
Response
{ "publication": { /* Publication object */ } }

Update publication

Update editable fields on a publication. Only draft or pending publications can be updated — published ones are immutable.

PATCH /v1/publications/:id application/json
Body fields
FieldTypeDescription
titleoptstringUpdate the title
descriptionoptstringUpdate the description
privacyoptstringpublic · private · unlisted
statusoptstringdraft or pending
Response
{ "publication": { /* Updated publication object */ } }

Delete publication

Delete a publication record. This removes the record from Storyload only — the video on YouTube or TikTok is not affected.

DELETE /v1/publications/:id No request body
Response
{ "ok": true, "message": "Publication deleted" }

cURL examples

List profiles
bash
curl https://storyload.io/v1/profiles \
  -H "Authorization: Bearer sl_live_your_key"
Publish a video to TikTok
bash
curl -X POST https://storyload.io/v1/profiles/PROFILE_ID/publish \
  -H "Authorization: Bearer sl_live_your_key" \
  -F "platform=tiktok" \
  -F "title=My awesome video" \
  -F "video=@/path/to/video.mp4"

JavaScript example

javascript (Node.js)
import fs from "fs";
import FormData from "form-data";

const API_KEY = "sl_live_your_key";
const BASE = "https://storyload.io/v1";

// 1. Get your profiles
const profiles = await fetch(`${BASE}/profiles`, {
  headers: { Authorization: `Bearer ${API_KEY}` }
}).then(r => r.json());

const profileId = profiles.profiles[0].id;

// 2. Publish a video
const form = new FormData();
form.append("platform", "tiktok");
form.append("title", "My video title");
form.append("video", fs.createReadStream("./video.mp4"));

const result = await fetch(`${BASE}/profiles/${profileId}/publish`, {
  method: "POST",
  headers: { Authorization: `Bearer ${API_KEY}`, ...form.getHeaders() },
  body: form,
}).then(r => r.json());

console.log(result); // { ok: true, publish_id: "...", platform: "tiktok" }

Python example

python
import requests

API_KEY = "sl_live_your_key"
BASE = "https://storyload.io/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# 1. List profiles
profiles = requests.get(f"{BASE}/profiles", headers=HEADERS).json()
profile_id = profiles["profiles"][0]["id"]

# 2. Publish a video
with open("video.mp4", "rb") as f:
    result = requests.post(
        f"{BASE}/profiles/{profile_id}/publish",
        headers=HEADERS,
        data={"platform": "tiktok", "title": "My video"},
        files={"video": f}
    ).json()

print(result)  # {'ok': True, 'publish_id': '...', 'platform': 'tiktok'}