Mega-influencers are easy to find and hard to afford. Their engagement is also frequently underwhelming: when an account passes a few million followers, the audience becomes too broad, too passive, and too sceptical of sponsored content. Micro-influencers, generally defined as creators in the 10,000-100,000 follower range, consistently post higher engagement rates, command lower per-post fees, and feel more like a friend's recommendation than a billboard. For a marketing team, that means better ROI per dollar - if you can actually find them.
That "if" is the entire problem. TikTok's native search is built for entertainment discovery, not influencer sourcing. You cannot filter by follower tier, you cannot sort by engagement rate, and you cannot export anything. This post walks through a complete programmatic discovery workflow using the TikLiveAPI endpoints: from a niche keyword to a ranked CSV your outreach team can work through on Monday morning.
The follower tiers used across the industry are not standardized, but the most common definition looks like this:
Micro-influencers are the sweet spot for most performance campaigns because they cover a specific topic (fitness for new moms, sourdough baking, indie skincare) with an audience that actually believes them. They are also a discovery nightmare without tooling: you cannot just open TikTok and scroll past 200 mega-creators to find them.
Before you write a single line of code, agree on the signals your shortlist will be scored against. Five usually cover it:
Each of these maps cleanly to a field in the TikTok API response. We will pull them in steps.
You need a starting pool of posts that are clearly on-topic. Two endpoints work well as seeds: /challenge-posts/ when you have a strong hashtag, and /search-video/ when you want a broader keyword sweep with sort filters.
The hashtag route is usually higher-precision. Combine it with a region filter if you only care about one market.
import requests
API_KEY = "YOUR_API_KEY"
BASE = "https://api.tikliveapi.com"
HEADERS = {"X-Api-Key": API_KEY}
def search_videos(keyword, count=35, region=None, publish_time=30, sort_by=0):
"""Seed candidates with keyword search. publish_time=30 = last month."""
params = {
"keyword": keyword,
"count": count,
"publish_time": publish_time,
"sort_by": sort_by,
}
if region:
params["region"] = region
r = requests.get(f"{BASE}/search-video/", headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
return r.json()
def challenge_posts(challenge_id, count=35, region=None):
"""Seed candidates from a specific hashtag (challenge_id)."""
params = {"challenge_id": challenge_id, "count": count}
if region:
params["region"] = region
r = requests.get(f"{BASE}/challenge-posts/", headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
return r.json()
If you only know the hashtag text (say, cleanbeauty) and not its numeric ID, look it up first via /challenge-info-name/, then pass the returned id into /challenge-posts/.
Each seed response returns a videos array (along with cursor and hasMore for pagination). Every video item carries an author object - that is your candidate. Deduplicate aggressively because popular hashtags surface the same creators many times.
def collect_candidates(seed_response, candidates):
for v in seed_response.get("videos", []):
author = v.get("author") or {}
uid = author.get("id") or author.get("uid")
if not uid:
continue
if uid not in candidates:
candidates[uid] = {
"userid": uid,
"username": author.get("unique_id") or author.get("uniqueId"),
"nickname": author.get("nickname"),
"seed_posts": [],
}
candidates[uid]["seed_posts"].append(v.get("aweme_id"))
return candidates
# Page through a hashtag
candidates = {}
resp = challenge_posts("7164308107226054661", count=35, region="US")
collect_candidates(resp, candidates)
while resp.get("hasMore") and len(candidates) < 500:
resp = requests.get(
f"{BASE}/challenge-posts/",
headers=HEADERS,
params={"challenge_id": "7164308107226054661", "count": 35,
"cursor": resp.get("cursor"), "region": "US"},
timeout=30,
).json()
collect_candidates(resp, candidates)
500 candidates from a single hashtag is usually plenty; if you need more, run the same flow against 3-5 related hashtags and merge the dictionaries.
Now hydrate each candidate with their profile so you can apply the 10K-100K filter. Use /userinfo-by-username/ when you have the handle, or /userinfo-by-id/ when you only have the numeric ID.
The response contains a user object and a stats object, both using camelCase. stats.followerCount is the field you want.
def userinfo(username):
r = requests.get(
f"{BASE}/userinfo-by-username/",
headers=HEADERS,
params={"username": username},
timeout=30,
)
r.raise_for_status()
return r.json()
def in_tier(profile, low=10_000, high=100_000):
stats = profile.get("stats") or {}
fc = stats.get("followerCount", 0)
return low <= fc <= high
tier_filtered = {}
for uid, c in candidates.items():
if not c["username"]:
continue
try:
p = userinfo(c["username"])
except requests.HTTPError:
continue
if in_tier(p):
c["profile"] = p
c["followers"] = p["stats"]["followerCount"]
c["verified"] = p["user"].get("verified", False)
c["signature"] = p["user"].get("signature", "")
tier_filtered[uid] = c
A single seed batch of 500 candidates typically drops to 60-120 micro-tier accounts after this filter, which is exactly what you want.
Follower count alone is meaningless - a 50K account with 200 views per post is dead weight. Pull each creator's recent posts via /user-posts/ and compute a median engagement rate.
Note the casing quirk: the top-level pagination flag is hasMore (camelCase), but inside each video the fields are snake_case - play_count, digg_count, comment_count, share_count.
import statistics
def recent_posts(userid, count=20):
r = requests.get(
f"{BASE}/user-posts/",
headers=HEADERS,
params={"userid": userid, "count": count},
timeout=30,
)
r.raise_for_status()
return r.json().get("videos", [])
def engagement_rate(videos):
rates = []
for v in videos:
plays = v.get("play_count") or 0
if plays < 500: # too few plays to be statistically useful
continue
engagements = (v.get("digg_count") or 0) + \
(v.get("comment_count") or 0) + \
(v.get("share_count") or 0)
rates.append(engagements / plays)
if not rates:
return 0.0, 0
return statistics.median(rates), len(rates)
for uid, c in tier_filtered.items():
videos = recent_posts(uid, count=20)
er, n = engagement_rate(videos)
c["engagement_rate"] = er
c["posts_sampled"] = n
c["recent_videos"] = videos[:5]
# cadence: are they posting recently?
import time
cutoff = time.time() - 14 * 86400
c["recent_count_14d"] = sum(1 for v in videos if (v.get("create_time") or 0) >= cutoff)
Industry benchmarks for TikTok micro-creators tend to sit between 5% and 12% engagement. A creator at 0.5% is probably running a bot-padded follower count; a creator at 25% is either genuinely viral or has a tiny play base that is gaming the ratio. Keep both extremes in mind during review.
A 50K creator with 8% engagement is only useful to you if they actually talk about your category. Score each candidate by counting how many of your niche keywords show up in their bio (user.signature) and in recent post titles.
NICHE_TERMS = [
"skincare", "skin care", "skinroutine", "glowup",
"cleanbeauty", "sensitiveskin", "acne", "moisturizer",
]
def niche_score(signature, videos, terms):
blob_parts = [signature or ""]
for v in videos:
if v.get("title"):
blob_parts.append(v["title"])
blob = " ".join(blob_parts).lower()
hits = sum(1 for t in terms if t.lower() in blob)
return hits
for c in tier_filtered.values():
c["niche_score"] = niche_score(
c.get("signature", ""),
c.get("recent_videos", []),
NICHE_TERMS,
)
You can refine this with weighted keywords (brand names worth 3, generic category terms worth 1) or with embedding similarity if you already have an NLP pipeline. For most brand teams, a plain keyword count is enough to separate "yes" from "maybe" from "no".
Two regions to check: the creator's declared region on their profile, and the regions appearing on their recent posts. Each post item carries a region field, and the user object often carries one too.
TARGET_REGIONS = {"US", "CA", "GB"}
def passes_region(c, allowed):
user_region = (c.get("profile", {}).get("user", {}) or {}).get("region")
if user_region in allowed:
return True
# Fall back to majority region in their recent uploads
regions = [v.get("region") for v in c.get("recent_videos", []) if v.get("region")]
if regions and max(set(regions), key=regions.count) in allowed:
return True
return False
geo_filtered = {uid: c for uid, c in tier_filtered.items()
if passes_region(c, TARGET_REGIONS)}
If you need a definitive country list, hit /region-list/ once and cache it; the response is a dictionary keyed by ISO 3166-1 alpha-2 codes (uppercase).
Combine the signals into a simple weighted score and write the result somewhere your outreach team can open in a spreadsheet.
import csv
def final_score(c):
er = c.get("engagement_rate", 0) * 100 # to percent
niche = c.get("niche_score", 0)
cadence = min(c.get("recent_count_14d", 0), 10)
return round(er * 5 + niche * 4 + cadence * 2, 2)
ranked = sorted(geo_filtered.values(), key=final_score, reverse=True)
with open("micro_influencers.csv", "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow([
"username", "nickname", "followers", "verified",
"engagement_rate_pct", "niche_score", "posts_14d",
"score", "sample_post_1", "sample_post_2", "signature",
])
for c in ranked[:200]:
samples = [v.get("aweme_id") for v in c.get("recent_videos", [])][:2]
w.writerow([
c.get("username"),
c.get("nickname"),
c.get("followers"),
c.get("verified"),
round(c.get("engagement_rate", 0) * 100, 2),
c.get("niche_score"),
c.get("recent_count_14d"),
final_score(c),
samples[0] if samples else "",
samples[1] if len(samples) > 1 else "",
(c.get("signature") or "").replace("\n", " "),
])
A 200-row CSV ranked by score is a manageable Monday-morning task list for a single campaign manager: the top 30 get warm outreach, the next 50 go into a longer-term nurture list, and the rest stay in the database for the next campaign.
Inflated follower counts are the single biggest waste of micro-influencer budget. Even a clean engagement rate can be misleading if a creator bought a large block of followers six months ago and now posts to a real (but smaller) audience. Before you spend a dollar, run each shortlisted creator through a quick fake-follower audit: pull a follower sample via /user-followers/, then check the proportion of accounts with zero posts, default avatars, or completion ratios that look algorithmic. We walked through the full method in Detect Fake TikTok Followers Programmatically - run it on your top 50 candidates before outreach.
Once you have the CSV:
Programmatic discovery does not exempt you from the rules. A short checklist:
#ad, #sponsored, or TikTok's built-in branded content toggle. Build this into the contract, not into a verbal request.Talk to legal once, document the workflow, and your team can reuse the same compliance template across every campaign.
For a 500-candidate seed funnel that ends with 200 ranked micro-influencers, expect roughly 500-700 credits: 5-10 hashtag pages, 500 /userinfo-by-username/ calls, and 100-150 /user-posts/ calls on the tier-passing accounts. At pay-as-you-go pricing that is a one-time cost per campaign, and the CSV is reusable.
Yes. If a specific song is trending in your category, call /music-posts/ with its music_id as the seed instead of /challenge-posts/. The downstream pipeline (Steps 2 through 8) is identical because both endpoints return the same videos shape with an author object per item.
Use 3-5 adjacent hashtags as parallel seeds, lower the engagement-rate floor (to 3% instead of 5%), and consider widening the tier to 5K-150K. You can also pull related creators via /user-following/ from your strongest matches - micro-influencers in a niche tend to follow each other.
Profile stats and recent posts are pulled live on each request, so engagement numbers reflect the creator's state at query time. Re-run the pipeline before each campaign launch rather than relying on a stale CSV from three months ago - micro-creators can double or halve their follower count quickly.
Yes. Register on tikliveapi.com, verify your email, and you get 100 free credits - enough to seed a small hashtag, hydrate 20 profiles, and confirm the end-to-end flow returns what you expect. From there you can try a full run on the playground before wiring it into your stack, or jump straight into the users documentation and posts documentation to plan the pipeline. Questions during integration? Contact support and you will hear back within one business day.
Ready to put what you read into code? Try our endpoints live or grab the full reference.