Twitch Scraper - Streams, Clips, VODs & Channel Data
Pricing
from $1.00 / 1,000 results
Twitch Scraper - Streams, Clips, VODs & Channel Data
From $1/1K. Scrape Twitch data at scale, including channels, live streams, clips, VODs, and top games. Extract HLS m3u8 stream URLs, clip MP4 downloads in multiple qualities, chapter markers, and metadata. Supports keyword search with rich, analytics-ready output.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
2
Bookmarked
37
Total users
9
Monthly active users
8 days ago
Last modified
Categories
Share
Twitch Scraper: Channels, Live Streams, Clips, VODs, and Chat Replay
Twitch Scraper turns Twitch into structured data. Get channel profiles, currently-live streams by game, top games, clips with direct MP4 download links, VOD lists, VOD chapter markers, and VOD chat-replay messages, plus a unified keyword search across channels and games. Paste channel logins, a game category slug, video IDs, or any twitch.tv link, then export the result to JSON, CSV, or Excel, or read it through the API.
Why This Scraper?
- Ten modes in one actor. Channel profile (with clips and VODs attached), live streams by game, a game-streams alias, top games, unified search, clips, VOD lists, VOD metadata with chapters, VOD chat replay, and a URL mode that auto-detects what you paste.
- Playable media links as a default field. Each live stream and VOD metadata record carries a ready-to-play HLS playlist URL, and each clip carries direct MP4 download links in 1080p, 720p, 480p, and 360p.
- Chat history without pulling the archive yourself. VOD chat-replay records come back with the commenter, the message text, the timestamp offset, and any badges or emotes already parsed out.
- VOD chapter markers. Game-change segments with position and duration, useful for building highlight timestamps.
- Two ways to avoid re-collecting data. Resume one interrupted run by pasting its run or dataset ID, or turn on incremental mode for scheduled runs that return only new, updated, or expired records.
- Built for automation. Pipe results into Notion, Linear, Airtable, or your other Apify integrations straight from the run.
Use Cases
- Stream discovery tools: surface currently live channels in a game, sorted by viewers, for a directory or alert bot.
- Channel and esports tracking: monitor a roster of channels' VOD history, clip performance, and follower counts over time.
- Highlight and clip curation: pull top clips per channel with direct MP4 URLs for re-use or archiving.
- Chat research: collect VOD chat-replay messages for sentiment, moderation, or community studies.
- Category and trend monitoring: track which games are trending right now with top games, per-category viewer, follower and live-channel (
broadcastersCount) counts.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
recordType | "live_stream" (also channel, game, clip, video, vod_metadata, vod_comment, search_channel, search_game) |
id | "123456789" |
login | "examplelogin" |
displayName | "Example Display" |
url | "https://www.twitch.tv/examplelogin" |
title | "Sample stream title" (live stream, clip, video, VOD metadata) |
viewersCount | 12345 (live channels and streams) |
viewCount | 54321 (clip, video, VOD metadata) |
followersCount | 100000 |
gameName / gameSlug / gameId | "Sample Game" / "sample-game" / "000000" |
boxArtUrl | "https://static-cdn.jtvnw.net/ttv-boxart/000000-285x380.jpg" |
thumbnailUrl | preview or profile image URL |
isPartner / isAffiliate | false / true |
durationSeconds | 185 (clip, video, VOD metadata) |
tags | ["English", "FPS"] |
streamHlsUrl | playable HLS playlist URL (live stream, live channel) |
vodHlsUrl | playable HLS playlist URL (VOD metadata) |
clipMp4Urls | { "1080": "https://.../1080.mp4", "720": "https://.../720.mp4" } (clip) |
chapters | [{ "type": "GAME_CHANGE", "positionMs": 0, "gameName": "Game A" }] (VOD metadata) |
messageText / commenterLogin | chat message text and commenter login (VOD chat replay) |
changeType / changedFields | "UPDATED" / ["viewersCount", "title"] (incremental mode only) |
scrapedAt | "2026-05-09T00:00:00.000Z" |
Channel records also carry the description, banner image, primary color, live status flags, last broadcast title, and linked social accounts. Clip records also carry the slug, embed URL, and curator login. VOD metadata records also carry the description, publish date, and mute info. VOD chat records also carry the commenter ID, user color, badges, emotes, and the video ID and URL they belong to.
How to Use
- Pick a mode (ten in total):
channel,live_streams(or its aliasgame_streams),top_games,search,clips,videos,vod_metadata,vod_comments, orurl. - Fill in only the fields for that mode's section. Other sections are ignored.
- Set Max results (and Max pages for paginated modes) to control run size and cost, then click Start.
- Download the dataset as JSON, CSV, or Excel, or read it through the API.
URL mode
Paste any of these twitch.tv URL shapes in urls:
| URL shape | Example | Returns |
|---|---|---|
| Channel page | https://www.twitch.tv/shroud | channel profile, clips, VODs |
| Mobile channel page | https://m.twitch.tv/ninja | channel profile, clips, VODs |
| Category page | https://www.twitch.tv/directory/category/just-chatting | live streams in the category |
| Legacy category page (by name) | https://www.twitch.tv/directory/game/Just%20Chatting | live streams in the category |
| All categories | https://www.twitch.tv/directory | most-watched categories |
| VOD page | https://www.twitch.tv/videos/2767213751 | VOD metadata |
| Clip page (channel path) | https://www.twitch.tv/shroud/clip/TangentialBillowingJalapenoYee-ccA2oB1pEYh4CHWJ | the clip |
| Clip page (clips host) | https://clips.twitch.tv/TangentialBillowingJalapenoYee-ccA2oB1pEYh4CHWJ | the clip |
| Channel videos tab | https://www.twitch.tv/shroud/videos | recent VODs of the channel |
| Channel clips tab | https://www.twitch.tv/shroud/clips?range=7d | top clips (range = 24hr, 7d, 30d, all) |
| Search page | https://www.twitch.tv/search?term=valorant | channels and categories matching the term |
A URL that matches none of these is skipped with a warning in the run log. With the urls field left untouched, 10 default URLs cover every shape in the table (the channel-path clip URL resolves to the same clip as the clips-host form, so it is not repeated). Behaviour change from the previous version: the default list went from 11 URLs to 10 distinct targets (the repeated channel-path clip URL was dropped, m.twitch.tv/shroud became m.twitch.tv/ninja, and the legacy category example became directory/game/Valorant). Incremental state saved by callers who left urls untouched keeps its key, so the 8 unchanged targets are not delivered or billed again as NEW; only the targets that are new in this default are NEW on their first run. Max results is split evenly across the URLs (at least 1 each while the total allows; the run still stops at Max results, so set it to at least the number of URLs to cover every URL), so the default 30 gives each of the 10 default URLs a share of 3 records; raise Max results to see more per URL, and a URL that returns fewer than its share does not hand the rest to the others. channels also accepts pasted channel URLs, and gameSlug also accepts a category name.
Channel profile, plus its clips and recent VODs:
{"mode": "channel","channels": ["shroud", "ninja", "pokimane"],"includeMediaUrls": true,"maxResults": 30}
Live streams in a game, with playable HLS URLs:
{"mode": "live_streams","gameSlug": "valorant","streamSort": "VIEWER_COUNT","includeMediaUrls": true,"maxResults": 50,"maxPages": 2}
Top clips for a channel, with MP4 download URLs:
{"mode": "clips","channels": ["xqc"],"clipPeriod": "LAST_WEEK","includeMediaUrls": true,"maxResults": 25}
VOD chat replay for one video:
{"mode": "vod_comments","videoIds": ["2767213751"],"maxResults": 200}
Output views
The dataset has one view per record kind: Records (overview), Channels, Live streams, Categories, Clips, Videos, VOD comments, and Changes (incremental). Pick one in the Console Output tab to see only that kind's columns.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/twitch-scraper").call(run_input={"mode": "live_streams", "gameSlug": "valorant"})for stream in client.dataset(run["defaultDatasetId"]).iterate_items():print(stream["displayName"], stream["viewersCount"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/twitch-scraper').call({ mode: 'live_streams', gameSlug: 'valorant' });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets, or webhooks from the Integrations tab.
Media URLs and pagination caps
Channel and search-channel rows also carry channelId, profileImageURL, a nested stream (while live), latestVideo, topClip, nextSchedule (next scheduled stream, null when none), lastBroadcastGameName (game of the last broadcast) and, in search mode, the keyword that matched.
includeMediaUrls attaches a playable HLS URL to live stream, live channel, and VOD metadata records (clip MP4 URLs are always included). maxResults stops a run after that many records; 0 means the hard cap of 100000 records. In channel mode with maxResults 0, each channel returns its profile plus 5 clips and 5 recent VODs. maxPages caps how many pages (about 30 items each) a paginated mode walks, 0 to walk until the source is exhausted or maxResults is hit. In channel, clips, and videos modes with multiple logins, the cap is shared roughly evenly across the requested channels.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted run: paste its run or dataset ID and this run skips every record already collected there, so you don't pay twice. - Incremental mode (
incrementalMode) is for scheduled runs over the same mode and filters. Each record is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on),REAPPEARED, orEXPIRED(only after a run that scanned the whole tracked scope, and only withemitExpired).stateKeynames or shares the stored state. A record isUPDATEDwhen a content field changes; live counters (viewersCount,viewCount,followersCount), rotatingthumbnailUrl, signedclipMp4Urls, HLS playlist URLs andscrapedAtare never compared and never appear inchangedFields, but stay in the output.ignoreFieldsForChangesadds more fields (names or dotted paths) to that list. With incremental mode off, output is exactly as before.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify โ Settings โ Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | channel | Which scrape to run: channel, live_streams, game_streams (alias of live_streams), top_games, search, clips, videos, vod_metadata, vod_comments, or url. Only the fields for the chosen mode are read. |
channels | array | ["shroud"] | Twitch logins, one per line. Used by channel, clips, and videos modes. |
clipPeriod | string | LAST_WEEK | Time window for clip collection: LAST_DAY, LAST_WEEK, LAST_MONTH, ALL_TIME. Used by clips mode. |
gameSlug | string | (none, sample valorant) | Category URL slug or category name (for example Just Chatting; a name that matches nothing fails the run with a clear message). Used by live_streams and game_streams modes. |
streamSort | string | VIEWER_COUNT | Sort order for the live stream list: VIEWER_COUNT, RECENT, RELEVANCE. |
searchQuery | string | (none, sample valorant) | Free-text keyword. Used by search mode. |
videoIds | array | (none, sample ["2767213751"]) | Numeric video IDs. Used by vod_metadata and vod_comments modes. |
urls | array | 10 example URLs, one per supported shape | Any twitch.tv URLs, one per line. Used by url mode; each is auto-detected (see the URL mode table). |
resumeFromRunId | string | (none) | Continue one specific interrupted run: paste its run or dataset ID and this run skips every record already collected there. |
incrementalMode | boolean | false | Turn on for scheduled or recurring runs against the same mode and filters. Classifies each record against remembered state. |
stateKey | string | (none) | Name or share an incremental-mode state. Left empty, a key is derived from the mode and its filters. |
emitUnchanged | boolean | false | Incremental mode only: also return (and bill) records that have not changed since the last run. |
emitExpired | boolean | false | Incremental mode only: also return (and bill) records no longer found, after a run that fully scanned the tracked scope. |
ignoreFieldsForChanges | array | [] | Incremental mode only: field names (or dotted paths like stream.title) not compared when detecting changes. They stay in the output. |
includeMediaUrls | boolean | true | Attach a playable HLS URL to live stream, live channel, and VOD metadata records. Clip MP4 URLs are always included. |
keywords | array | (none) | Search mode only: several queries in one run, merged with searchQuery. Each row carries its query in keyword. In search mode with several keywords the Max results limit applies to each keyword, so a run can return up to keywords x Max results records. |
maxItems | integer | (none) | Alias of maxResults (wins when set; 0 or empty means use maxResults). In search mode with several keywords it applies to each keyword, so a run can return up to keywords x Max results records. |
maxResults | integer | 30 | Stop after this many records. 0 means the hard cap of 100000 records (still capped by maxPages, hard cap 1000 pages). In url mode the number is split evenly across the URLs (at least 1 each while the total allows; set it to at least the number of URLs to cover every URL). In search mode with several keywords the limit applies to each keyword, so a run can return up to keywords x Max results records. |
maxPages | integer | 0 | Pages to walk for paginated modes, about 30 items per page. 0 means walk until the source is exhausted or maxResults is hit (hard cap 1000 pages). |
proxyConfiguration | object | Apify Proxy | Connection settings. Apify Datacenter is the default and works for this site; a backup connection is used automatically if it is unavailable. |
mcpConnectors | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which records are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"recordType": "live_stream","id": "123456789","login": "examplelogin","displayName": "Example Display","url": "https://www.twitch.tv/examplelogin","title": "Sample stream title","viewersCount": 12345,"createdAt": "2026-01-01T00:00:00Z","streamType": "live","gameName": "Sample Game","gameSlug": "sample-game","gameId": "000000","boxArtUrl": "https://static-cdn.jtvnw.net/ttv-boxart/000000-285x380.jpg","thumbnailUrl": "https://static-cdn.jtvnw.net/previews-ttv/live_user_examplelogin-0000x0000.jpg","broadcasterId": "00000000","broadcasterDescription": "Sample broadcaster bio.","profileImageUrl": "https://static-cdn.jtvnw.net/jtv_user_pictures/0000-profile_image-70x70.png","isPartner": true,"isAffiliate": false,"followersCount": 100000,"tags": ["English", "FPS"],"streamHlsUrl": "https://usher.ttvnw.net/api/channel/hls/examplelogin.m3u8?sig=0000&token=%7B...%7D","scrapedAt": "2026-05-09T00:00:00.000Z"}
Plan Requirement
The default Apify Datacenter connection works for this site: pages and the data API both answered on it in testing. One field is the exception: on a Datacenter connection Twitch withholds a channel's linked social accounts, so socialMedias is empty on channel records and the run says so in its log and status ("Twitch withheld socialMedias on this connection ... It is usually returned when running with Apify proxy group: RESIDENTIAL."); every other field is read normally. Apify proxy access needs a paid Apify plan. If the default connection is refused, the actor continues on a backup connection and logs "Served over the backup connection (the default connection was unavailable)." On a free plan it first prints a notice that the selected connection is unavailable and that free plans do not include residential proxies. If nothing can be loaded, the run stops with one of these messages instead of an empty result:
- "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required" (also shown when your own proxy URLs have bad credentials).
- "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Twitch pages. Upgrade ... or supply your own proxy URLs, then try again."
- "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."
Choosing residential is optional and billed to your Apify plan; it is only needed if you want socialMedias on channel records.
FAQ
How much does it cost?
Pay per event. The events and when each is charged:
| Event | When it is charged |
|---|---|
Actor Start (apify-actor-start) | once per run |
Result (apify-default-dataset-item) | once for each record pushed to the dataset |
Media URL enrichment (media-url-enrichment) | once for each pushed record that carries an attached playable HLS URL (includeMediaUrls on) |
The price of each event is shown on the Pricing tab; paid Apify plans get lower tiered rates. Unchanged or suppressed records in incremental mode are not billed. Use Max results to cap the cost of a run (in search mode with several keywords the limit applies to each keyword, so a run can return up to keywords x Max results records). Cost guard for chat replay: every vod_comments message is its own billed record and a long VOD can hold hundreds of thousands, so keep Max results (default 30) set to what you need, and note that 0 means the hard cap of 100000 records (and 1000 pages), not unlimited.
Is it legal to scrape Twitch?
This actor collects only publicly available channel, stream, clip, video, and chat data. You are responsible for how you use it: follow Twitch's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Usernames, chat messages, and other user-generated content may carry privacy or copyright considerations, so handle them accordingly.
Can I get only new or changed records on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated, reappeared, or (if enabled) expired records, and unchanged ones are not billed.
What's the difference between Resume and Incremental mode?
Resume (resumeFromRunId) continues one specific interrupted run: it skips whatever that run already delivered. Incremental mode (incrementalMode) is for running the same mode and filters again and again on a schedule and getting only what changed since last time. They solve different problems and can be combined for a first run.
Why did my run fail instead of returning an empty dataset?
If the site refuses a request and a refreshed session does not help, the run fails with a clear message rather than silently returning nothing, so an empty result is never confused with an unreadable one. A missing required field for the chosen mode (for example an empty channels list in channel mode, or no gameSlug in live_streams mode) also fails the run, and the message names the inputs to set. channels defaults to shroud, so a bare call in the default channel mode returns data.
If Twitch asks for a fresh verification in the middle of a multi-page read (live_streams, url categories, vod_comments) and it cannot be completed, the pages already read are kept and delivered, the run ends with a status message saying only the first N pages could be read, and the scan counts as incomplete (no EXPIRED detection, no completed-scan baseline saved; with incremental mode on, the records already delivered are still remembered so the next run does not deliver and bill them again as new). This partial result applies only when the fresh verification itself failed: a lost connection or a refused proxy in the middle of a read still fails the run with the connection message. Only rows actually delivered are billed. If not even the first page could be read, the run fails with a clear message and delivers nothing. If the verification fails only on a later target or URL after earlier pages were read but returned no records, the run fails with a message saying the pages read before it returned no records, so nothing was collected or charged. A channel with no matching content, a search with no hits, or an incremental run with nothing changed all succeed normally and simply return fewer (or zero) records, with the reason noted on the run's status.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear, or Airtable.
๐ Want more video data?
Pair this actor with these related scrapers from the same team:
| ๐ฑ Tiktok Live Recorder Record TikTok live streams to MP4 with full metadata, all stream quality URLs, and... | ๐ฑ Universal Media Extractor Extract videos, audio, and metadata from 1000+ websites including YouTube, TikTok... |
| ๐ฑ TikTok Profile, Hashtag, Search, Video & Trending Scraper Scrape TikTok without login. Extract profiles, bios, follower stats and videos; search by... | ๐ฑ ViewStats Scraper Scrape YouTube channel analytics from ViewStats. Look up channels by handle, URL, or... |
| ๐ฑ Lemon8 Media Scraper Lightweight Lemon8 scraper for extracting image and video URLs from posts, with optional... | ๐ฑ Wattpad Scraper Scrape Wattpad stories by keyword, tag, category, language or URL. Extract authors... |
๐ Browse all abotapi scrapers
๐ฌ Support
- ๐ Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- ๐ ๏ธ Need another site, extra fields or a private build? Email contact@abotapi.com.
- โญ Enjoying it? A quick review on the actor page helps other users find it.