TikTok Live API: check if a creator is live (and get the stream URLs)
TikTok's developer platform has no endpoint that tells you whether an arbitrary account is live right now. The practical way to get it is one GET request: GET /v1/tiktok/live takes a handle or profile URL and returns isLive, the room metadata, and the raw stream URLs, for 1 credit per call.
Everything below — every field name and every value — comes from responses captured against the production API on September 29, 2026. Nothing is paraphrased from docs.
What a live creator looks like
QVC was mid-broadcast when this was captured. Trimmed, but with the real values:
{
"success": true,
"data": {
"platform": "tiktok",
"username": "qvc",
"isLive": true,
"liveStatus": "live",
"creator": {
"id": "6768510980420043782",
"displayName": "QVC, Inc",
"followers": 1598629,
"verified": true
},
"room": {
"id": "7690942771967838990",
"streamId": "3578935502357660592",
"status": "live",
"title": "Summer\u2019s Over Savings with Steph",
"startedAt": "2026-09-29T13:07:55.000Z",
"viewerCount": 182,
"totalEnterCount": 4733,
"streamQualities": [
{
"quality": "hd",
"codec": "h264",
"resolution": "720x1280",
"bitrate": 1000000,
"flv": "https://pull-flv-f77-tt01.tiktokcdn-us.com/stage/stream-…_hd.flv?expire=1791900675&sign=…",
"cmaf": "https://pull-flv-f77-tt01.tiktokcdn-us.com/stage/stream-…_hd/index.mpd?expire=1791900675&sign=…",
"dash": "https://pull-flv-f77-tt01.tiktokcdn-us.com/stage/stream-…_hd/index.mpd?expire=1791900675&sign=…"
}
]
},
"fetchedAt": "2026-09-29T14:11:16.647Z"
}
}
That capture came back with qualities origin (1080×1920), hd, sd, ld, and ao (audio-only). There is also a streams object keyed by the same quality names if you just want URL strings without the codec metadata. hls appears as a sixth URL field when TikTok exposes it; in this room it only served FLV and CMAF/DASH.
The call
JavaScript:
const res = await fetch(
"https://api.captapi.com/v1/tiktok/live?url=" +
encodeURIComponent("https://www.tiktok.com/@qvc/live"),
{ headers: { Authorization: `Bearer ${process.env.CAPTAPI_KEY}` } },
);
const { data } = await res.json();
if (data.isLive) {
const hd = data.room.streamQualities.find((q) => q.quality === "hd");
console.log(data.room.title, data.room.viewerCount, hd.cmaf);
} else {
console.log(`not live (${data.liveStatus})`); // "ended" or "offline"
}
Python:
import httpx, os
r = httpx.get(
"https://api.captapi.com/v1/tiktok/live",
params={"url": "https://www.tiktok.com/@qvc/live"},
headers={"Authorization": f"Bearer {os.environ['CAPTAPI_KEY']}"},
timeout=30,
)
data = r.json()["data"]
if data["isLive"]:
room = data["room"]
hd = next(q for q in room["streamQualities"] if q["quality"] == "hd")
print(room["title"], room.get("viewerCount"), hd["cmaf"])
else:
print("not live:", data["liveStatus"]) # "ended" or "offline"
A bare handle works in url too — you don't need to build the full /live URL. On latency: the QVC check above returned in 1.3 s; a handle the system hasn't seen recently can take around 10 s (an ESPN check in the same session took 11 s).
The trap: a non-empty room does not mean live
This is the mistake that breaks most homegrown checks. When a creator's last broadcast has ended, TikTok still serves the full room payload — title, start time, even the stream URLs. ESPN, captured offline in the same session:
{
"username": "espn",
"isLive": false,
"liveStatus": "ended",
"room": {
"status": "ended",
"title": "CFB Saturday w/ Kirk Herbstreit!",
"startedAt": "2026-09-19T20:03:35.000Z",
"totalEnterCount": 189571,
"streamQualities": [ … ],
"isLastKnown": true
}
}
That broadcast started ten days before the capture, and the response still contains stream URLs. If your code treats "room exists" or "stream URL present" as "creator is live", ESPN looks permanently on air. The API makes the distinction explicit so you don't have to infer it:
liveStatus | isLive | What you get |
|---|---|---|
live | true | Full room with viewerCount and playable stream URLs |
ended | false | Last known room, flagged isLastKnown: true, no viewerCount |
offline | false | No room key at all (NASA and United Nations returned this) |
Trust isLive and nothing else. viewerCount is only present while live — stale concurrent counts are omitted rather than served as if current.
Two more things before you ship
FLV will not play in a browser. The flv URL is what TikTok's own apps consume; web players can't use it. The cmaf and dash fields point to an index.mpd manifest that plays in Shaka Player or dash.js. Pick per quality from streamQualities, or take streams.hd if you don't care about codec metadata.
The stream URLs are signed and they expire. Every URL carries expire= (a unix timestamp — the ones above decode to about two weeks out) and a sign= parameter. Don't store them and expect them to work next month; re-fetch when you actually need to play. The same applies to coverUrl and avatar URLs, which live on TikTok's CDN with their own signatures — here's how to read the expiry on every platform's media URLs before they surprise you.
Polling for "went live" alerts
The obvious use is a notifier: poll the endpoint on an interval and fire when isLive flips to true. At 1 credit per check, a 5-minute interval on one creator costs 288 credits/day; a 15-minute interval costs 96. room.startedAt tells you how far into the broadcast you caught it, so you can decide whether an alert is still worth sending. If you're watching one account, start with 15 minutes — TikTok lives tend to run long (the QVC room above had been up for over an hour at capture time).
One pricing note for the docs-skimmers: /v1/tiktok/live-info returns the identical payload — same runner, same JSON — but bills 7 credits. Use /v1/tiktok/live at 1 credit; there is no data you gain by paying more.
The endpoint page has the full field reference and example responses. A free key comes with 100 credits — enough to poll a creator every 15 minutes for a day and see the three states yourself.