TikTok LIVE Comment Scraper avatar

TikTok LIVE Comment Scraper

Pricing

$20.00 / 1,000 live capture minutes

Go to Apify Store
TikTok LIVE Comment Scraper

TikTok LIVE Comment Scraper

Capture the live chat of a TikTok LIVE room while it is happening. One flat row per comment, with commenter identity where public, plus optional gift and join events. No login, no cookies, no WebSocket client to operate.

Pricing

$20.00 / 1,000 live capture minutes

Rating

0.0

(0)

Developer

Delowar Munna

Delowar Munna

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

24 days ago

Last modified

Share

TikTok LIVE Comment Scraper — capture what the audience is actually saying, while it is being said

Capture what the audience is actually saying, while it is being said.

Point this Actor at a TikTok LIVE room, say how long to listen, and get one clean row per comment — with commenter identity where public, plus optional gift and join events in the same dataset.


TL;DR

  • Input: a live URL, an @handle, a short link, or a room ID — plus how many minutes to listen.
  • Output: one flat, CSV-safe row per comment. Stable columns, ISO 8601 timestamps.
  • Billing: per minute of capture, per room. Not per comment.
  • No TikTok login, cookies, or API key required from you.
  • There is no comment history. You get what arrives while the Actor is listening. See below.

⚠️ Read this first: live chat is ephemeral

TikTok exposes no endpoint that returns a live room's chat history. None. A comment that was posted before you started is not retrievable by this Actor or any other.

So "scrape the comments" here is not a lookup — it is a duration. You choose how long to listen, and how much you get depends on how busy the room is. A five-minute capture of a quiet room may return four comments; the same capture of a live-commerce drop may hit your cap in ninety seconds.

This single fact drives the input shape, the pricing unit, and the error model below.


Why choose this Actor

Predictable costBilled per capture-minute, so you can compute your bill before pressing Start: rooms × captureMinutes.
Honest empty resultsA quiet room and a room that is not live are normal outcomes. Both are reported clearly, and neither is charged.
Clean, flat schemaOne row per comment, stable columns, no nested payloads to unpack. Exports to CSV without surprises.
Nothing to operateNo WebSocket client, no protobuf decoding, no signing service, no cookies.
Pipeline-readyOutput from TikTok LIVE Scraper pastes straight in — it is the discovery half of this pair.
Survives a room endingIf the broadcast ends mid-capture, everything already captured is kept and the early stop is flagged.

Quick start in 3 steps

  1. Find a room that is live right now. Paste a live URL, or use TikTok LIVE Scraper to discover rooms worth listening to.
  2. Set captureMinutes. Start with 5. This is what you pay for.
  3. Run it. Rows appear in the dataset as they arrive — you do not have to wait for the run to finish to see results.

Minimal — one room, five minutes. Every other field has a sensible default.

{
"liveRooms": ["https://www.tiktok.com/@examplecreator/live"],
"captureMinutes": 5
}

Several rooms at once. Rooms are captured concurrently, so three rooms for ten minutes takes ten minutes of wall-clock, not thirty. Mix input styles freely — full URL, @handle, short link, or a bare room ID all resolve the same way.

{
"liveRooms": [
"https://www.tiktok.com/@examplecreator/live",
"@anothercreator",
"https://vm.tiktok.com/ZMabcdefg/",
"7676761425155771157"
],
"captureMinutes": 10,
"maxCommentsPerRoom": 2000,
"maxTotalComments": 8000
}

A longer listen with gifts and joins included. Use this shape for live-commerce sessions where you want the whole picture in one dataset.

{
"liveRooms": ["@examplecreator"],
"captureMinutes": 30,
"includeGifts": true,
"includeJoins": true,
"includeCommenterFields": true,
"deduplicateComments": true
}

How capture works

  1. Resolve. Whatever you paste — live URL, short link, @handle, or room ID — is normalised to a single room, and duplicates across notations collapse to one.
  2. Check it is live. A room that is not live returns one NOT_LIVE row and the run continues to the next room. It never fails the run, and it is never charged.
  3. Listen. The Actor attaches to the room and records comments as they arrive, for captureMinutes — or until the room ends, or until a cap is reached.
  4. Write as it goes. Rows are pushed to the dataset continuously, so an interruption never costs you what was already captured.
  5. Summarise. A run summary is written to the key-value store under SUMMARY.

