Track a TikTok creator's new videos and growth with Python
A small script that checks a list of TikTok creators once a day and tells you what changed: how many followers they gained or lost, which videos they posted, and which posts picked up views. Each creator costs two requests per run.
Brands watch creators before and during a campaign; agencies watch competitors' creators; talent managers watch their own roster. They all need the same daily numbers, and TikTok's official APIs don't provide them for accounts you don't own. The TikTok Video & Creator API reads the public profile and posts without a TikTok login, and this guide turns two of its endpoints into a growth log.
Setup
You need Python 3, requests (pip install requests) and a key from the RapidAPI listing; the free plan's 100 requests are enough to try it. Put the key in an environment variable called RAPIDAPI_KEY. Ten creators checked daily use about 600 requests a month.
import json
import os
import time
from datetime import date
from pathlib import Path
import requests
BASE = "https://tiktok-video-creator-api.p.rapidapi.com"
HEADERS = {
"X-RapidAPI-Key": os.environ["RAPIDAPI_KEY"],
"X-RapidAPI-Host": "tiktok-video-creator-api.p.rapidapi.com",
}
def get(path, **params):
"""GET a path and return the JSON. Retries rate limits and upstream errors."""
for attempt in range(4):
response = requests.get(BASE + path, headers=HEADERS, params=params, timeout=30)
if response.status_code in (429, 502, 503) and attempt < 3:
time.sleep(2 ** attempt)
continue
response.raise_for_status()
return response.json()
Read the profile
GET /v1/tiktok/users/{username} returns the public profile. Its follower, like and post counts are exact, so it's the source for growth numbers.
curl --request GET \
--url 'https://tiktok-video-creator-api.p.rapidapi.com/v1/tiktok/users/zachking' \
--header "X-RapidAPI-Key: $RAPIDAPI_KEY" \
--header 'X-RapidAPI-Host: tiktok-video-creator-api.p.rapidapi.com'
Real response from 2026-09-24, avatar link shortened
{
"id": "68616495085350913",
"username": "zachking",
"nickname": "Zach King",
"avatar": "https://p19-common-sign.tiktokcdn-us.com/…",
"verified": true,
"sec_uid": "MS4wLjABAAAANnzJ9pdfxtlf3m5Ku_M-rCXVitJC13w4GavqyUD3RE8h1cZ-7O9FT-uQ9rCJjU_E",
"url": "https://www.tiktok.com/@zachking",
"bio": "Bringing a little more wonder into the world, 15 seconds at a time",
"bio_link": "https://youtu.be/ulVscDewRPI?si=-65KnrnwSiGHDeT2",
"private": false,
"language": "en",
"created_at": "2016-02-29T07:29:22Z",
"stats": { "followers": 86890382, "following": 182, "likes": 1301093987, "videos": 617, "friends": 119 }
}
A private account returns 403; an unknown, banned or removed one returns 404.
Read the newest posts
GET /v1/tiktok/users/{username}/videos returns about 30 posts per page, newest first, with pinned posts at the top of the first page. Pass the sec_uid from the profile; it saves a lookup, so the call is faster.
Real response from 2026-09-24, one of 31 posts shown, some fields and media links left out
{
"username": "zachking",
"sec_uid": "MS4wLjABAAAANnzJ9pdfxtlf3m5Ku_M-rCXVitJC13w4GavqyUD3RE8h1cZ-7O9FT-uQ9rCJjU_E",
"count": 31,
"has_more": true,
"next_cursor": "1771605525067",
"videos": [
{
"id": "7686898345519942925",
"url": "https://www.tiktok.com/@zachking/video/7686898345519942925",
"description": "when you give your friend the aux cord… ",
"created_at": "2026-09-18T15:31:42Z",
"duration": 6,
"is_photo_post": false,
"is_pinned": false,
"cover": "https://p19-common-sign.tiktokcdn-us.com/…",
"hashtags": [],
"stats": { "plays": 15100000, "likes": 2600000, "comments": 5901, "shares": 157200, "bookmarks": 161956 },
"author": { "id": "68616495085350913", "username": "zachking", "…": "…" },
"music": { "id": "7686898373269474062", "title": "original sound", "author": "Zach King", "…": "…" }
}
]
}
The full video object also has width, height, is_ad, dynamic_cover, play_url and images (for photo posts). The tracker only needs the first page; the user videos guide shows how to page through a whole account.
Take a snapshot
One snapshot per creator: the profile counts plus the first page of posts, keyed by video ID.
def snapshot(username):
profile = get(f"/v1/tiktok/users/{username}")
page = get(f"/v1/tiktok/users/{username}/videos", sec_uid=profile["sec_uid"])
return {
"followers": profile["stats"]["followers"],
"likes": profile["stats"]["likes"],
"posts": profile["stats"]["videos"],
"videos": {
v["id"]: {
"created_at": v["created_at"],
"plays": v["stats"]["plays"],
"likes": v["stats"]["likes"],
"comments": v["stats"]["comments"],
"pinned": v["is_pinned"],
"url": v["url"],
}
for v in page.get("videos") or []
},
}
Compare with the last run
The script loads the latest snapshot from an earlier day, prints the differences and saves today's snapshot. A second run on the same day compares with the earlier day, not with itself.
Answers are cached for up to 24 hours, counted from when the previous request fetched them. A job that runs at the same time every day arrives just under 24 hours later, so it often gets the previous run's cached answer. The script spots this: when every number matches the last run exactly, it keeps the older snapshot and prints a note instead of a zero change. The next run then shows the change since that older snapshot, so a day's change can show up a day late, but none is lost. Each report says which date it's measured from.
KEYS = ["followers", "likes", "posts", "videos"]
def same_answer(old, new):
"""True when every number matches the last run: a cached answer."""
return all(old[k] == new[k] for k in KEYS)
def compare(old, new):
print(f' since {old["as_of"]}:')
print(f' followers {new["followers"]:,} ({new["followers"] - old["followers"]:+,})')
fresh = [v for vid, v in new["videos"].items()
if vid not in old["videos"] and not v["pinned"]]
for v in sorted(fresh, key=lambda v: v["created_at"]):
print(f' new post {v["created_at"][:10]} {v["plays"]:,} plays {v["url"]}')
for vid, v in new["videos"].items():
if vid in old["videos"]:
gained = v["plays"] - old["videos"][vid]["plays"]
if gained >= 100_000:
print(f' +{gained:,} plays {v["url"]}')
CREATORS = ["zachking"] # the accounts you want to track
SNAPSHOTS = Path("snapshots")
SNAPSHOTS.mkdir(exist_ok=True)
today = date.today().isoformat()
earlier = [p for p in sorted(SNAPSHOTS.glob("*.json")) if p.stem < today]
old = json.loads(earlier[-1].read_text()) if earlier else {}
new = {}
for username in CREATORS:
s = snapshot(username)
s["as_of"] = today
print(f'@{username}: {s["followers"]:,} followers, {s["posts"]} posts, '
f'{len(s["videos"])} on the first page')
prev = old.get(username)
if prev and same_answer(prev, s):
print(f' same numbers as the {prev["as_of"]} run (cached answer); '
'the change shows in the next run')
s = prev # keep the date these numbers are really from
elif prev:
compare(prev, s)
new[username] = s
(SNAPSHOTS / f"{today}.json").write_text(json.dumps(new, indent=1))
First run, built from the two responses above
@zachking: 86,890,382 followers, 617 posts, 31 on the first page
From the second day on, each creator also gets lines in this format
since <date of the snapshot compared with>:
followers <count> (<change>)
new post <date> <plays> plays <video url>
+<gained> plays <video url>
Run it every day
Save the script as track.py and run it once a day with cron:
0 9 * * * cd /path/to/tracker && python3 track.py >> track.log
The snapshots are plain JSON files, one per day, so you can load them into a spreadsheet or a database later and chart each creator's growth.
Ideas to extend it
- Median views of the last 30 posts: a steadier "typical reach" number than the average, because one viral post doesn't skew it. Leave pinned posts out.
- Engagement rate per post:
(likes + comments + shares + bookmarks) / plays. - Comments on new posts:
GET /v1/tiktok/videos/{id}/commentsreturns a page of comments with text, likes and language (see the comments guide). - A whole niche instead of a fixed list: find creators with
GET /v1/tiktok/search/users?q=, through a keyword search or through a hashtag.
Things to know
- Counts can be up to 24 hours old. Answers are cached for 24 hours from when they were fetched, and a cached answer doesn't restart that clock. A daily run can get the previous run's answer; the script then reports the change a day later (see Compare with the last run). A second run on the same day returns the same numbers and still costs requests.
- Counts inside videos are rounded by TikTok for large numbers (15,100,000 plays), so small changes on big posts only show once they cross TikTok's rounding step. The profile's follower, like and post counts are exact.
- The first page is the newest ~30 posts plus pinned posts. The tracker compares those; posts that drop off the first page stop being compared.
- Media links (
cover,play_url,avatar) expire after a few hours. Don't store them as permanent links. - Errors:
400bad parameter,403private account,404unknown or removed account,429plan limit,502TikTok failed after our retries (thegethelper retries it). - This API is unofficial and not affiliated with TikTok. It reads public data only; no TikTok login is needed.
Next steps
- Get every video from a creator, not just the first page.
- See who follows a creator.
- API docs for all 13 endpoints.
- No code? Run the TikTok Profile & Videos Actor on Apify: it collects profiles and their latest videos and exports them to CSV or Excel.
Plans
Each creator costs two requests per run: the profile and one page of posts. A free plan covers testing. Plans, monthly quotas and rate limits are listed on the RapidAPI listing. For more volume or a custom plan, use Contact provider on the listing.