{
  "data": {
    "id": "RX1LgB29gltcaO0El",
    "actId": "KuR4Wj8bAkeGAly61",
    "userId": "zq8lKbpLzTjGDayPC",
    "startedAt": "2026-11-03T21:37:20.189Z",
    "finishedAt": "2026-11-03T21:37:29.943Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 9611,
      "runTimeSecs": 9.611,
      "computeUnits": 0.010678888888888889,
      "imageSizeBytes": 319604224
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "createdByOrganizationMemberUserId": "GK9YjZRyQZVzOdoH5",
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"YouTube Live Chat Input\",\n  \"description\": \"Read public live chat from videos, live streams or channels. No cookies, login or proxy setup required.\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"videoRefs\": {\n      \"title\": \"Video URLs or IDs\",\n      \"description\": \"YouTube watch, live, shorts or youtu.be links, 11-character video IDs, or a channel live link such as @handle/live.\",\n      \"type\": \"array\",\n      \"editor\": \"stringList\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"prefill\": [\n        \"https://www.youtube.com/watch?v=FQbNtLOVlwU\"\n      ]\n    },\n    \"channelRefs\": {\n      \"title\": \"Channels\",\n      \"description\": \"Channel handles (@name), channel URLs or UC… channel IDs. The actor discovers current live streams and recent stream replays from each channel.\",\n      \"type\": \"array\",\n      \"editor\": \"stringList\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"mode\": {\n      \"title\": \"Chat mode\",\n      \"description\": \"Auto detects live versus replay. Active captures an ongoing live chat. Replay reads the saved chat of an ended stream.\",\n      \"type\": \"string\",\n      \"editor\": \"select\",\n      \"default\": \"auto\",\n      \"prefill\": \"auto\",\n      \"enum\": [\n        \"auto\",\n        \"active\",\n        \"replay\"\n      ],\n      \"enumTitles\": [\n        \"Detect automatically\",\n        \"Live chat\",\n        \"Chat replay\"\n      ]\n    },\n    \"channelDiscoveryMode\": {\n      \"title\": \"Channel discovery\",\n      \"description\": \"Which streams to pick from each channel. Used with channel references.\",\n      \"type\": \"string\",\n      \"editor\": \"select\",\n      \"default\": \"current_live_then_replays\",\n      \"prefill\": \"current_live\",\n      \"enum\": [\n        \"current_live\",\n        \"current_live_then_replays\",\n        \"latest_replays\"\n      ],\n      \"enumTitles\": [\n        \"Current live only\",\n        \"Current live, then recent replays\",\n        \"Recent stream replays only\"\n      ]\n    },\n    \"maxVideosPerChannel\": {\n      \"title\": \"Videos per channel\",\n      \"description\": \"Maximum streams to process from each channel.\",\n      \"type\": \"integer\",\n      \"default\": 5,\n      \"prefill\": 1,\n      \"minimum\": 1,\n      \"maximum\": 50\n    },\n    \"lookbackDays\": {\n      \"title\": \"Replay lookback days\",\n      \"description\": \"How far back to include ended streams from a channel. Use 0 for no limit.\",\n      \"type\": \"integer\",\n      \"default\": 30,\n      \"prefill\": 30,\n      \"minimum\": 0,\n      \"maximum\": 365\n    },\n    \"includeUpcomingLives\": {\n      \"title\": \"Include upcoming streams\",\n      \"description\": \"Emit an upcoming status row for scheduled channel streams instead of skipping them.\",\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"maxMessagesPerVideo\": {\n      \"title\": \"Messages per video\",\n      \"description\": \"Maximum unique chat events collected from each video. Use 0 to collect every available event.\",\n      \"type\": \"integer\",\n      \"default\": 5000,\n      \"prefill\": 3,\n      \"minimum\": 0,\n      \"maximum\": 200000\n    },\n    \"maxTotalItems\": {\n      \"title\": \"Maximum dataset items\",\n      \"description\": \"Whole-run cap across all videos and summary rows. Use 0 for no limit.\",\n      \"type\": \"integer\",\n      \"default\": 0,\n      \"prefill\": 3,\n      \"minimum\": 0,\n      \"maximum\": 1000000\n    },\n    \"maxTimePerVideoSecs\": {\n      \"title\": \"Time budget per video\",\n      \"description\": \"Safety limit for replay pagination on one video, in seconds. A run that reaches it reports partial_capture.\",\n      \"type\": \"integer\",\n      \"default\": 600,\n      \"prefill\": 120,\n      \"minimum\": 30,\n      \"maximum\": 86400\n    },\n    \"includeNormalMessages\": {\n      \"title\": \"Include normal messages\",\n      \"description\": \"Include standard text chat messages.\",\n      \"type\": \"boolean\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"includeSuperChats\": {\n      \"title\": \"Include Super Chats and Stickers\",\n      \"description\": \"Include paid Super Chat messages and paid Super Stickers.\",\n      \"type\": \"boolean\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"includeMembershipEvents\": {\n      \"title\": \"Include memberships\",\n      \"description\": \"Include new and renewed channel membership events.\",\n      \"type\": \"boolean\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"includeGiftEvents\": {\n      \"title\": \"Include gifts\",\n      \"description\": \"Include gift membership purchases, gift membership redemptions and paid gift messages.\",\n      \"type\": \"boolean\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"includeSystemEvents\": {\n      \"title\": \"Include system events\",\n      \"description\": \"Include engagement notices, slow-mode changes, restricted participation and other system rows.\",\n      \"type\": \"boolean\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"includePlaceholders\": {\n      \"title\": \"Include placeholders\",\n      \"description\": \"Include raw placeholder rows that contain only an ID and timestamp.\",\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"includeSummary\": {\n      \"title\": \"Include stream summary\",\n      \"description\": \"Add one stream_summary row per video with event counts, per-currency totals, top supporters and completeness.\",\n      \"type\": \"boolean\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"startOffsetSecs\": {\n      \"title\": \"Start offset (seconds)\",\n      \"description\": \"Replay only. Skip events before this video offset. Use 0 to start at the beginning.\",\n      \"type\": \"integer\",\n      \"default\": 0,\n      \"prefill\": 0,\n      \"minimum\": 0,\n      \"maximum\": 604800\n    },\n    \"endOffsetSecs\": {\n      \"title\": \"End offset (seconds)\",\n      \"description\": \"Replay only. Stop after this video offset. Use 0 to read to the end.\",\n      \"type\": \"integer\",\n      \"default\": 0,\n      \"prefill\": 0,\n      \"minimum\": 0,\n      \"maximum\": 604800\n    },\n    \"liveCollectSecs\": {\n      \"title\": \"Live capture seconds\",\n      \"description\": \"How long to keep polling an active chat before stopping. Live captures are always partial.\",\n      \"type\": \"integer\",\n      \"default\": 60,\n      \"prefill\": 10,\n      \"minimum\": 10,\n      \"maximum\": 86400\n    },\n    \"pollIntervalMs\": {\n      \"title\": \"Live poll interval (ms)\",\n      \"description\": \"Minimum delay between live chat polls. YouTube's own timeout is honoured when it is longer.\",\n      \"type\": \"integer\",\n      \"default\": 2000,\n      \"prefill\": 2000,\n      \"minimum\": 2000,\n      \"maximum\": 30000\n    },\n    \"language\": {\n      \"title\": \"Language\",\n      \"description\": \"Interface language used when requesting YouTube pages and chat.\",\n      \"type\": \"string\",\n      \"editor\": \"select\",\n      \"default\": \"en\",\n      \"prefill\": \"en\",\n      \"enum\": [\n        \"en\",\n        \"es\",\n        \"pt\",\n        \"de\",\n        \"fr\",\n        \"it\",\n        \"ja\",\n        \"ko\",\n        \"hi\",\n        \"id\"\n      ],\n      \"enumTitles\": [\n        \"English\",\n        \"Spanish\",\n        \"Portuguese\",\n        \"German\",\n        \"French\",\n        \"Italian\",\n        \"Japanese\",\n        \"Korean\",\n        \"Hindi\",\n        \"Indonesian\"\n      ]\n    },\n    \"regionCode\": {\n      \"title\": \"Region\",\n      \"description\": \"Region used for YouTube page and chat requests.\",\n      \"type\": \"string\",\n      \"editor\": \"select\",\n      \"default\": \"US\",\n      \"prefill\": \"US\",\n      \"enum\": [\n        \"US\",\n        \"GB\",\n        \"CA\",\n        \"AU\",\n        \"IN\",\n        \"BR\",\n        \"DE\",\n        \"FR\",\n        \"JP\",\n        \"KR\",\n        \"MX\",\n        \"ID\"\n      ],\n      \"enumTitles\": [\n        \"United States\",\n        \"United Kingdom\",\n        \"Canada\",\n        \"Australia\",\n        \"India\",\n        \"Brazil\",\n        \"Germany\",\n        \"France\",\n        \"Japan\",\n        \"South Korea\",\n        \"Mexico\",\n        \"Indonesia\"\n      ]\n    },\n    \"includeMetadata\": {\n      \"title\": \"Include stream metadata\",\n      \"description\": \"Repeat video and stream metadata on each chat row.\",\n      \"type\": \"boolean\",\n      \"default\": true,\n      \"prefill\": true\n    }\n  }\n}",
    "readme": "# YouTube Live Stream & Replay Chat Extractor\n\nRead the chat behind a YouTube live stream — while it is running or from the saved replay after it ends. Give the actor a video link, a video ID or a channel, and it returns the chat timeline as structured records: text messages, Super Chats, Super Stickers, memberships, gift memberships and paid gift messages.\n\nNo cookies, login, API key or proxy setup is required.\n\n## What you get\n\n- **Replay chat in full** — pages through the saved chat to the end of the stream instead of stopping after a fixed number of pages.\n- **Live chat capture** — opens the live chat feed, emits the recent history already on screen, then polls at YouTube's own interval until your time window ends.\n- **Channel discovery** — finds the current live stream and recent stream replays from an `@handle`, channel URL or `UC…` channel ID.\n- **Every visible event type** — normal messages, Super Chats, Super Stickers, memberships, gift purchases, gift redemptions, paid gift messages, engagement notices and slow-mode/system notices.\n- **Replay offsets** — each replay event carries `videoOffsetMsec` and a readable `videoOffsetText` such as `0:05:06`.\n- **Numeric money** — `amountText` plus `amountValue` and `currency` when the amount is unambiguous.\n- **Author detail** — display name, channel ID, avatar, badges, handle, profile URL, and owner / moderator / member / verified flags.\n- **Stream context** — title, channel, live status, start/end time, publish date, duration, views, likes and category.\n- **Type filters and caps** — include or exclude event families, cap messages per video and total items, and restrict replay output to a time range.\n- **Optional summary** — one `stream_summary` record per video with event counts, per-currency totals, top supporters and a completeness note.\n\n## Inputs\n\n| Field | Default | What it does |\n|---|---|---|\n| `videoRefs` | — | Watch, live, shorts or `youtu.be` links, bare 11-character IDs, or channel live links such as `@handle/live`. |\n| `channelRefs` | — | `@handle`, channel URL or `UC…` ID. Each channel is searched for streams. |\n| `mode` | `auto` | `auto` detects live versus replay. `active` forces live chat. `replay` forces saved replay chat. |\n| `channelDiscoveryMode` | `current_live_then_replays` | `current_live`, `current_live_then_replays` or `latest_replays`. |\n| `maxVideosPerChannel` | `5` | How many streams to take from each channel. |\n| `lookbackDays` | `30` | How far back to include ended streams from a channel. `0` means no limit. |\n| `includeUpcomingLives` | `false` | Emit an `upcoming` status row for scheduled channel streams. |\n| `maxMessagesPerVideo` | `5000` | Chat events per video. `0` collects every available event. |\n| `maxTotalItems` | `0` | Whole-run dataset cap. `0` means no limit. |\n| `maxTimePerVideoSecs` | `600` | Safety budget for replay pagination on one video. |\n| `includeNormalMessages` | `true` | Include standard text messages. |\n| `includeSuperChats` | `true` | Include Super Chats and Super Stickers. |\n| `includeMembershipEvents` | `true` | Include memberships. |\n| `includeGiftEvents` | `true` | Include gift purchases, redemptions and paid gift messages. |\n| `includeSystemEvents` | `true` | Include engagement, slow-mode and other system notices. |\n| `includePlaceholders` | `false` | Include empty placeholder rows. |\n| `includeSummary` | `false` | Add a stream summary row per video. |\n| `startOffsetSecs` / `endOffsetSecs` | `0` / `0` | Replay-only time window inside the video. |\n| `liveCollectSecs` | `60` | How long to poll an active chat. |\n| `pollIntervalMs` | `2000` | Minimum live poll delay; YouTube's longer timeout is honoured. |\n| `language` / `regionCode` | `en` / `US` | Page and chat request locale. |\n| `includeMetadata` | `true` | Repeat stream metadata on each chat row. |\n\n## Output\n\nEvery chat record has `recordType: \"live_chat_message\"` and:\n\n- identity and timing: `videoId`, `videoUrl`, `chatType`, `messageId`, `eventType`, `messageType`, `timestamp`, `timestampUsec`, `publishedAt`, `videoOffsetMsec`, `videoOffsetText`, `scrapedAt`\n- author: `authorName`, `authorChannelId`, `authorHandle`, `authorPhotoUrl`, `authorProfileUrl`, `authorBadges`, `memberBadge`, `isOwner`, `isModerator`, `isMember`, `isVerified`\n- content: `messageText`, `messageParts[]` (text and emoji runs)\n- money and membership: `amountText`, `amountValue`, `currency`, `superChatColor`, `stickerName`, `stickerUrl`, `membershipText`, `membershipLevel`, `memberMonths`\n- gifts: `giftCount`, `giftName`, `giftImageUrl`, `giftedBy`\n- system: `systemAction`, `systemText`\n- stream context when `includeMetadata` is on: `videoTitle`, `channelName`, `channelId`, `channelUrl`, `channelHandle`, `isLiveContent`, `isLiveNow`, `streamStatus`, `streamStartedAt`, `streamEndedAt`, `videoPublishedAt`, `videoDurationSeconds`, `videoViewCount`, `videoLikeCount`, `videoCategory`\n- channel discovery context when the target came from a channel: `discoverySource`, `channelRef`, `isDiscoveredLive`, `isDiscoveredUpcoming`, `discoveredAt`\n\nEvent values include `message`, `super_chat`, `super_sticker`, `membership`, `membership_gift`, `gift_received`, `gift`, `system` and `placeholder`.\n\nWhen chat cannot be read, the actor emits a `recordType: \"status\"` row with a specific status such as `chat_disabled`, `upcoming`, `no_replay`, `stream_ended`, `stream_live`, `channel_not_live`, `rate_limited` or `partial_capture`, plus a plain-language `error`.\n\n## Examples\n\nRead a complete replay:\n\n```json\n{\n  \"videoRefs\": [\"https://www.youtube.com/watch?v=FQbNtLOVlwU\"],\n  \"mode\": \"replay\",\n  \"maxMessagesPerVideo\": 0\n}\n```\n\nCapture 60 seconds of a live chat:\n\n```json\n{\n  \"videoRefs\": [\"https://www.youtube.com/live/74WjpIH4jeE\"],\n  \"mode\": \"active\",\n  \"liveCollectSecs\": 60\n}\n```\n\nFind a channel's current live stream and its two most recent replays:\n\n```json\n{\n  \"channelRefs\": [\"@CBSNews\"],\n  \"channelDiscoveryMode\": \"current_live_then_replays\",\n  \"maxVideosPerChannel\": 2,\n  \"lookbackDays\": 30,\n  \"includeSummary\": true\n}\n```\n\nRead only paid events from a replay between two offsets:\n\n```json\n{\n  \"videoRefs\": [\"1vvDbx3nYpg\"],\n  \"mode\": \"replay\",\n  \"includeNormalMessages\": false,\n  \"includeMembershipEvents\": false,\n  \"includeGiftEvents\": false,\n  \"includeSystemEvents\": false,\n  \"startOffsetSecs\": 60,\n  \"endOffsetSecs\": 600\n}\n```\n\n## FAQ\n\n**Do I need cookies, an API key or a proxy?**\nNo. The actor reads public chat data without cookies or credentials. It connects directly and switches proxy automatically only when a request is blocked.\n\n**Why is a live run shorter than the whole chat?**\nA live read only sees what appears while the actor is collecting. The actor emits the recent history already on screen, then polls until `liveCollectSecs` ends. Run replay mode after the stream ends for the full timeline.\n\n**How much replay chat can I get?**\nAs much as YouTube exposes. Pagination stops only at the end of the replay, `maxMessagesPerVideo`, `maxTotalItems`, `maxTimePerVideoSecs`, or a real error. There is no fixed page cap.\n\n**What is the difference between `active` and `auto`?**\n`auto` checks the video and chooses live or replay. `active` insists on live chat and reports `stream_ended` if the stream is already over. `replay` insists on saved chat and reports `stream_live` if the stream is still running.\n\n**Why is `amountValue` sometimes missing?**\nOnly unambiguous amounts are converted to a number and currency. The original `amountText` is always kept. Unusual formats are counted in `amountsNotRead` on the summary row instead of being guessed.\n\n**Are gift memberships the same as memberships?**\nNo. A purchase is `membership_gift` (the buyer sent memberships), a redemption is `gift_received` (someone received one), and a paid gift message is `gift`. The summary keeps separate counts.\n\n**Why are some messages missing from a channel result?**\nChannel discovery only returns streams that match `channelDiscoveryMode`, `lookbackDays` and `maxVideosPerChannel`. Some streams have chat disabled, no saved replay, or are members-only. Each of those cases produces a status row instead of fabricated data.\n\n**Does the actor convert currencies or provide a single total?**\nNo. Totals stay per currency in `totalAmountByCurrency`; no exchange-rate service is used. Top supporters are ranked by numeric amount and keep their own currency.\n\n**Can it read members-only chat or deleted-message moderation details?**\nNo. Those require an authenticated channel session. The actor reads public chat, reports unavailable paths as status rows, and never fakes restricted data.\n\n## Limits\n\n- Live captures are partial by design; replay is the complete path.\n- Members-only chat, age/region-gated videos and moderation logs are not available without authentication.\n- Reply-thread parent IDs and a numeric concurrent-viewer count are not exposed in the public chat payload, so they are not returned.\n- Currency conversion is intentionally not performed.\n- Chat availability is controlled by the creator. A stream can be public while its chat or replay is disabled.\n\n## Data source\n\nThe actor reads YouTube's public watch pages and its public InnerTube live-chat endpoints. It does not log in, post messages, or perform moderation actions.\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "youtube-live-stream-and-replay-chat-extractor",
      "title": "YouTube Live Stream & Replay Chat Extractor",
      "description": "Collect public YouTube live chat or replay chat with replay offsets, numeric Super Chat and gift amounts, memberships, author badges, stream metadata and optional per-stream summaries. Works from video links, video IDs and channel handles.",
      "version": "1.1",
      "buildTag": "latest",
      "dockerfile": "../Dockerfile",
      "input": {
        "title": "YouTube Live Chat Input",
        "description": "Read public live chat from videos, live streams or channels. No cookies, login or proxy setup required.",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "videoRefs": {
            "title": "Video URLs or IDs",
            "description": "YouTube watch, live, shorts or youtu.be links, 11-character video IDs, or a channel live link such as @handle/live.",
            "type": "array",
            "editor": "stringList",
            "items": {
              "type": "string"
            },
            "prefill": [
              "https://www.youtube.com/watch?v=FQbNtLOVlwU"
            ]
          },
          "channelRefs": {
            "title": "Channels",
            "description": "Channel handles (@name), channel URLs or UC… channel IDs. The actor discovers current live streams and recent stream replays from each channel.",
            "type": "array",
            "editor": "stringList",
            "items": {
              "type": "string"
            }
          },
          "mode": {
            "title": "Chat mode",
            "description": "Auto detects live versus replay. Active captures an ongoing live chat. Replay reads the saved chat of an ended stream.",
            "type": "string",
            "editor": "select",
            "default": "auto",
            "prefill": "auto",
            "enum": [
              "auto",
              "active",
              "replay"
            ],
            "enumTitles": [
              "Detect automatically",
              "Live chat",
              "Chat replay"
            ]
          },
          "channelDiscoveryMode": {
            "title": "Channel discovery",
            "description": "Which streams to pick from each channel. Used with channel references.",
            "type": "string",
            "editor": "select",
            "default": "current_live_then_replays",
            "prefill": "current_live",
            "enum": [
              "current_live",
              "current_live_then_replays",
              "latest_replays"
            ],
            "enumTitles": [
              "Current live only",
              "Current live, then recent replays",
              "Recent stream replays only"
            ]
          },
          "maxVideosPerChannel": {
            "title": "Videos per channel",
            "description": "Maximum streams to process from each channel.",
            "type": "integer",
            "default": 5,
            "prefill": 1,
            "minimum": 1,
            "maximum": 50
          },
          "lookbackDays": {
            "title": "Replay lookback days",
            "description": "How far back to include ended streams from a channel. Use 0 for no limit.",
            "type": "integer",
            "default": 30,
            "prefill": 30,
            "minimum": 0,
            "maximum": 365
          },
          "includeUpcomingLives": {
            "title": "Include upcoming streams",
            "description": "Emit an upcoming status row for scheduled channel streams instead of skipping them.",
            "type": "boolean",
            "default": false,
            "prefill": false
          },
          "maxMessagesPerVideo": {
            "title": "Messages per video",
            "description": "Maximum unique chat events collected from each video. Use 0 to collect every available event.",
            "type": "integer",
            "default": 5000,
            "prefill": 3,
            "minimum": 0,
            "maximum": 200000
          },
          "maxTotalItems": {
            "title": "Maximum dataset items",
            "description": "Whole-run cap across all videos and summary rows. Use 0 for no limit.",
            "type": "integer",
            "default": 0,
            "prefill": 3,
            "minimum": 0,
            "maximum": 1000000
          },
          "maxTimePerVideoSecs": {
            "title": "Time budget per video",
            "description": "Safety limit for replay pagination on one video, in seconds. A run that reaches it reports partial_capture.",
            "type": "integer",
            "default": 600,
            "prefill": 120,
            "minimum": 30,
            "maximum": 86400
          },
          "includeNormalMessages": {
            "title": "Include normal messages",
            "description": "Include standard text chat messages.",
            "type": "boolean",
            "default": true,
            "prefill": true
          },
          "includeSuperChats": {
            "title": "Include Super Chats and Stickers",
            "description": "Include paid Super Chat messages and paid Super Stickers.",
            "type": "boolean",
            "default": true,
            "prefill": true
          },
          "includeMembershipEvents": {
            "title": "Include memberships",
            "description": "Include new and renewed channel membership events.",
            "type": "boolean",
            "default": true,
            "prefill": true
          },
          "includeGiftEvents": {
            "title": "Include gifts",
            "description": "Include gift membership purchases, gift membership redemptions and paid gift messages.",
            "type": "boolean",
            "default": true,
            "prefill": true
          },
          "includeSystemEvents": {
            "title": "Include system events",
            "description": "Include engagement notices, slow-mode changes, restricted participation and other system rows.",
            "type": "boolean",
            "default": true,
            "prefill": true
          },
          "includePlaceholders": {
            "title": "Include placeholders",
            "description": "Include raw placeholder rows that contain only an ID and timestamp.",
            "type": "boolean",
            "default": false,
            "prefill": false
          },
          "includeSummary": {
            "title": "Include stream summary",
            "description": "Add one stream_summary row per video with event counts, per-currency totals, top supporters and completeness.",
            "type": "boolean",
            "default": false,
            "prefill": false
          },
          "startOffsetSecs": {
            "title": "Start offset (seconds)",
            "description": "Replay only. Skip events before this video offset. Use 0 to start at the beginning.",
            "type": "integer",
            "default": 0,
            "prefill": 0,
            "minimum": 0,
            "maximum": 604800
          },
          "endOffsetSecs": {
            "title": "End offset (seconds)",
            "description": "Replay only. Stop after this video offset. Use 0 to read to the end.",
            "type": "integer",
            "default": 0,
            "prefill": 0,
            "minimum": 0,
            "maximum": 604800
          },
          "liveCollectSecs": {
            "title": "Live capture seconds",
            "description": "How long to keep polling an active chat before stopping. Live captures are always partial.",
            "type": "integer",
            "default": 60,
            "prefill": 10,
            "minimum": 10,
            "maximum": 86400
          },
          "pollIntervalMs": {
            "title": "Live poll interval (ms)",
            "description": "Minimum delay between live chat polls. YouTube's own timeout is honoured when it is longer.",
            "type": "integer",
            "default": 2000,
            "prefill": 2000,
            "minimum": 2000,
            "maximum": 30000
          },
          "language": {
            "title": "Language",
            "description": "Interface language used when requesting YouTube pages and chat.",
            "type": "string",
            "editor": "select",
            "default": "en",
            "prefill": "en",
            "enum": [
              "en",
              "es",
              "pt",
              "de",
              "fr",
              "it",
              "ja",
              "ko",
              "hi",
              "id"
            ],
            "enumTitles": [
              "English",
              "Spanish",
              "Portuguese",
              "German",
              "French",
              "Italian",
              "Japanese",
              "Korean",
              "Hindi",
              "Indonesian"
            ]
          },
          "regionCode": {
            "title": "Region",
            "description": "Region used for YouTube page and chat requests.",
            "type": "string",
            "editor": "select",
            "default": "US",
            "prefill": "US",
            "enum": [
              "US",
              "GB",
              "CA",
              "AU",
              "IN",
              "BR",
              "DE",
              "FR",
              "JP",
              "KR",
              "MX",
              "ID"
            ],
            "enumTitles": [
              "United States",
              "United Kingdom",
              "Canada",
              "Australia",
              "India",
              "Brazil",
              "Germany",
              "France",
              "Japan",
              "South Korea",
              "Mexico",
              "Indonesia"
            ]
          },
          "includeMetadata": {
            "title": "Include stream metadata",
            "description": "Repeat video and stream metadata on each chat row.",
            "type": "boolean",
            "default": true,
            "prefill": true
          }
        }
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "YouTube Live Stream & Replay Chat Extractor Output",
        "description": "Chat events with authors, replay offsets, Super Chat and gift amounts, membership detail, badges, stream metadata and optional summaries.",
        "properties": {
          "messages": {
            "type": "string",
            "title": "YouTube chat events",
            "description": "Dataset containing chat messages, paid events, memberships, gifts, system rows and stream summaries.",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "readme": "# YouTube Live Stream & Replay Chat Extractor\n\nRead the chat behind a YouTube live stream — while it is running or from the saved replay after it ends. Give the actor a video link, a video ID or a channel, and it returns the chat timeline as structured records: text messages, Super Chats, Super Stickers, memberships, gift memberships and paid gift messages.\n\nNo cookies, login, API key or proxy setup is required.\n\n## What you get\n\n- **Replay chat in full** — pages through the saved chat to the end of the stream instead of stopping after a fixed number of pages.\n- **Live chat capture** — opens the live chat feed, emits the recent history already on screen, then polls at YouTube's own interval until your time window ends.\n- **Channel discovery** — finds the current live stream and recent stream replays from an `@handle`, channel URL or `UC…` channel ID.\n- **Every visible event type** — normal messages, Super Chats, Super Stickers, memberships, gift purchases, gift redemptions, paid gift messages, engagement notices and slow-mode/system notices.\n- **Replay offsets** — each replay event carries `videoOffsetMsec` and a readable `videoOffsetText` such as `0:05:06`.\n- **Numeric money** — `amountText` plus `amountValue` and `currency` when the amount is unambiguous.\n- **Author detail** — display name, channel ID, avatar, badges, handle, profile URL, and owner / moderator / member / verified flags.\n- **Stream context** — title, channel, live status, start/end time, publish date, duration, views, likes and category.\n- **Type filters and caps** — include or exclude event families, cap messages per video and total items, and restrict replay output to a time range.\n- **Optional summary** — one `stream_summary` record per video with event counts, per-currency totals, top supporters and a completeness note.\n\n## Inputs\n\n| Field | Default | What it does |\n|---|---|---|\n| `videoRefs` | — | Watch, live, shorts or `youtu.be` links, bare 11-character IDs, or channel live links such as `@handle/live`. |\n| `channelRefs` | — | `@handle`, channel URL or `UC…` ID. Each channel is searched for streams. |\n| `mode` | `auto` | `auto` detects live versus replay. `active` forces live chat. `replay` forces saved replay chat. |\n| `channelDiscoveryMode` | `current_live_then_replays` | `current_live`, `current_live_then_replays` or `latest_replays`. |\n| `maxVideosPerChannel` | `5` | How many streams to take from each channel. |\n| `lookbackDays` | `30` | How far back to include ended streams from a channel. `0` means no limit. |\n| `includeUpcomingLives` | `false` | Emit an `upcoming` status row for scheduled channel streams. |\n| `maxMessagesPerVideo` | `5000` | Chat events per video. `0` collects every available event. |\n| `maxTotalItems` | `0` | Whole-run dataset cap. `0` means no limit. |\n| `maxTimePerVideoSecs` | `600` | Safety budget for replay pagination on one video. |\n| `includeNormalMessages` | `true` | Include standard text messages. |\n| `includeSuperChats` | `true` | Include Super Chats and Super Stickers. |\n| `includeMembershipEvents` | `true` | Include memberships. |\n| `includeGiftEvents` | `true` | Include gift purchases, redemptions and paid gift messages. |\n| `includeSystemEvents` | `true` | Include engagement, slow-mode and other system notices. |\n| `includePlaceholders` | `false` | Include empty placeholder rows. |\n| `includeSummary` | `false` | Add a stream summary row per video. |\n| `startOffsetSecs` / `endOffsetSecs` | `0` / `0` | Replay-only time window inside the video. |\n| `liveCollectSecs` | `60` | How long to poll an active chat. |\n| `pollIntervalMs` | `2000` | Minimum live poll delay; YouTube's longer timeout is honoured. |\n| `language` / `regionCode` | `en` / `US` | Page and chat request locale. |\n| `includeMetadata` | `true` | Repeat stream metadata on each chat row. |\n\n## Output\n\nEvery chat record has `recordType: \"live_chat_message\"` and:\n\n- identity and timing: `videoId`, `videoUrl`, `chatType`, `messageId`, `eventType`, `messageType`, `timestamp`, `timestampUsec`, `publishedAt`, `videoOffsetMsec`, `videoOffsetText`, `scrapedAt`\n- author: `authorName`, `authorChannelId`, `authorHandle`, `authorPhotoUrl`, `authorProfileUrl`, `authorBadges`, `memberBadge`, `isOwner`, `isModerator`, `isMember`, `isVerified`\n- content: `messageText`, `messageParts[]` (text and emoji runs)\n- money and membership: `amountText`, `amountValue`, `currency`, `superChatColor`, `stickerName`, `stickerUrl`, `membershipText`, `membershipLevel`, `memberMonths`\n- gifts: `giftCount`, `giftName`, `giftImageUrl`, `giftedBy`\n- system: `systemAction`, `systemText`\n- stream context when `includeMetadata` is on: `videoTitle`, `channelName`, `channelId`, `channelUrl`, `channelHandle`, `isLiveContent`, `isLiveNow`, `streamStatus`, `streamStartedAt`, `streamEndedAt`, `videoPublishedAt`, `videoDurationSeconds`, `videoViewCount`, `videoLikeCount`, `videoCategory`\n- channel discovery context when the target came from a channel: `discoverySource`, `channelRef`, `isDiscoveredLive`, `isDiscoveredUpcoming`, `discoveredAt`\n\nEvent values include `message`, `super_chat`, `super_sticker`, `membership`, `membership_gift`, `gift_received`, `gift`, `system` and `placeholder`.\n\nWhen chat cannot be read, the actor emits a `recordType: \"status\"` row with a specific status such as `chat_disabled`, `upcoming`, `no_replay`, `stream_ended`, `stream_live`, `channel_not_live`, `rate_limited` or `partial_capture`, plus a plain-language `error`.\n\n## Examples\n\nRead a complete replay:\n\n```json\n{\n  \"videoRefs\": [\"https://www.youtube.com/watch?v=FQbNtLOVlwU\"],\n  \"mode\": \"replay\",\n  \"maxMessagesPerVideo\": 0\n}\n```\n\nCapture 60 seconds of a live chat:\n\n```json\n{\n  \"videoRefs\": [\"https://www.youtube.com/live/74WjpIH4jeE\"],\n  \"mode\": \"active\",\n  \"liveCollectSecs\": 60\n}\n```\n\nFind a channel's current live stream and its two most recent replays:\n\n```json\n{\n  \"channelRefs\": [\"@CBSNews\"],\n  \"channelDiscoveryMode\": \"current_live_then_replays\",\n  \"maxVideosPerChannel\": 2,\n  \"lookbackDays\": 30,\n  \"includeSummary\": true\n}\n```\n\nRead only paid events from a replay between two offsets:\n\n```json\n{\n  \"videoRefs\": [\"1vvDbx3nYpg\"],\n  \"mode\": \"replay\",\n  \"includeNormalMessages\": false,\n  \"includeMembershipEvents\": false,\n  \"includeGiftEvents\": false,\n  \"includeSystemEvents\": false,\n  \"startOffsetSecs\": 60,\n  \"endOffsetSecs\": 600\n}\n```\n\n## FAQ\n\n**Do I need cookies, an API key or a proxy?**\nNo. The actor reads public chat data without cookies or credentials. It connects directly and switches proxy automatically only when a request is blocked.\n\n**Why is a live run shorter than the whole chat?**\nA live read only sees what appears while the actor is collecting. The actor emits the recent history already on screen, then polls until `liveCollectSecs` ends. Run replay mode after the stream ends for the full timeline.\n\n**How much replay chat can I get?**\nAs much as YouTube exposes. Pagination stops only at the end of the replay, `maxMessagesPerVideo`, `maxTotalItems`, `maxTimePerVideoSecs`, or a real error. There is no fixed page cap.\n\n**What is the difference between `active` and `auto`?**\n`auto` checks the video and chooses live or replay. `active` insists on live chat and reports `stream_ended` if the stream is already over. `replay` insists on saved chat and reports `stream_live` if the stream is still running.\n\n**Why is `amountValue` sometimes missing?**\nOnly unambiguous amounts are converted to a number and currency. The original `amountText` is always kept. Unusual formats are counted in `amountsNotRead` on the summary row instead of being guessed.\n\n**Are gift memberships the same as memberships?**\nNo. A purchase is `membership_gift` (the buyer sent memberships), a redemption is `gift_received` (someone received one), and a paid gift message is `gift`. The summary keeps separate counts.\n\n**Why are some messages missing from a channel result?**\nChannel discovery only returns streams that match `channelDiscoveryMode`, `lookbackDays` and `maxVideosPerChannel`. Some streams have chat disabled, no saved replay, or are members-only. Each of those cases produces a status row instead of fabricated data.\n\n**Does the actor convert currencies or provide a single total?**\nNo. Totals stay per currency in `totalAmountByCurrency`; no exchange-rate service is used. Top supporters are ranked by numeric amount and keep their own currency.\n\n**Can it read members-only chat or deleted-message moderation details?**\nNo. Those require an authenticated channel session. The actor reads public chat, reports unavailable paths as status rows, and never fakes restricted data.\n\n## Limits\n\n- Live captures are partial by design; replay is the complete path.\n- Members-only chat, age/region-gated videos and moderation logs are not available without authentication.\n- Reply-thread parent IDs and a numeric concurrent-viewer count are not exposed in the public chat payload, so they are not returned.\n- Currency conversion is intentionally not performed.\n- Chat availability is controlled by the creator. A stream can be public while its chat or replay is disabled.\n\n## Data source\n\nThe actor reads YouTube's public watch pages and its public InnerTube live-chat endpoints. It does not log in, post messages, or perform moderation actions.\n",
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "views": {
            "chatEvents": {
              "title": "Chat events",
              "transformation": {
                "fields": [
                  "videoId",
                  "eventType",
                  "authorName",
                  "messageText",
                  "videoOffsetText",
                  "amountText",
                  "currency"
                ]
              },
              "display": {
                "component": "table",
                "properties": {}
              }
            },
            "paidEvents": {
              "title": "Super Chats, Stickers & gifts",
              "transformation": {
                "fields": [
                  "videoId",
                  "eventType",
                  "authorName",
                  "amountText",
                  "amountValue",
                  "currency",
                  "giftCount"
                ]
              },
              "display": {
                "component": "table",
                "properties": {}
              }
            },
            "streamSummaries": {
              "title": "Stream summaries",
              "transformation": {
                "fields": [
                  "videoId",
                  "videoTitle",
                  "messageCount",
                  "superChatCount",
                  "membershipGiftCount",
                  "totalAmountByCurrency",
                  "chatComplete"
                ]
              },
              "display": {
                "component": "table",
                "properties": {}
              }
            }
          }
        }
      },
      "environmentVariables": {}
    },
    "buildNumber": "1.1.1",
    "imageDigest": "c29b5b9304a6fe703751f2937252cfa897e7f2478bad9c1a74e38a3e62fd00e4",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.1"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.010678888888888889
    }
  }
}