Webhooks or polling. Every backend engineer who has ever wired a third-party data source into a product workflow has stood at this fork. It is not a stylistic choice. It is a foundational data-architecture decision that ripples into your latency budget, your infrastructure footprint, your failure modes, and the shape of every downstream service that consumes the data.
For TikTok specifically, the answer is shaped by what the platform exposes - and what API providers wrap around it. This article walks through the trade-offs honestly, shows how to do polling well when polling is what you have, and explains how to simulate webhook-style event emission on top of a clean REST surface.
Before anything else: TikLiveAPI does not offer webhooks today. The product is a polling-based REST surface with 37 endpoints under https://api.tikliveapi.com, authenticated with the X-Api-Key header, billed on a flat 1 request = 1 credit model.
If your architecture is built on the assumption that a third party will push events to you the millisecond something happens on TikTok, TikLiveAPI is not that product. What you get instead is a deterministic, low-latency request/response API with roughly 750ms average response time and an uptime claim of 99.9% across the fleet. The rest of this article is about how to make that polling story sharp, cheap, and event-shaped where you need it to be.
Webhooks get the marketing love, but polling has unglamorous advantages that show up in production:
X-Api-Key and send it outbound. You do not need to expose, secure, and rotate a public webhook receiver.For most analytics dashboards, scheduled reports, social listening pipelines, creator monitoring tools, and back-office workflows, polling is the right default. The question is not whether to poll. It is how often, on what triggers, and how to turn polling output into events.
There are workloads where push delivery genuinely beats pull. In a TikTok context, those are:
None of these are achievable with sub-second guarantees against TikTok itself - TikTok's public surface is not push-based - but you can get close with aggressive polling. The trick is to push the event boundary inward: poll the REST API, detect deltas, and emit your own internal webhooks to subscriber services. That is what the rest of this article builds.
Naive polling re-fetches a resource on a fixed interval and reprocesses everything every time. Differential polling stores the last response's identifying fields - on user posts, the create_time on the newest item - and only emits events for items that did not exist last cycle. The API endpoint is /user-posts/, which returns a videos[] array with snake_case items (aweme_id, create_time, play_count, digg_count, and so on) along with a cursor and hasMore.
Not all accounts deserve the same poll rate. A creator who posts twice a day does not need a 60-second interval, and a creator who is mid-livestream-promotion campaign does need it. Track each resource's recent activity and modulate accordingly:
This single optimization can cut credit consumption by 80% or more on a long-tail creator dataset without sacrificing meaningful freshness on the accounts that matter.
The post endpoint returns a cursor and hasMore envelope. For backfills you walk cursors forward until hasMore is false. For ongoing monitoring you do the opposite: cache the newest aweme_id you have seen, and only walk the cursor far enough to catch up to it. New posts always arrive at the top of the list, so a single page fetch (default 10 items) covers all but the most prolific creators.
One sharp edge: /user-followers/ and /user-following/ do not use cursor. They paginate via a time timestamp parameter, return hasMore in the envelope, and on the following endpoint the top-level array key is followings (plural, trailing s) - not following. Code that assumes a uniform pagination model across the surface will break on these.
Pool your polling work into queues by SLA tier:
Each queue has its own concurrency cap, its own credit budget, and its own backoff policy. When you eventually need to add webhook-style features for paying customers, you build them on top of the VIP queue's diff detector and leave the longtail unchanged.
The architecture that makes polling feel like webhooks to the rest of your stack is straightforward: a polling worker fetches, a diff detector compares against last-known state, and detected changes are published as events to an internal event bus that your other services subscribe to.
Here is a minimal Python implementation that polls a creator's posts, detects new ones, and emits events. It uses X-Api-Key for auth, the /user-posts/ endpoint shape (videos[], cursor, hasMore), and snake_case fields like aweme_id and create_time:
import time
import json
import redis
import requests
API_BASE = "https://api.tikliveapi.com"
API_KEY = "your_tikliveapi_key"
HEADERS = {"X-Api-Key": API_KEY}
r = redis.Redis(decode_responses=True)
def fetch_latest_posts(userid: str, count: int = 10):
"""Single page fetch of newest posts for a user."""
resp = requests.get(
f"{API_BASE}/user-posts/",
params={"userid": userid, "count": count},
headers=HEADERS,
timeout=10,
)
resp.raise_for_status()
body = resp.json()
return body.get("videos", []), body.get("cursor"), body.get("hasMore", False)
def emit_event(stream: str, event: dict) -> None:
"""Publish to a Redis stream that downstream services consume."""
r.xadd(stream, {"payload": json.dumps(event)})
def diff_and_emit(userid: str):
"""Differential polling - emit one event per new post."""
seen_key = f"seen:posts:{userid}"
seen_ids = r.smembers(seen_key)
videos, _cursor, _has_more = fetch_latest_posts(userid)
fresh = []
for v in videos:
aweme_id = v.get("aweme_id")
if not aweme_id or aweme_id in seen_ids:
continue
fresh.append(v)
for v in fresh:
emit_event("tiktok.post.created", {
"userid": userid,
"aweme_id": v["aweme_id"],
"create_time": v.get("create_time"),
"play_count": v.get("play_count"),
"digg_count": v.get("digg_count"),
"comment_count": v.get("comment_count"),
"share_count": v.get("share_count"),
"title": v.get("title"),
})
r.sadd(seen_key, v["aweme_id"])
r.expire(seen_key, 60 * 60 * 24 * 30)
return len(fresh)
def adaptive_interval(userid: str) -> int:
"""Slow down idle accounts, speed up active ones."""
last_post_ts = r.get(f"last_post_ts:{userid}")
if not last_post_ts:
return 600
age = time.time() - int(last_post_ts)
if age < 3600:
return 60
if age < 86400:
return 300
if age < 604800:
return 1800
return 86400
def poll_forever(userid: str):
while True:
new_count = diff_and_emit(userid)
if new_count:
r.set(f"last_post_ts:{userid}", int(time.time()))
time.sleep(adaptive_interval(userid))
if __name__ == "__main__":
poll_forever("6784819479778378757")
Downstream services subscribe to tiktok.post.created on the Redis stream and react. From their perspective the data is push-delivered. The fact that a polling worker mediates the relationship is an implementation detail.
The end-to-end shape looks like this:
+-----------------+ +------------------+ +------------------+
| TikLiveAPI | HTTP | Polling Workers | | Diff Detector |
| REST endpoints |<-------+ (per-tier queue)+------->+ (last-seen IDs) |
+-----------------+ +------------------+ +--------+---------+
|
| new items
v
+------------------+
| Event Bus |
| Kafka / Redis |
| Streams / SQS |
+--------+---------+
|
+------------------------+------------------------+
v v v
+------------------+ +------------------+ +------------------+
| Moderation svc | | Notification svc | | Analytics sink |
+------------------+ +------------------+ +------------------+
The polling workers are the only component that talks to TikLiveAPI. Everything else lives inside your stack, reading from the event bus. This is the same shape you would use if a real webhook were feeding you - it just happens that the source is a worker pool instead of a provider's outbound HTTP fanout.
Every poll costs one credit. A few patterns let you keep the bill predictable:
/userid/ endpoint resolves a username to a numeric id and returns a tiny flat object with just an id field. Use it as a HEAD-equivalent existence check before spending credits on richer endpoints. Pair it with cached results./music-info/, /playlist-info/, /collection-info/, /challenge-info-id/ and /challenge-info-name/ return flat metadata that changes slowly. Hashtag info exposes fields like cha_name (not name), user_count, and view_count. Cache these for hours or days; only invalidate on demand./userinfo-by-id/ (which returns nested user{} and stats{} objects in camelCase: followerCount, heartCount, videoCount) and then their posts via /user-posts/. A single page fetch of /user-posts/ already tells you whether new content exists.play_count, digg_count, comment_count, share_count) on existing posts move continuously and rarely need sub-hour resolution. Separate "is there new content" polling (frequent) from "how is existing content performing" polling (rare).Combine these and the credit budget for monitoring thousands of creators becomes tractable. You can model expected spend exactly using the credit pricing and the queue intervals above.
Polling has its own quirks. Plan for them:
cursor and the underlying list mutates between pages (a creator deletes a video mid-walk), you can either skip items or revisit them. For monotonic event emission, dedupe on aweme_id at the emit boundary and treat the cursor as a hint, not a contract.id - not cid. Replies live under /post-comment-replies/ and require both video_id and parent comment_id. A diff detector that looks for the wrong identifier field will emit duplicates forever./userinfo-by-username/ with its followerCount and secUid), others return snake_case (/user-posts/ with aweme_id and create_time), and a few return flat dictionaries with no envelope at all (/region-list/ returns ISO codes as keys). Write per-endpoint parsers, not a single generic one./user-liked/ only works if the user has their "liked" tab public. Treat empty responses on that endpoint as a privacy signal, not as an outage.Test your diff detector against all of these scenarios in staging. Try the live surface yourself in the playground before locking your parser in.
Honest answer: probably yes, for a subset of customers. The customers who benefit most are the ones building real-time moderation, alerting, and engagement-driven workflows where five-minute polling is too slow. For backfills, analytics, and creator-data products, the polling story is already strong.
If outbound webhooks would unlock your use case, say so. Tell the team what events you would subscribe to, what latency you need, and what payload shape would let you skip your own diff detector. Product priorities follow concrete customer requests, not abstract feature requests. The clearer the use case, the more credibly it can compete for roadmap time.
No. TikLiveAPI is a polling-based REST API with 37 endpoints. All requests are authenticated with the X-Api-Key header against https://api.tikliveapi.com. If you need event-style delivery, you build a client-side diff detector that emits events to your own bus.
Use adaptive intervals. Active creators (posted in the last hour) every 60 seconds; today-active every 5 minutes; this-week-active every 30 minutes; dormant accounts every 24 hours. This keeps the credit bill predictable without sacrificing freshness on accounts that matter.
Cache the newest aweme_id you have seen for each user. Fetch one page of /user-posts/ (default 10 items), iterate videos[], and emit events only for items whose aweme_id is not in your seen set. New posts always arrive at the top, so single-page diffing covers virtually every creator.
Yes, with care. /user-followers/ paginates via a time timestamp parameter (not cursor) and returns followers[], total, time, and hasMore at the top level. Cache the previous total and the most recently seen follower ids. When total jumps, walk the first page and emit events for any unseen ids.
The /userid/ endpoint returns a tiny flat object with just id - cheap, but it does not signal change. For change detection, hit /userinfo-by-id/ and watch the nested stats.videoCount, stats.followerCount, and stats.heartCount camelCase counters. A delta in videoCount is a strong signal to follow up with /user-posts/.
For a deeper tour of the REST surface, browse the full documentation, scan the engineering blog for related patterns, or watch your existing usage from your profile dashboard.
Ready to put what you read into code? Try our endpoints live or grab the full reference.