TikTok Videos Downloader
Download every public video from a TikTok profile (or a single video / short link) into a user-specified folder, named by upload datetime.
Unlike Instagram, TikTok does NOT require a browser mirror. yt-dlp works directly on profile and video URLs, so there is no Picnob-style scrape here. Keep the two-source resilience pattern though: yt-dlp is primary, tikwm.com (no-watermark public API) is the fallback.
Inputs
| Parameter | Required | Example |
|---|---|---|
| TikTok URL (profile, video, or short link) | yes | https://www.tiktok.com/@scout2015 |
| Output directory | yes | out/tiktok |
| Engine | no | hybrid (default), ytdlp, or tikwm |
| Timezone for filenames | no | America/Santiago (default) |
Accepted URL shapes (the script normalizes them):
- Profile:
tiktok.com/@user→ all videos - Single video:
tiktok.com/@user/video/{id} - Photo carousel:
tiktok.com/@user/photo/{id}(skipped unless--include-images) - Short links:
vm.tiktok.com/xxx,vt.tiktok.com/xxx(redirect is resolved first)
Prerequisites
yt-dlp and ffmpeg must be installed (both present on this machine). Keep yt-dlp fresh — TikTok rotates its signing, so an outdated yt-dlp is the #1 cause of failures:
yt-dlp --version || python3 -m pip install -U yt-dlp
Workflow checklist
- [ ] Step 1: Verify profile/video is public
- [ ] Step 2: (optional) Pre-fetch listing to review before downloading
- [ ] Step 3: Run download script
- [ ] Step 4: Report results
Step 1: Verify it's public
Open the URL. If it shows "This account is private" / a login wall / "Video currently unavailable", stop — this skill is for public content only.
Step 2 (optional): Pre-fetch the listing
The script lists internally, but you can inspect first. This needs no browser:
yt-dlp --flat-playlist -J "https://www.tiktok.com/@username" > listing.json
listing.json has an entries array of {id, url, title}. Pass it back in with --listing-json to download exactly that set.
Step 3: Download and rename
python3 .cursor/skills/tiktok-videos/scripts/download_profile_videos.py \
--url "https://www.tiktok.com/@username" \
--output-dir out/tiktok \
--engine hybrid \
--timezone America/Santiago
The script:
- Resolves short links and lists the profile's videos (yt-dlp
--flat-playlist; falls back to tikwmuser/postsif yt-dlp listing fails) - Downloads each video — yt-dlp primary, tikwm no-watermark fallback per item
- Reads the exact upload
timestamp(yt-dlpinfo.json) orcreate_time(tikwm) - Renames to
YYYY-MM-DD_HH-MM-SS.mp4in the chosen timezone - Writes
{output_dir}/videos_manifest.json(id, url, title, uploadDate, source, file)
Useful flags: --max N (cap count), --engine tikwm (force no-watermark), --engine ytdlp (no third-party dependency), --include-images, --delay-ms.
Collisions: two videos at the same second get _2, _3 suffixes.
Step 4: Report to user
Summarize: handle/URL, output path, count downloaded, any failures, engine source per file, and list filenames sorted newest-first.
Engine strategy (why hybrid)
| Engine | Strength | Weakness |
|---|---|---|
| yt-dlp (primary) | Most maintained, handles TikTok signing, whole-profile in one call, no third-party | Occasionally serves a watermarked render; breaks for a few days when TikTok changes signing |
| tikwm (fallback) | Guaranteed no-watermark + exact create_time, simple JSON |
Third-party service, ~1 req/sec rate limit, can go down |
hybrid runs yt-dlp first and only calls tikwm for items yt-dlp fails on — mirroring the instagram-videos Picnob+ImgInn dual-source resilience.
Anti-patterns
- Do not start by hand-scraping the TikTok web page —
yt-dlpalready handles listing. (This is the opposite of Instagram, where direct tools 401.) - Do not run with a stale yt-dlp. Update it before blaming the site.
- Do not hammer tikwm faster than ~1 req/sec — it rate-limits.
- Do not download private / age-gated / region-locked content.
- Do not commit large
out/video folders — add the output dir to.gitignore.
Utility scripts
| Script | Purpose |
|---|---|
scripts/download_profile_videos.py |
List + download (yt-dlp + tikwm fallback) + datetime rename + manifest (main entry point) |
Additional resources
- Real run example: examples.md
- For Instagram (needs a browser mirror), use the
instagram-videosskill.