tovid API
Post videos to YouTube, TikTok, Facebook, Instagram, Threads, LinkedIn, Telegram and Discord from your own code: a video tovid makes from a link, an idea or a text, or your own MP4. REST, JSON, one API key.
Working with an AI assistant? Give it the Markdown version of this page:tovid.app/api/docs.mdUpdated: 2026-11-12
The tovid API does what the dashboard does: you send a link (an article, a product page), an idea or a text; tovid reads it, writes the script, records the narration and renders an MP4. You then download the file or post it to the channels connected to your account.
You can also skip the making: send your own MP4 (a link to the file, or an upload) and tovid posts it as it is. One call publishes a video to several channels at once, right away, at a time you choose, or in the next free posting time of the project; every post reports its status and the link of the published post.
Everything is asynchronous. Creating a video answers at once with an id; you poll that video until its status is ready (usually 2 to 6 minutes), then read the download link or create posts. Posts are queued and go out in the background; you poll them the same way.
Quick start
Make a video from a link, wait for it, download it:
# 1. Create the video (renders as soon as the script is ready)
curl -X POST https://tovid.app/api/v1/videos \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/blog/my-article"}'
# → 201 {"video": {"id": "cmh2x8k3v0001l5089qjz7w1a", "status": "processing", ...}}
# 2. Poll every 10-15 s until status is "ready" (or "failed")
curl https://tovid.app/api/v1/videos/cmh2x8k3v0001l5089qjz7w1a \
-H "Authorization: Bearer $TOVID_API_KEY"
# → 200 {"video": {"status": "ready", "renders": [{"downloadUrl": "https://…", ...}], ...}}
# 3. Download the MP4, or post it to the project's channels
curl -X POST https://tovid.app/api/v1/posts \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"videoId": "cmh2x8k3v0001l5089qjz7w1a", "when": "now"}'
# → 201 {"posts": [{"id": "cmh2y3tu0007l508y3z4a5b6", "platform": "youtube", "status": "pending", ...}]}Authentication
Create a key in the dashboard under Account → API (https://tovid.app/dashboard/api). The API is included in the Growth, Scale and Pro plans. A key acts as the account owner on every project of the account, so keep it on your server and never ship it in a browser or an app.
Send the key in the Authorization header of every request. The full key is shown once, when it is created; deleting a key in the dashboard revokes it immediately. An account can hold 5 keys.
Authorization: Bearer tovid_sk_your_key_hereRequests and answers
Base URL: https://tovid.app/api/v1. Request bodies are JSON (Content-Type: application/json) and so are all answers. Ids are opaque strings. Times are ISO 8601 in UTC.
A failed request answers with a 4xx or 5xx status and a body of the form { "error": "CODE", "message": "what went wrong" }. Branch on the code; the message is for people. The codes are listed under Errors.
Rate limits per account: 120 requests a minute overall, 10 new videos a minute, 10 renders a minute, 20 calls that create posts a minute, 30 uploaded videos an hour. Over the limit the API answers 429 RATE_LIMITED with a Retry-After header. When polling a video, every 10 to 15 seconds is plenty.
Credits
The API spends the account's credits at the same prices as the dashboard. A render costs 25,000 credits for 60s, 31,500 credits for 90s, 37,500 credits for 120s (a script that runs much longer than the length asked is billed at the next length). The credits are held when the render starts and returned if it fails. Writing a script without rendering is free. Adding your own video is free. Each post to a channel costs 500 credits, taken when the post goes out and returned if it fails.
Creating a video is refused with 402 INSUFFICIENT_CREDITS when the balance cannot cover its render. GET /account returns the balance.
Life of a video
status is one of: processing (the script is being written, or a render is running or about to start), draft (the script is ready and no render was asked: you created it with render: false), ready (the newest render has an MP4), failed (see error).
While the script is written, stage says how far it is: research, read, media, brand, screenshot, script, finish. A render starts within a minute of the script being ready, and its progress goes from 0 to 100.
Finished MP4s are kept for 3 days (renders[].expiresAt). downloadUrl is a signed link valid until then; fetch the video again for a fresh one. A video made through the API also appears in the dashboard, where its script and look can be edited in the studio and rendered again.
Posting to social channels
Channels (a YouTube channel, a TikTok account, a Facebook Page, an Instagram account, a Threads or LinkedIn profile, a Telegram channel, a Discord channel) are connected once in the dashboard under Connections, where each platform asks for its own sign-in; the API cannot connect them. GET /channels lists them with the ids you post to. A project holds a default set of channels, used when a request names none.
POST /posts takes a video that is ready and creates one post per channel. when says the time: "now", "at" a scheduledAt you give, or "slot": the project's next free posting time (set in the project's settings), which fills a calendar without computing times yourself.
A post goes through: pending (queued for now) or scheduled, then processing (being sent to the platform), then posted (url is the link of the published post) or failed (error says why; the credits are returned). canceled is a post you canceled before it went out. Most posts finish within a minute or two of their time; TikTok confirms later and can stay processing for a while.
The video file must still exist when the post goes out: schedule within 3 days of the render for a video tovid made, within 7 days for your own video (MEDIA_EXPIRES_FIRST otherwise). The title is the YouTube title and the first line elsewhere; a link (see link) goes in the first comment on Facebook and Threads and at the end of the caption on the other platforms.
Post your own video
Your own MP4 or MOV (H.264 or HEVC, 3 seconds to 10 minutes) is posted as it is: no script, no render, no credits for adding it. The file is kept 7 days. Give a direct link to the file (up to 100 MB), or upload it (up to 200 MB) with POST /uploads.
# 1. Hand tovid the file by link (or upload it first: see Create an upload)
curl -X POST https://tovid.app/api/v1/videos/upload \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"videoUrl": "https://cdn.example.com/clips/store-opening.mp4", "title": "Our new store is open", "caption": "Come say hi this weekend."}'
# → 201 {"video": {"id": "cmh3a1b2c0009l508d4e5f6g7", "status": "ready", ...}}
# 2. Post it to two channels tomorrow morning
curl -X POST https://tovid.app/api/v1/posts \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"videoId": "cmh3a1b2c0009l508d4e5f6g7", "channelIds": ["cmg9u4d5e0002l408k1m2n3p4", "cmg9u7f8g0004l408q5r6s7t8"], "when": "at", "scheduledAt": "2026-11-13T09:00:00+07:00"}'
# → 201 {"posts": [{"id": "cmh2y3tu0007l508y3z4a5b6", "status": "scheduled", ...}, ...]}
# 3. Read the result of a post
curl https://tovid.app/api/v1/posts/cmh2y3tu0007l508y3z4a5b6 \
-H "Authorization: Bearer $TOVID_API_KEY"
# → 200 {"post": {"status": "posted", "url": "https://www.youtube.com/watch?v=abc123", ...}}Endpoints
- GET/accountGet the account
- GET/projectsList projects
- GET/channelsList channels
- POST/videosCreate a video
- POST/videos/uploadAdd your own video
- POST/uploadsCreate an upload
- GET/videos/{id}Get a video
- GET/videosList videos
- POST/videos/{id}/renderRender a video
- POST/postsCreate posts
- GET/posts/{id}Get a post
- GET/postsList posts
- DELETE/posts/{id}Cancel a post
- POST/videos/{id}/publishPublish a video
- DELETE/videos/{id}Delete a video
Get the account
GEThttps://tovid.app/api/v1/account
The plan and credit balance of the account the key belongs to. A cheap call to check that a key works.
curl https://tovid.app/api/v1/account \
-H "Authorization: Bearer $TOVID_API_KEY"{
"plan": "growth",
"planExpiresAt": "2026-12-01T00:00:00.000Z",
"credits": 2140500,
"limits": { "maxVideoSeconds": 120, "concurrentRenders": 3 }
}Response fields
| Field | Type | Description |
|---|---|---|
| credits | number | Credits left. |
| limits.concurrentRenders | number | Renders the account may run at once; further videos wait their turn. |
List projects
GEThttps://tovid.app/api/v1/projects
Every video belongs to a project, which holds the brand, the default language, ratio, length and voice, the channels to post to and the posting times. Projects are created and set up in the dashboard; the API reads them.
curl https://tovid.app/api/v1/projects \
-H "Authorization: Bearer $TOVID_API_KEY"{
"projects": [
{
"id": "cmg9t1b2c0000l408h3x9y7zq",
"name": "My project",
"language": "en",
"ratio": "9:16",
"length": "60s",
"timeZone": "America/New_York",
"postingTimes": ["09:00", "18:30"],
"channels": [
{ "id": "cmg9u4d5e0002l408k1m2n3p4", "platform": "youtube", "name": "My channel" },
{ "id": "cmg9u7f8g0004l408q5r6s7t8", "platform": "facebook", "name": "My page" }
]
}
]
}Response fields
| Field | Type | Description |
|---|---|---|
| language, ratio, length | string | What a new video of the project gets when the request does not say. |
| postingTimes | string[] | The project's posting times (HH:MM in timeZone), used by publish with when: "slot". |
| channels | object[] | The connected channels the project posts to: the default of Create posts. GET /channels lists every channel of the account. |
List channels
GEThttps://tovid.app/api/v1/channels
The social channels connected to the account. Their ids are what Create posts takes as channelIds; a channel does not have to belong to the video's project to be posted to.
Channels are connected (and disconnected) in the dashboard under Connections. When a post fails because a sign-in expired or was revoked, lastError carries the platform's answer; connect the channel again in the dashboard.
curl https://tovid.app/api/v1/channels \
-H "Authorization: Bearer $TOVID_API_KEY"{
"channels": [
{
"id": "cmg9u4d5e0002l408k1m2n3p4",
"platform": "youtube",
"name": "My channel",
"username": "mychannel",
"projectIds": ["cmg9t1b2c0000l408h3x9y7zq"],
"totalPosts": 42,
"lastPostedAt": "2026-11-11T11:30:12.000Z",
"lastError": null
}
]
}Response fields
| Field | Type | Description |
|---|---|---|
| platform | string | youtube | tiktok | facebook | instagram | threads | linkedin | telegram | discord. |
| projectIds | string[] | The projects that post to this channel by default. |
| lastError | string | null | What the platform answered when the last post failed (or a warning about the last post); null when all is well. |
Create a video
POSThttps://tovid.app/api/v1/videos
Starts a video from exactly one of url, idea or text. The answer comes back at once with status processing; the script is written in the background and, unless render is false, the video is rendered as soon as the script is ready.
url: a public page tovid can read, such as a news or blog article, or a product page (Shopify, WooCommerce, Tiki, Shopee, Lazada, App Store, Google Play...). Links to videos or posts on social networks are refused (SOCIAL_VIDEO_LINK). A page that cannot be read fails the video later, with error SOURCE_UNREADABLE or SHOP_UNREADABLE.
idea: a question or topic; tovid researches it on the web first. text: your own content, written into scenes without research.
JSON body
| Field | Type | Description |
|---|---|---|
| url | string | http(s) link of the page to make the video from. One of url / idea / text is required. |
| idea | string | A topic or question, 3 to 200 characters. |
| notes | string | With idea: what to stress or where to look, up to 300 characters. |
| text | string | Your own text, 40 to 20,000 characters. Its first line is the working title. |
| projectId | string | The project the video belongs to. Default: the account's first project. |
| language | string | Language of the script and voice as a two-letter code ("en", "vi", "es"...). Default: the project's. |
| ratio | string | "9:16" | "16:9" | "1:1". Default: the project's. |
| length | string | "60s" | "90s" | "120s". Default: the project's. |
| templateId | string | A look, by the id in its URL on https://tovid.app/templates (e.g. "news_breaking"). Default: one of the project's looks, else picked to suit the content. |
| affiliateUrl | string | A link to carry in the posts of this video. Default: the project's affiliate link. |
| render | boolean | Default true. false stops at the script (status draft, no credits): render it later with POST /videos/{id}/render, or edit it in the dashboard first. |
curl -X POST https://tovid.app/api/v1/videos \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/blog/my-article",
"language": "en",
"ratio": "9:16",
"length": "60s"
}'{
"video": {
"id": "cmh2x8k3v0001l5089qjz7w1a",
"projectId": "cmg9t1b2c0000l408h3x9y7zq",
"title": "example.com",
"status": "processing",
"stage": "read",
"error": null,
"sourceUrl": "https://example.com/blog/my-article",
"language": "en",
"length": "60s",
"createdAt": "2026-11-12T08:30:00.000Z",
"renders": []
}
}Add your own video
POSThttps://tovid.app/api/v1/videos/upload
Saves your own MP4 or MOV as a video that is ready at once, to post as it is. Give exactly one of videoUrl (a direct public link to the file, up to 100 MB; it is downloaded during the call, which can take up to a minute) or uploadId (a file you uploaded with Create an upload, up to 200 MB).
The file must be H.264 or HEVC and last 3 seconds to 10 minutes. It is kept 7 days (renders[0].expiresAt). No credits. The ratio is read from the file. A page of a social network or a video site is not a file: pass the address of the MP4 itself.
JSON body
| Field | Type | Description |
|---|---|---|
| videoUrl | string | Direct http(s) link to the MP4 / MOV, up to 100 MB. One of videoUrl / uploadId is required. |
| uploadId | string | The uploadId of Create an upload, after the file was PUT to its uploadUrl. |
| title | string | Required. Name of the video and default title of its posts, up to 500 characters. |
| caption | string | Default caption of its posts, up to 5,000 characters. |
| projectId | string | The project the video belongs to. Default: the account's first project. |
| coverSec | number | Second of the frame used as cover (YouTube thumbnail, TikTok and Instagram cover). Default: 3, or the middle of a shorter video. |
curl -X POST https://tovid.app/api/v1/videos/upload \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"videoUrl": "https://cdn.example.com/clips/store-opening.mp4",
"title": "Our new store is open",
"caption": "Come say hi this weekend."
}'{
"video": {
"id": "cmh3a1b2c0009l508d4e5f6g7",
"projectId": "cmg9t1b2c0000l408h3x9y7zq",
"title": "Our new store is open",
"status": "ready",
"stage": null,
"error": null,
"sourceUrl": null,
"language": "en",
"length": "42s",
"createdAt": "2026-11-12T09:00:00.000Z",
"renders": [
{
"id": "cmh3a1b2d000al508h8i9j0k1",
"ratio": "9:16",
"status": "completed",
"progress": 100,
"durationSec": 41.8,
"error": null,
"createdAt": "2026-11-12T09:00:00.000Z",
"expiresAt": "2026-11-19T09:00:00.000Z",
"downloadUrl": "https://…/video.mp4?X-Amz-Signature=…"
}
]
}
}Create an upload
POSThttps://tovid.app/api/v1/uploads
For a file that is not on a public link, or larger than a link allows: returns an address to PUT the file to. Send the file as the raw body of a PUT to uploadUrl with the Content-Type you declared, within the hour, then pass uploadId to Add your own video.
JSON body
| Field | Type | Description |
|---|---|---|
| size | number | Required. Size of the file in bytes, up to 200 MB. |
| type | string | "video/mp4" (default) | "video/quicktime". |
curl -X POST https://tovid.app/api/v1/uploads \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"size": 18734112, "type": "video/mp4"}'
# then send the file itself
curl -X PUT "<uploadUrl>" -H "Content-Type: video/mp4" --data-binary @clip.mp4{
"uploadId": "7days/tovid/videos/…/3f9c2a7b1d4e5f6a7b8c9d0e.mp4",
"uploadUrl": "https://…/3f9c2a7b1d4e5f6a7b8c9d0e.mp4?X-Amz-Signature=…",
"method": "PUT",
"headers": { "Content-Type": "video/mp4" },
"expiresIn": 3600
}Get a video
GEThttps://tovid.app/api/v1/videos/{id}
The video with its renders and posts. Poll this until status is ready or failed.
curl https://tovid.app/api/v1/videos/cmh2x8k3v0001l5089qjz7w1a \
-H "Authorization: Bearer $TOVID_API_KEY"{
"video": {
"id": "cmh2x8k3v0001l5089qjz7w1a",
"projectId": "cmg9t1b2c0000l408h3x9y7zq",
"title": "5 things to know about my article",
"status": "ready",
"stage": null,
"error": null,
"sourceUrl": "https://example.com/blog/my-article",
"language": "en",
"length": "60s",
"createdAt": "2026-11-12T08:30:00.000Z",
"renders": [
{
"id": "cmh2xb7pd0003l508m2n4k6s8",
"ratio": "9:16",
"status": "completed",
"progress": 100,
"durationSec": 58.4,
"error": null,
"createdAt": "2026-11-12T08:31:10.000Z",
"expiresAt": "2026-11-15T08:36:02.000Z",
"downloadUrl": "https://…/video.mp4?X-Amz-Signature=…"
}
],
"posts": []
}
}Response fields
| Field | Type | Description |
|---|---|---|
| status | string | processing | draft | ready | failed (see Life of a video). |
| stage | string | null | Step of the script while it is written, else null. |
| error | string | null | A code when the script or the newest render failed (SOURCE_UNREADABLE, EXTRACT_FAILED, INSUFFICIENT_CREDITS...). |
| title | string | The site's host name at first, the video's headline once the script is written. |
| renders | object[] | Newest first (up to 5). status: queued | voicing | rendering | completed | failed. |
| renders[].downloadUrl | string | null | Signed MP4 link for a completed render, valid until expiresAt; null otherwise. |
| posts | object[] | Posts of this video that were not canceled, in the shape of Get a post. |
List videos
GEThttps://tovid.app/api/v1/videos
The account's videos, newest first. Same shape as Get a video, without downloadUrl and posts.
Query parameters
| Field | Type | Description |
|---|---|---|
| projectId | string | Only the videos of this project. |
| limit | number | 1 to 100, default 20. |
curl "https://tovid.app/api/v1/videos?limit=10" \
-H "Authorization: Bearer $TOVID_API_KEY"{
"videos": [
{ "id": "cmh2x8k3v0001l5089qjz7w1a", "status": "ready", "title": "5 things to know about my article", "renders": [ … ], … }
]
}Render a video
POSThttps://tovid.app/api/v1/videos/{id}/render
Renders a video whose script is ready: a draft made with render: false, or a finished video again in another ratio (or after editing it in the dashboard). Holds the credits of the video's length. Not needed after a plain Create a video, which renders by itself.
Answers 409 NOT_READY while the script is still being written and 429 TOO_MANY_RENDERS when the account is at its limit of renders running at once.
JSON body
| Field | Type | Description |
|---|---|---|
| ratio | string | "9:16" | "16:9" | "1:1". Default: the video's ratio. |
curl -X POST https://tovid.app/api/v1/videos/cmh2x8k3v0001l5089qjz7w1a/render \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ratio": "16:9"}'{ "renderId": "cmh2xq1rs0005l508u9v0w1x2", "ratio": "16:9", "credits": 25000 }Create posts
POSThttps://tovid.app/api/v1/posts
Posts a video to social channels, one post per channel: right away, at a time you give, or in the project's next free posting time. The video must be ready (409 RENDER_NOT_READY otherwise). With only videoId, it is posted now to all the channels of its project.
Text you leave out is filled in: for a video tovid made, the title, caption and hashtags are written from its script; for your own video, its title and caption are used.
Each channel costs 500 credits when its post goes out, and the balance must cover all the posts of the call (402 INSUFFICIENT_CREDITS otherwise). TikTok channels use the TikTok settings saved in the project (privacy level and so on).
The answer returns the posts as they were queued (pending or scheduled). Poll Get a post, or posts on Get a video, for the result on each channel.
JSON body
| Field | Type | Description |
|---|---|---|
| videoId | string | Required. A video whose status is ready. |
| channelIds | string[] | Channels to post to (ids from List channels), up to 10. Default: the channels of the video's project. |
| when | string | "now" (default) | "at" (at scheduledAt) | "slot" (the project's next free posting time). |
| scheduledAt | string | With when: "at": ISO 8601 time with an offset, at least a minute ahead (e.g. "2026-11-13T09:00:00+07:00"). |
| title | string | Title of the posts (YouTube title, first line elsewhere), up to 500 characters. |
| caption | string | Text of the posts. |
| hashtags | string[] | Up to 30, with or without #. |
| privacy | string | "public" (default) | "unlisted" | "private", on platforms that have it (YouTube). |
| link | string | The link the posts carry: "project" (default: as the project's settings say) | "source" (the page the video was made from) | "affiliate" | "none". It goes in the first comment on Facebook and Threads, at the end of the caption elsewhere. |
curl -X POST https://tovid.app/api/v1/posts \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"videoId": "cmh2x8k3v0001l5089qjz7w1a",
"channelIds": ["cmg9u4d5e0002l408k1m2n3p4"],
"when": "at",
"scheduledAt": "2026-11-13T09:00:00+07:00",
"caption": "A short take on my article.",
"hashtags": ["news", "howto"]
}'{
"posts": [
{
"id": "cmh2y3tu0007l508y3z4a5b6",
"videoId": "cmh2x8k3v0001l5089qjz7w1a",
"projectId": "cmg9t1b2c0000l408h3x9y7zq",
"channelId": "cmg9u4d5e0002l408k1m2n3p4",
"platform": "youtube",
"channel": "My channel",
"status": "scheduled",
"title": "5 things to know about my article",
"caption": "A short take on my article.",
"hashtags": ["news", "howto"],
"privacy": "public",
"url": null,
"scheduledAt": "2026-11-13T02:00:00.000Z",
"postedAt": null,
"error": null,
"createdAt": "2026-11-12T08:40:55.000Z"
}
],
"scheduledAt": "2026-11-13T02:00:00.000Z"
}Response fields
| Field | Type | Description |
|---|---|---|
| scheduledAt | string | null | When the posts go out: the time you gave, the slot that was taken, or null for now. |
Get a post
GEThttps://tovid.app/api/v1/posts/{id}
One post: its status on its channel and, once it is up, the link of the published post.
curl https://tovid.app/api/v1/posts/cmh2y3tu0007l508y3z4a5b6 \
-H "Authorization: Bearer $TOVID_API_KEY"{
"post": {
"id": "cmh2y3tu0007l508y3z4a5b6",
"videoId": "cmh2x8k3v0001l5089qjz7w1a",
"projectId": "cmg9t1b2c0000l408h3x9y7zq",
"channelId": "cmg9u4d5e0002l408k1m2n3p4",
"platform": "youtube",
"channel": "My channel",
"status": "posted",
"title": "5 things to know about my article",
"caption": "A short take on my article.",
"hashtags": ["news", "howto"],
"privacy": "public",
"url": "https://www.youtube.com/watch?v=abc123",
"scheduledAt": "2026-11-13T02:00:00.000Z",
"postedAt": "2026-11-13T02:00:41.000Z",
"error": null,
"createdAt": "2026-11-12T08:40:55.000Z"
}
}Response fields
| Field | Type | Description |
|---|---|---|
| status | string | pending | scheduled | processing | posted | failed | canceled (see Posting to social channels). |
| url | string | null | Link of the published post once status is posted (a few platforms give none). |
| error | string | null | Why a failed post failed: the platform's own message, or a code such as INSUFFICIENT_CREDITS, CHANNEL_DISCONNECTED, INTERRUPTED. |
| caption | string | The text as it is posted, with the link appended on platforms that carry it in the caption. |
List posts
GEThttps://tovid.app/api/v1/posts
The account's posts, newest first, in the shape of Get a post. Includes posts made in the dashboard and by content plans.
Query parameters
| Field | Type | Description |
|---|---|---|
| videoId | string | Only the posts of this video. |
| projectId | string | Only the posts of this project. |
| channelId | string | Only the posts on this channel. |
| status | string | pending | scheduled | processing | posted | failed | canceled. |
| limit | number | 1 to 100, default 20. |
curl "https://tovid.app/api/v1/posts?status=scheduled&limit=50" \
-H "Authorization: Bearer $TOVID_API_KEY"{
"posts": [
{ "id": "cmh2y3tu0007l508y3z4a5b6", "platform": "youtube", "status": "scheduled", "scheduledAt": "2026-11-13T02:00:00.000Z", … }
]
}Cancel a post
DELETEhttps://tovid.app/api/v1/posts/{id}
Cancels a post that has not gone out yet (pending or scheduled); no credits are taken. A post that is processing, posted or failed answers 409 POST_NOT_CANCELABLE: tovid does not delete what is already on a platform.
curl -X DELETE https://tovid.app/api/v1/posts/cmh2y3tu0007l508y3z4a5b6 \
-H "Authorization: Bearer $TOVID_API_KEY"{ "ok": true }Publish a video
POSThttps://tovid.app/api/v1/videos/{id}/publish
A shortcut for Create posts with the video in the path: the same body without videoId, the same answer. An empty body posts now to all the channels of the video's project.
curl -X POST https://tovid.app/api/v1/videos/cmh2x8k3v0001l5089qjz7w1a/publish \
-H "Authorization: Bearer $TOVID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"when": "slot"}'{ "posts": [ … ], "scheduledAt": "2026-11-12T11:30:00.000Z" }Delete a video
DELETEhttps://tovid.app/api/v1/videos/{id}
Deletes the video with its renders. Refused with 409 RENDERING while a render is running. Posts that already went out stay on their platforms.
curl -X DELETE https://tovid.app/api/v1/videos/cmh2x8k3v0001l5089qjz7w1a \
-H "Authorization: Bearer $TOVID_API_KEY"{ "ok": true }Errors
Every failed request answers with its HTTP status and { "error": "CODE", "message": "…" }.
| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_INPUT | The body does not match the fields above (for Create a video: not exactly one of url, idea, text). |
| 400 | INVALID_URL | The url is not a public http(s) address. |
| 400 | SOCIAL_VIDEO_LINK | The url is a video or post on a social network, which tovid does not read. |
| 400 | UPLOAD_VIDEO_TYPE | Create an upload: the type is not video/mp4 or video/quicktime. |
| 400 | UPLOAD_VIDEO_TOO_BIG | Own video: the file is over 200 MB (100 MB from a link). |
| 400 | VIDEO_URL_UNREADABLE | Own video: the file could not be downloaded from videoUrl (not a direct link, an error status, or too slow). |
| 400 | UPLOAD_VIDEO_UNREADABLE | Own video: the file is not a readable video. |
| 400 | UPLOAD_VIDEO_CODEC | Own video: the video is not H.264 or HEVC. |
| 400 | UPLOAD_VIDEO_TOO_SHORT | Own video: shorter than 3 seconds. |
| 400 | UPLOAD_VIDEO_TOO_LONG | Own video: longer than 10 minutes. |
| 400 | NO_CHANNELS | Posts: no channel given and the project has none (or the ids are not active channels of the account). |
| 400 | SCHEDULE_IN_PAST | Posts: scheduledAt is missing or less than a minute ahead. |
| 400 | NO_SLOT_TIMES | Posts with when: "slot": the project has no posting times. |
| 400 | MEDIA_EXPIRES_FIRST | Posts: the MP4 is deleted before the post would go out. |
| 400 | TIKTOK_PRIVACY_REQUIRED | Posts to TikTok: the project has no TikTok settings yet (set them in the dashboard). |
| 401 | UNAUTHORIZED | No key, a malformed key, or a key that was deleted. |
| 402 | INSUFFICIENT_CREDITS | Not enough credits for the render or the posts. |
| 403 | PLAN_REQUIRED | The account's plan does not include the API (Growth and up). |
| 403 | PLAN_LIMIT | The length asked is longer than the plan allows. |
| 404 | NOT_FOUND | No video or post with this id in the account. |
| 404 | UPLOAD_NOT_FOUND | Own video: no file was PUT for this uploadId. |
| 404 | PROJECT_NOT_FOUND | No project with this id in the account. |
| 409 | NOT_READY | Render: the script is still being written, or the video has none. |
| 409 | RENDER_NOT_READY | Posts: the video has no finished render. |
| 409 | POST_NOT_CANCELABLE | Cancel a post: the post is already processing, posted, failed or canceled. |
| 409 | RENDERING | Delete: a render is running. |
| 409 | UPLOADED_VIDEO | Render: the video is an uploaded MP4, which is posted as it is. |
| 429 | RATE_LIMITED | Too many requests; wait for Retry-After seconds. |
| 429 | TOO_MANY_RENDERS | Render: the account is at its limit of renders running at once. |
| 503 | TTS_NOT_CONFIGURED | Narration is unavailable; nothing was charged. Try again later. |
Full example (Node.js)
Create a video, wait for the MP4, then post it in the project's next free posting time. Node 18 or later, no dependencies.
const API = "https://tovid.app/api/v1";
const headers = {
Authorization: `Bearer ${process.env.TOVID_API_KEY}`,
"Content-Type": "application/json",
};
async function call(method, path, body) {
const res = await fetch(API + path, { method, headers, body: body ? JSON.stringify(body) : undefined });
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
return data;
}
const { video } = await call("POST", "/videos", { url: "https://example.com/blog/my-article" });
let current = video;
while (current.status === "processing") {
await new Promise((resolve) => setTimeout(resolve, 15_000));
current = (await call("GET", `/videos/${video.id}`)).video;
}
if (current.status !== "ready") throw new Error(`Video failed: ${current.error}`);
console.log("MP4:", current.renders[0].downloadUrl);
const { posts } = await call("POST", "/posts", { videoId: video.id, when: "slot" });
for (const post of posts) console.log(post.platform, post.status, post.scheduledAt);