A creator may look like a good fit for a campaign based on their profile and a few successful videos, but their recent feed tells a more important story: do they continue to post regularly about the topic you need? Manual checks quickly become outdated, and a one-off review can easily focus only on the most prominent posts. For an agency or brand, it is more useful to collect a repeatable sample of public videos, save a snapshot dated to the time of review, and assess topical relevance using consistent rules.

Below is a practical process for assessing a TikTok creator’s public feed. It uses the jsonscraper TikTok Scraper API and Python to retrieve posts through a documented endpoint, save the original JSON, map confirmed fields into a table, and calculate the share of videos that fit a defined topic. If you wish, you can pass descriptions to a text classifier as a separate, controlled next step.
First, define what “relevant” means
Suppose a team is choosing a creator for a campaign about making coffee at home. Follower count and the popularity of individual videos may help describe reach, but on their own they do not answer how closely the feed relates to coffee. For an initial filter, you need content signals: whether a video description mentions coffee preparation, equipment, recipes, or reviews; how many posts on the topic appeared in the observation window; and what share of the collected posts are topical.
Before collecting data, write down the criteria in a short vocabulary, such as “espresso,” “coffee grinder,” “latte,” and “brewing.” Add exceptions and ambiguous terms. “Coffee” might appear in a joke, a sponsored integration, or an incidental context, so an automatic text match is an initial label—not a definitive assessment of the creator. Use at least three categories: clear match, possible match, and irrelevant video.
Define the sample in advance, too. For example, you might analyze the last 20 posts returned at each weekly run. This is a condition of your monitoring setup, not a promise that the API will return exactly that number: the availability and composition of results depend on the response to a specific request. Record the collection time, number of items returned, and request parameters so you can compare snapshots without mixing different rules.
Requesting a creator’s feed
In the jsonscraper documentation, the GET /getUserFeed endpoint takes a required query parameter—the username or profile URL. Optional parameters include count, region, sort_type, with_pinned_posts, and max_cursor. The API key is passed as license_key in the query string; keep it in an environment variable rather than in your source code. Details of the endpoints and parameters are available in the official TikTok API documentation.
In the response, first check the outer status, then access the tiktok object. For a user feed, the post list is at tiktok.aweme_list. The observed schema includes a video ID, description text, publication time, author, URL, and statistics object. Some fields may be missing, so your handler should retain missing values as empty rather than replacing them with made-up numbers.
The following minimal example is for Python 3.8+ and the requests library. Save your key in JSONSCRAPER_TIKTOK_LICENSE_KEY. The code makes one request, checks the HTTP response and the API’s outer status, saves the original JSON, and prints normalized rows.
import json
import os
from datetime import datetime, timezone
from pathlib import Path
import requests
API_KEY = os.environ["JSONSCRAPER_TIKTOK_LICENSE_KEY"]
PROFILE = "creator_username"
params = {
"license_key": API_KEY,
"query": PROFILE,
"count": 20,
"region": "US",
"sort_type": 0,
"with_pinned_posts": 0,
}
response = requests.get(
"https://tiktok.evelode.com/getUserFeed",
params=params,
timeout=30,
)
response.raise_for_status()
payload = response.json()
if payload.get("status") != "ok":
raise RuntimeError("TikTok API returned a non-ok status")
tiktok = payload.get("tiktok") or {}
items = tiktok.get("aweme_list") or []
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
Path("snapshots").mkdir(exist_ok=True)
Path(f"snapshots/{PROFILE}_{stamp}.json").write_text(
json.dumps(payload, ensure_ascii=False, indent=2),
encoding="utf-8",
)
rows = []
for item in items:
stats = item.get("statistics") or {}
author = item.get("author") or {}
rows.append({
"aweme_id": item.get("aweme_id"),
"created_at": item.get("create_time"),
"description": item.get("desc") or "",
"share_url": item.get("share_url"),
"creator": author.get("unique_id") or author.get("nickname"),
"play_count": stats.get("play_count"),
"digg_count": stats.get("digg_count"),
"comment_count": stats.get("comment_count"),
"share_count": stats.get("share_count"),
})
print(json.dumps(rows, ensure_ascii=False, indent=2))
For this endpoint, sort_type is documented as 0 for the recent feed and 1 for the popular feed. The example uses recent posts and excludes pinned videos with with_pinned_posts=0, so an older pinned post does not enter the sample of new posts. If you need a different mode, save the parameter in the snapshot metadata and use it consistently in every run.
Processing the response: from JSON to a reproducible table
It is useful to save the original response before transforming it. That lets you verify normalization, investigate why a row is empty, and adapt the handler if optional fields change in future responses. Alongside the JSON, store the collection time in UTC, the requested profile, the parameters, and your script version in your working storage. Do not put the API key in an archive or log.
For repeat runs, use aweme_id as the post identifier when the field is present. Deduplicating by this ID prevents the same video from being counted as multiple posts when it is returned again. At the same time, keep separate snapshots of the statistics: view and reaction counts describe the time of observation, not unchanging characteristics of the video. If a statistics field is missing, leave it empty and exclude that record from calculations for the corresponding metric.
In the schema, the publication time is identified as create_time. For your analytics table, convert it to a date and store both the original value and a normalized UTC time. Check several real records from the response before calculating, to confirm the expected type and units. This is especially important when building time charts: an error in time conversion shifts posts between days and distorts weekly comparisons.
You can build a basic topic classifier without a model: normalize the text, look for specified key phrases, and label matches. For more useful labeling, prepare a set of manually checked descriptions and rules for borderline cases. Then compare a simple dictionary filter with human labels: identify expressions that produce false matches and update the dictionary. This gives you a transparent baseline for evaluating subsequent use of AI.
import re
KEYWORDS = (
"кофе", "эспрессо", "кофемолка", "латте", "заваривание",
)
def normalize(text):
text = (text or "").casefold()
return re.sub(r"\s+", " ", text).strip()
def classify(description):
text = normalize(description)
matches = [word for word in KEYWORDS if word in text]
return {
"topic_match": bool(matches),
"matched_terms": matches,
}
for row in rows:
row.update(classify(row["description"]))
A keyword in a description can be a useful signal, but the classifier should return an explainable reason—a list of matched terms, not just a “yes/no” label. For language models, use a structured prompt: provide the video description and fixed categories, and ask for a category, a brief rationale, and a confidence level. Store the original text and the model’s response separately. Have a person check decisions that affect budgets or partner selection against links to the posts.
Calculate metrics with a clear denominator
A simple report can include the share of topical videos and posting frequency. The share is the number of videos confirmed as topically relevant divided by the number of videos that could be classified. Always show both numbers—for example, “7 of 18 descriptions checked.” This makes the result understandable and helps flag a percentage based on too small a sample.
Posting frequency can be expressed as the number of posts returned during a fixed period, such as the last 30 days. Set the period boundaries using the collection time and publication dates. With weekly snapshots, store the result of every run rather than just an updated summary row; this makes changes in topic and activity visible. Do not treat the number of items collected as a complete archive of the creator’s posts—analyze the sample actually returned and state its rules clearly.
View, like, comment, and repost statistics can provide context for each video. Compare absolute values with care: an older post may have had longer to accumulate reactions. To compare posts from one creator, record the time of each collection and examine how the same video changes between snapshots. Do not call the difference between two snapshots a “growth rate” unless the interval and measurement availability have been recorded.
A useful summary record for each profile includes the collection date, selected parameters, number of posts returned, number of descriptions classified, number of topical matches, example URLs, and a brief note on ambiguous cases. This table lets a colleague check the conclusion and another analyst repeat the method on the next profile.
Pagination and scheduling
If you need the next page of results, the documentation for the profile feed uses max_cursor from the previous response. Pass it without modification: the cursor is an opaque value that should be saved and returned to the API. Also check has_more in the response if you plan to request additional pages. Limit the number of pages or the total request budget so collection ends predictably.
cursor = None
all_items = []
max_pages = 3
for _ in range(max_pages):
page_params = dict(params)
if cursor is not None:
page_params["max_cursor"] = cursor
page_response = requests.get(
"https://tiktok.evelode.com/getUserFeed",
params=page_params,
timeout=30,
)
page_response.raise_for_status()
page_payload = page_response.json()
if page_payload.get("status") != "ok":
raise RuntimeError("TikTok API returned a non-ok status")
page = page_payload.get("tiktok") or {}
all_items.extend(page.get("aweme_list") or [])
if not page.get("has_more"):
break
cursor = page.get("max_cursor")
if cursor is None:
break
Choose the run frequency for the task: a manual run is enough for an initial assessment; a weekly schedule may help track changes across a list of creators without constant polling. You can run it through cron, a CI job, or an orchestrator. The process is simple: retrieve profiles from a table, make requests with concurrency limits, save snapshots, check the share of successful responses, and send a summary to a working table. Retry after temporary errors with a limited number of attempts and a delay, and log authorization errors separately to check the key and configuration.
Where AI is useful—and where review is needed
AI can help classify descriptions that contain slang, multiple languages, or wording that lacks an exact keyword. First, define a closed list of categories and ask the model to use only the description text. Then measure performance on a small, manually labeled sample: compare the model’s categories with an editor’s decisions, and review errors and ambiguous cases separately. It is better to report the share of model labels that agree with manual labeling and the number of examples checked than an uninterpretable overall score.
Do not give a model the task of “choosing the best creator” without criteria. Instead, break the process into measurable signals: topical share, consistency, examples of relevant videos, and a range of public reactions. A model can briefly summarize these fields and point to supporting links; the final decision stays with the team, which can account for the campaign format, audience, and arrangements with the creator.
If your analysis covers sponsored integrations, separately check the creator’s disclosure of their relationship with the brand in the posts themselves and against applicable requirements. The U.S. Federal Trade Commission’s guidance explains that a connection with a brand should be clearly disclosed alongside the endorsement; a profile bio or a hidden part of the text may not be enough to give the audience clear notice. See the FTC guidance for social media creators. Monitoring public data can help gather material for review, but assessing compliance requires context about the specific post and campaign.
What you will get
Once the pipeline is set up, the team gets dated snapshots of a public feed, a table of posts with descriptions and available metrics, and a repeatable calculation of topical share. These can help you shortlist profiles for further review more quickly, spot shifts in topic, and document the reasons for an assessment. The method remains auditable: the original JSON, request parameters, classification rules, and links to videos make it possible to trace the path from data to conclusion.
For your first test, run the script on one profile, save the raw response, and manually check several classifications. Then set the topic vocabulary, sampling rules, and report format. Once the result is reproducible, add a schedule and a queue of profiles. For endpoint and parameter details, consult the API documentation; for a product overview and available integration options, visit the TikTok Scraper API page.
Practical checklist before launch
- Write down the campaign topic, inclusion vocabulary, and examples of borderline cases.
- Set the sample window,
countvalue, sorting, and pinned-post setting. - Store the original JSON, collection date, and parameters separately from the secret key.
- Use
aweme_idfor deduplication when the identifier is present. - Distinguish a missing value from zero, and do not average metrics without a clear basis.
- Show the sample size alongside topical share and include example links.
- Check AI classifications against a manually labeled sample before using them in campaign decisions.
- Update assessments on a schedule and compare snapshots collected under the same rules.