Rooms are captured concurrently, up to five at a time. This matters: the rooms are live now, so capturing them one after another would mean the later rooms lose their first several minutes of chat permanently.


Input reference

FieldTypeDefaultDescription
liveRoomsarray[]Live URLs, short links, @handles, bare handles, or room IDs. Required in practice.
captureMinutesinteger5Minutes to listen per room. The cost driver. Max 60.
maxCommentsPerRoominteger1000Ceiling per room. Hitting it stops that room early — which also stops its billing.
maxTotalCommentsinteger5000Absolute ceiling for the whole run. The hard cost cap.
includeGiftsbooleanfalseAlso record gifts, as rows with eventType = gift. Free.
includeJoinsbooleanfalseAlso record viewers joining, as eventType = join. High volume, low value — these count toward your caps.
includeCommenterFieldsbooleantrueInclude the commenter identity block.
deduplicateCommentsbooleanfalseOff by default — you get the raw stream. Turn on to drop repeat deliveries of the same message. See below.
includeSummarybooleantrueWrite the run summary to the key-value store.
proxyConfigurationobjectApify ProxyDatacenter or your own custom URLs. See the proxy policy below.

About deduplicateComments

It matches on TikTok's per-message ID, never on comment text. Repetition in chat is signal, and none of it is affected:

  • ten people posting "🔥" → ten separate rows
  • one person posting "🔥" three times → three separate rows

What it removes is the same physical message reaching the Actor twice. That happens because the Actor replays the chat backlog when it connects, and again after any reconnect — typically 20–30% of messages in a short capture.

Default is off, so what you get is the raw stream exactly as it arrived. Two things to know if you leave it off:

  • Repeat deliveries count toward maxCommentsPerRoom and maxTotalComments, so a cap can fill with messages you have already got.
  • Frequency analysis ("which question came up most?") will count protocol repeats as if they were real repeats. Turn deduplication on for that kind of work — especially for back-to-back scheduled runs, where windows overlap.

Billing is unaffected either way: you are charged per minute, not per row.

Worked cost examples

Because billing is per capture-minute, the arithmetic is simply rooms × captureMinutes:

RunCapture minutes billed
1 room × 5 minutes5
3 rooms × 10 minutes30
5 rooms × 5 minutes, one of them not live20 — the offline room is free
1 room × 30 minutes that ends after 1212 — you pay for time actually captured
1 room × 5 minutes with zero comments0 — a silent room is not charged
1 room × 10 minutes that hits maxCommentsPerRoom at minute 33

You are billed only for capture minutes, and only for rooms that actually produced chat. There is no fee to start a run — a run that finds nothing live, or a room that stays silent, costs you nothing at all.

Live rates are on the Actor's Pricing tab. This README deliberately quotes no price amounts, so it cannot go stale.


Output reference

One row per captured event. Every row carries the same columns — including error rows — so CSV headers never shift mid-file.

Sample row — every column

Real output, captured 2026-08-22. One row per event, all 26 columns present on every row.

{
"sourceRoomInput": "https://www.tiktok.com/@seniormender8/live",
"roomId": "7676761425155771157",
"roomUrl": "https://www.tiktok.com/@seniormender8/live",
"capturedAt": "2026-08-22T08:45:44.828Z",
"captureWindowSeconds": 60,
"commentId": "7676774098299489056",
"commentText": "this new update is annoying🙄",
"commentTimestamp": "2026-08-22T08:44:22.727Z",
"commentLanguage": "en",
"eventType": "comment",
"position": 13,
"creatorUsername": "seniormender8",
"liveTitle": "Zero Build Joker Fortnite!!",
"viewerCountAtCapture": 176,
"roomStartTime": "2026-08-22T07:55:20.000Z",
"isRoomActive": true,
"isPartial": false,
"errorType": null,
"errorMessage": null,
"capturedSequence": 1,
"commenterId": "7328783434984850464",
"commenterUsername": "sigrit_siki",
"commenterDisplayName": "✨️⚜ 𝑺.𝑳 ⚜✨️",
"commenterAvatarUrl": "https://p16-common-sign.tiktokcdn-eu.com/tos-no1a-avt-0068c001-no/712141b259ef9a695209407a36212c41~tplv-tiktok-shrink:72:72.webp?...",
"commenterVerified": false,
"commenterFollowerCount": 23350
}

