How to bulk upload TikTok videos
TikTok's own uploader takes one video at a time. If you have 200 UGC clips and 20 creator accounts, you need a bulk path: upload everything once, give each file a caption, and let a schedule spread the posts. This guide shows the dashboard way and a script.
Updated
What bulk upload means here
Bulk upload is three jobs that tools often blur together.
- Get the files somewhere public. Platforms fetch media from a URL when the post publishes.
- Turn each file into a post. One video file is one post. One set of images is one slideshow post.
- Assign posts to accounts and times. This is where a campaign does the work.
Keep the three apart and the process stays simple, whether you upload 30 files or 3,000.
Prepare the files
| Type | Use | Why |
|---|---|---|
| Video | MP4, H.264, AAC, 1080x1920, 3 to 90 s | Works on TikTok and also on Reels, Facebook Reels and Shorts |
| Slideshow images | JPEG, 1080x1920 | 9:16 fills the TikTok screen. The first image is the cover. |
| File names | A stable ID, like brandx-hook-014.mp4 | Lets you match a post back to your sheet or brief |
Export clean files from your editor. Don't download from TikTok and re-upload, since that adds a watermark. Burn in on-screen text if it matters, because auto-captions vary by platform.
Option 1: bulk upload in the dashboard
- Open the composer in the dashboard and drop in all your files at once. Each video becomes its own post.
- For slideshows, group the images for each post in order.
- Add captions. Write one per post, or one shared caption and edit the posts that need their own.
- Pick the TikTok accounts. You can also add Instagram, Facebook and YouTube accounts if the clip suits them.
- Set the cadence: posts per account per day, posting times and time zone.
- Preview the full schedule and the credits it will use, then confirm.
This is the fastest path for agencies and brands that get a batch of UGC from creators each week. No code is involved.
Option 2: bulk upload with a script
If the files sit in a folder or come from a generator, script it. You have two ways to give us media:
- Your own HTTPS URLs. A public bucket or CDN URL that returns the file directly. It must still work when the post publishes.
- Upload to UGC API.
POST /v1/mediatakes the raw file as the body and returns{ "url": "..." }. One file per request. We keep it until 3 days after the last post that uses it. See media.
Uploading one file with curl:
curl https://ugcapi.dev/v1/media \
-H "Authorization: Bearer ugc_live_..." \
-H "Content-Type: video/mp4" \
--data-binary @brandx-hook-014.mp4A Node script that uploads a folder of MP4 files, builds one post per file, previews the schedule and then creates the campaign:
import { readdir, readFile } from "node:fs/promises";
const KEY = process.env.UGCAPI_KEY;
const DIR = "./clips";
const headers = { "Authorization": "Bearer " + KEY };
const files = (await readdir(DIR)).filter((f) => f.endsWith(".mp4")).sort();
const posts = [];
for (const file of files) {
const res = await fetch("https://ugcapi.dev/v1/media", {
method: "POST",
headers: { ...headers, "Content-Type": "video/mp4" },
body: await readFile(DIR + "/" + file),
});
const { url } = await res.json();
posts.push({
format: "video",
media: [url],
caption: captions[file] ?? "New drop #ugc",
external_id: file.replace(".mp4", ""),
options: { tiktok: { privacy: "PUBLIC_TO_EVERYONE" } },
});
}
const campaign = {
name: "Brand X - batch 7",
accounts: ["acc_tt_01", "acc_tt_02", "acc_tt_03", "acc_tt_04"],
cadence: { per_day: 2 },
times: ["12:00", "19:00"],
timezone: "America/Los_Angeles",
posts,
};
const send = (body) => fetch("https://ugcapi.dev/v1/campaigns", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then((r) => r.json());
const preview = await send({ ...campaign, dry_run: true });
console.log(preview.posts, "posts,", preview.credits_needed, "credits,", preview.first_at, "to", preview.last_at);
const created = await send(campaign);
console.log("campaign", created.id);captions is your own lookup, for example a CSV loaded into an object keyed by file name. The external_id comes back in results and webhooks, so you can match each published TikTok to its file.
Per-account captions
A campaign gives each post to one account in turn, so each post's caption is what that account publishes. If you want the same clip on several accounts with different captions, add it to posts several times, each with its own caption. Round-robin will put the copies on different accounts. This is a common way to test hooks: same video, three caption angles, three creators.
Slideshows in bulk
For TikTok photo posts, upload each image with POST /v1/media, one request per image, and keep the order. Then add a post with format: "slideshow" and the image URLs in media. TikTok accepts up to 35 images per photo post. Set options.tiktok.auto_music to true if you want TikTok to add a sound. More in the slideshow API page.
Uploading from a spreadsheet
Many teams track UGC in a sheet: one row per clip, with a file link, a caption, the client and the target accounts. That sheet is already your bulk upload. Export it to CSV and feed it to the script, or connect it with the n8n-nodes-ugcapi node in n8n so new rows become posts on a schedule. Keep these columns:
| Column | Maps to | Example |
|---|---|---|
| File URL | media | https://cdn.example.com/brandx/014.mp4 |
| Format | format | video or slideshow |
| Caption | caption | Packing hack nobody told me #travel |
| Clip ID | external_id | brandx-hook-014 |
| AI-made | options.tiktok.ai_generated | true for AI avatars |
Group rows by client, then send one campaign per client. Write the returned post IDs back to the sheet so anyone can see what is scheduled.
Check before you send hundreds
- Open three or four media URLs in a private browser window. If they don't download the file, platforms can't either.
- Run the campaign with
dry_run: trueand readfirst_at,last_atandcredits_needed. A wrong time zone shows up here. - Post one clip to one test account first. Fix captions and privacy settings before the full batch goes out.
Limits worth knowing
- One campaign takes up to 1,000 posts. Split larger batches into several campaigns.
- Credits for the whole campaign are reserved when you create it. Without enough credits, the request fails with 402 and nothing is scheduled.
- TikTok caps posts per creator per day. Keep
per_daylow and add accounts rather than raising it. - Uploaded files are deleted 3 days after their last post. Keep your masters elsewhere.
After the upload
Set a webhook to hear about post.published and post.failed. Failed posts are refunded, and POST /v1/posts/:id/retry sends them again once the cause is fixed. List a batch with GET /v1/posts?campaign_id=... to see status and results. Read webhooks for payloads.
200 clips across 20 accounts is 200 posts, or $2 at $10 per 1,000. There are no per-account fees. See pricing.
FAQ
Can I upload several files in one API request?
No. POST /v1/media takes one file per request. Loop over your files and collect the URLs. In the dashboard you can drop many files at once.
Do I have to upload files to UGC API?
No. Any public HTTPS URL that returns the file directly works, as long as it is still valid when the post publishes.
How many videos can I schedule in one go?
Up to 1,000 posts per campaign. For more, create several campaigns.
Can I bulk upload TikTok slideshows?
Yes. Upload the images, then add one slideshow post per set of images, with the image URLs in order. The first image is the cover.