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:
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.
Authorization: Bearer sl_live_your_api_key_here
Errors
The API uses standard HTTP status codes. All error responses return a JSON object with an error code and a message field.
{
"error": "channel_not_connected",
"message": "tiktok channel is not connected to this profile"
}
| Status | Error code | Description |
|---|---|---|
| 401 | unauthorized | Missing or invalid API key |
| 400 | bad_request | Missing required fields |
| 404 | not_found | Profile not found or doesn't belong to you |
| 400 | channel_not_connected | The requested platform is not connected to this profile |
| 400 | tiktok_error | TikTok API rejected the upload |
| 500 | server_error | Internal server error |
List profiles
Returns all profiles in your account, including connected channels for each.
{
"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.
| Parameter | Type | Description |
|---|---|---|
| idrequired | string | Profile 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.
| Parameter | Type | Description |
|---|---|---|
| idrequired | string | Profile ID |
| Field | Type | Description |
|---|---|---|
| videorequired | file | Video file to publish. MP4, MOV, AVI. Max 500 MB. |
| platformrequired | string | tiktok or youtube |
| titleoptional | string | Video title. Max 150 characters. Default: file name. |
{
"ok": true,
"publish_id": "v_pub_xxxxxxxxxxxx",
"platform": "tiktok",
"message": "Video submitted to TikTok for processing"
}List publications
Returns all your publications with optional filters. Results are sorted newest first (max 100 per page).
| Param | Type | Description |
|---|---|---|
| profile_idopt | string | Filter by profile UUID |
| platformopt | string | youtube or tiktok |
| statusopt | string | pending · processing · published · failed |
| limitopt | integer | 1–100, default 20 |
| offsetopt | integer | Pagination offset, default 0 |
{
"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.
multipart/form-data) or pass a public URL (application/json with video_url). The server downloads the URL automatically — no large file transfer needed.
| Field | Type | Description |
|---|---|---|
| platform* | string | youtube or tiktok |
| video* | file | Video file (mp4 recommended). Required unless status=draft |
| titleopt | string | Title (max 100 chars for YouTube, 150 for TikTok) |
| descriptionopt | string | Description (YouTube only, max 5000 chars) |
| privacyopt | string | YouTube: public · private · unlisted. Default: private |
| profile_idopt | string | Profile UUID. Omit to use the default channel |
| statusopt | string | published (default) or draft |
| Field | Type | Description |
|---|---|---|
| platform* | string | youtube or tiktok |
| video_url* | string | Public HTTP/HTTPS URL to the video. Server downloads it. Required unless status=draft |
| titleopt | string | Title (max 100 chars for YouTube, 150 for TikTok) |
| descriptionopt | string | Description (YouTube only, max 5000 chars) |
| privacyopt | string | YouTube: public · private · unlisted. Default: private |
| profile_idopt | string | Profile UUID. Omit to use the default channel |
| statusopt | string | published (default) or draft |
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" }'
{
"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 every 5s until status is "published" or "failed" curl https://storyload.io/v1/publications/PUBLICATION_ID \ -H "Authorization: Bearer sl_live_..."
{
"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.
{ "publication": { /* Publication object */ } }Update publication
Update editable fields on a publication. Only draft or pending publications can be updated — published ones are immutable.
| Field | Type | Description |
|---|---|---|
| titleopt | string | Update the title |
| descriptionopt | string | Update the description |
| privacyopt | string | public · private · unlisted |
| statusopt | string | draft or pending |
{ "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.
{ "ok": true, "message": "Publication deleted" }cURL examples
curl https://storyload.io/v1/profiles \
-H "Authorization: Bearer sl_live_your_key"
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
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
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'}