(commenterAvatarUrl truncated here for readability — the real value is a full signed CDN URL.)

Dataset tabs

The dataset ships with four views. Pick one in the Console's Output tab, or append ?view=<name> to the dataset API URL — overview, commenters, gifts, issues.

📝 Comments — ?view=overview

The default. What was said, by whom, when.

{
"eventType": "comment",
"commentText": "this new update is annoying🙄",
"commenterUsername": "sigrit_siki",
"commenterDisplayName": "✨️⚜ 𝑺.𝑳 ⚜✨️",
"commentTimestamp": "2026-08-22T08:44:22.727Z",
"commentLanguage": "en",
"creatorUsername": "seniormender8",
"roomId": "7676761425155771157",
"position": 13,
"commentId": "7676774098299489056"
}

👤 Commenters — ?view=commenters

The same rows projected onto who is talking — for audience analysis, follower-weighting, or spotting verified accounts in chat.

{
"commenterUsername": "sigrit_siki",
"commenterDisplayName": "✨️⚜ 𝑺.𝑳 ⚜✨️",
"commenterId": "7328783434984850464",
"commenterVerified": false,
"commenterFollowerCount": 23350,
"commenterAvatarUrl": "https://p16-common-sign.tiktokcdn-eu.com/tos-no1a-avt-0068c001-no/...",
"commentText": "this new update is annoying🙄",
"roomId": "7676761425155771157"
}

Note this is one row per comment, not one row per person — a viewer who comments five times appears five times. Deduplicate on commenterId if you want unique people. The run summary already does this for you as uniqueCommenters and topCommenters.

🎁 Gifts — ?view=gifts

Only populated when includeGifts is on. Gift rows sit in the same dataset as comments and are distinguished by eventType.

{
"eventType": "gift",
"giftSenderUsername": "someviewer",
"giftName": "Rose",
"giftCount": 5,
"giftDiamondValue": 5,
"commentTimestamp": "2026-08-22T08:44:31.180Z",
"creatorUsername": "seniormender8",
"roomId": "7676761425155771157"
}

Shape shown from the schema, not from a capture. No gift arrived during testing, so unlike every other sample on this page this one is illustrative. The field mapping is verified against the protobuf definition; the values are not from a real run.

⚠️ Rooms with no result — ?view=issues

One row per room that produced nothing, saying why. This view is the reason an empty run is still a useful run — and none of these rows are charged.

{
"sourceRoomInput": "@creatorwhoisoffline",
"errorType": "NOT_LIVE",
"errorMessage": "Room is not currently live",
"creatorUsername": "creatorwhoisoffline",
"roomId": null,
"isRoomActive": false,
"capturedAt": "2026-08-22T08:45:44.828Z"
}

Shape shown from the schema. Every room in the sample run was live and talkative, so there were no rows to show here.

Columns

Source lineagesourceRoomInput, roomId, roomUrl, capturedAt, captureWindowSeconds CommentcommentId, commentText, commentTimestamp, commentLanguage, eventType, position Commenter (when includeCommenterFields)commenterId, commenterUsername, commenterDisplayName, commenterAvatarUrl, commenterVerified, commenterFollowerCount Room contextcreatorUsername, liveTitle, viewerCountAtCapture, roomStartTime, isRoomActive Gift (when includeGifts)giftName, giftCount, giftDiamondValue, giftSenderUsername Run metadataisPartial, errorType, errorMessage, capturedSequence

eventType is comment, gift, join, or none (for a row explaining why a room produced nothing). capturedSequence is the arrival order within its room; position is the order across the whole run.

Run summary

Written to the key-value store under SUMMARY — never to the dataset, because a differently-shaped row would corrupt CSV exports. It carries per-room counts, commentsPerMinute, uniqueCommenters, topCommenters, what the pipeline filtered, and per-column fill rates.

commentsPerMinute is the metric worth watching: it tells you whether a room was worth listening to and whether a longer capture would pay off.


What this Actor is NOT

  • Not a chat-history tool. There is no history. This is the single most common misunderstanding.
  • Not a video recorder. It captures chat, not the stream.
  • Not a discovery tool. It listens to rooms you give it. Use TikTok LIVE Scraper to find rooms.
  • Not a posting tool. It never comments, replies, follows, or sends anything.
  • Not a private-data tool. Only what is publicly visible in the room.
  • Not a sentiment classifier. It returns clean text for you to analyse, not opinions about it.

Common mistakes

MistakeWhat happensFix
Pointing it at a creator who is offlineOne NOT_LIVE row, no chargeCheck the creator is live first
Pasting a normal video URLTreated as that creator's live roomUse the creator's live URL
Expecting past commentsYou get only what arrives while listeningIncrease captureMinutes, or schedule runs during the broadcast
Setting captureMinutes very high on many roomsLong run, larger billBill = rooms × minutes; use maxTotalComments as a hard stop
Turning on includeJoins in a busy roomJoin rows crowd out comments against your capsLeave it off unless you specifically want joins

Troubleshooting

SymptomMeaning
NOT_LIVEThe room was not broadcasting. Expected and free.
NO_COMMENTSThe room was live but nobody commented in the window. Not charged.
INVALID_INPUTThe entry could not be read as a live room. The row says why.
UNRESOLVABLEA short link or bare room ID could not be traced to a creator.
SIGNATURE_RATE_LIMITToo many rooms connecting at once. Reduce the number of rooms per run.
CONNECT_TIMEOUTCould not attach in time; retried automatically before giving up.
Fewer rows than expectedThe room was quiet, or a cap stopped capture early. Check SUMMARY.
isPartial: trueThe room ended mid-capture. What you have is complete up to that point.

🚦 Proxy policy

Use Apify Datacenter proxy or no proxy for normal runs — both work reliably for TikTok LIVE rooms at this Actor's conservative concurrency.

Apify Residential proxy is not supported. The Actor fails at startup if apifyProxyGroups includes RESIDENTIAL. Reason: in pay-per-event Actors, residential bandwidth (~$8/GB) is billed to the developer, not the run user — and a capture holds an open connection for minutes, so a single bandwidth-heavy run could exceed the revenue it generates.

If you genuinely need residential routing, supply your own provider via the proxy editor's Custom proxy URLs field. That traffic goes through your provider, not Apify, and is unaffected:

http://user:pass@proxy.iproyal.com:12321
http://user:pass@proxy.brightdata.com:22225
http://user:pass@proxy.oxylabs.io:7777

FAQ

Can I get comments from a stream that already ended? No. TikTok does not expose live chat history to anyone. If the broadcast is over, the chat is gone.

Do I need a TikTok account, cookies, or an API key? No.

What happens if the room ends while capturing? Everything captured is kept, the row is flagged isPartial, and SUMMARY records endedEarlyBecauseRoomClosed. You are billed only for the time actually captured.

Why is a quiet room not free... or is it? It is free. A room that produces zero comments is not charged at all, even though the Actor spent the full window listening.

Can I capture many rooms at once? Yes — up to five concurrently per run. Beyond that, split across scheduled runs to avoid SIGNATURE_RATE_LIMIT.

How do I capture a whole broadcast? Set captureMinutes to cover it (max 60) and schedule back-to-back runs. Use deduplicateComments so overlapping windows do not produce duplicate rows.


Integrations and scheduling

Works with everything on the Apify platform: Schedules (sample a room every hour through a broadcast), Webhooks, API, and integrations with Make, Zapier, Google Sheets, Slack and others. Because chat is ephemeral, scheduling is how you cover a long broadcast — a run every N minutes during the stream, with deduplication on.

Pair with TikTok LIVE Scraper: it finds rooms worth listening to and emits roomId/roomUrl that paste into liveRooms unchanged.


Changelog

1.0 — Initial release. Live chat capture with gift and join events, commenter identity, room context, per-room and run-wide caps, deduplication, run summary, and per-capture-minute billing.