# Twitch Search & Streams Scraper (`cirkit/twitch-search-scraper`) Actor

Search Twitch channels, live streams, VODs and categories, no OAuth needed. Keyword search runs to Twitch's natural end (~1,000 channels per term, not 30) and category runs slice by language to beat Twitch's 30-row cap. Returns followers, viewers, uptime, category, tags, partner status.

- **URL**: https://apify.com/cirkit/twitch-search-scraper.md
- **Developed by:** [Crikit](https://apify.com/cirkit) (community)
- **Categories:** Social media
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.30 / 1,000 result extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Twitch Search & Streams Scraper

Search Twitch channels, live streams, VODs and categories. No OAuth token, no login, no
browser — this talks to Twitch's public GraphQL endpoint directly, which is why it is fast
and cheap.

**$1.30 per 1,000 results.** Pay only for rows you receive; duplicates are removed before
you are billed.

### What makes this different

Every other Twitch scraper stops at Twitch's first page. Twitch caps a directory request at
**30 rows** and gates deeper paging behind an anti-bot check, so a scraper that asks the
obvious way gets 30 streams and calls it a day.

This one goes further, two ways:

- **Keyword search runs to Twitch's natural end.** `"minecraft"` returns **982 channels**
  across 34 pages, not 30. Videos go about 700 deep per term.
- **Category runs slice the directory by broadcaster language.** Each language is a
  separate 30-row window, so sweeping twelve of them returns **~200–250 live streams per
  category** instead of 30, with no duplicates across slices.

If you are doing influencer discovery, esports monitoring or audience research, that
difference is the whole product — the 31st result is where the mid-tier streamers you can
actually afford to work with begin.

### Three modes

#### Search — keyword search across Twitch

```json
{
  "mode": "search",
  "searchTerms": ["valorant", "speedrun"],
  "searchIndex": "channels",
  "maxItems": 500
}
```

`searchIndex` picks what you get back:

| Index | Depth | Notes |
|---|---|---|
| `channels` | ~1,000 per term | Live and offline, with follower counts |
| `videos` | ~700 per term | VODs with view counts and duration |
| `games` | ~29 per term | Twitch's category index does not paginate |

#### Category — live streams in a directory category

```json
{
  "mode": "category",
  "categories": ["Just Chatting", "VALORANT"],
  "languages": ["EN", "ES", "PT"],
  "maxItems": 500
}
```

Category names, slugs (`just-chatting`) and directory URLs all work. Leave `languages`
empty to sweep the twelve languages carrying the most Twitch inventory.

Set `sortBy` to `VIEWER_COUNT_ASC` to surface the smallest live channels first — the usual
ask for sponsorship prospecting.

#### Channels — look up specific channels

```json
{
  "mode": "channels",
  "channels": ["shroud", "https://www.twitch.tv/kaicenat", "@ninja"],
  "maxItems": 100
}
```

Returns the same row shape whether the channel is live or offline, so you can re-run it on
a schedule and diff.

### Filters

- `onlyLive` — drop channels that are not broadcasting right now.
- `minViewers` — drop live channels under a concurrent-viewer floor.

**Both filters run before billing.** You are never charged for a row a filter removed.

### Output

One flat row per result. Channels, category streams and direct lookups all share the same
shape, so you can union all three into one table keyed on `login` without reconciling
schemas.

```json
{
  "recordType": "channel",
  "id": "598903130",
  "login": "valorant_americas",
  "displayName": "VALORANT_Americas",
  "url": "https://www.twitch.tv/valorant_americas",
  "description": "Official VALORANT Champions Tour Broadcast Channel.",
  "followers": 940598,
  "isPartner": true,
  "isAffiliate": false,
  "isLive": true,
  "streamTitle": "LEV vs MIBR - VCT Americas Stage 2",
  "viewersCount": 6930,
  "streamStartedAt": "2026-08-08T20:30:24Z",
  "streamUptimeSeconds": 13865,
  "gameName": "VALORANT",
  "gameSlug": "valorant",
  "broadcasterLanguage": "EN",
  "tags": ["Esports", "English"],
  "thumbnailUrl": "https://static-cdn.jtvnw.net/previews-ttv/live_user_valorant_americas-640x360.jpg",
  "accountCreatedAt": "2020-10-23T01:23:36.264718Z",
  "lastBroadcastStartedAt": "2026-08-08T20:30:29.349559Z",
  "searchQuery": "valorant",
  "position": 1,
  "scrapedAt": "2026-08-09T00:21:29.086Z"
}
```

Offline channels still carry `streamTitle`, `gameName`, `followers` and
`lastBroadcastStartedAt`, so a dormant-channel sweep is one sort away.

Measured on a 300-row run: every field above populated on 100% of rows, and every
stream-specific field populated on 100% of the rows that were actually live.

### What this does not do

- **Chat, clips and subscriber counts.** Not covered.
- **Deeper than ~1,000 channels per search term.** That is Twitch's own end of results, not
  a limit here. Use more, narrower terms.
- **More than ~30 streams per category per language.** Twitch gates deeper directory paging
  behind an anti-bot check that would need a browser farm to clear. Language slicing is the
  workaround, and it is why a category run returns 200+ rather than 30.
- **Historical data.** Viewer counts and uptime are true as of `scrapedAt` and nothing else.

### Notes

- Runs on Apify's default datacenter proxy. Residential is not needed and would cost
  roughly ten times more.
- A search that legitimately matches nothing writes a single `no_results` row explaining
  why, rather than an empty dataset you cannot distinguish from a broken run.
- `maxItems` is a hard cost ceiling. It must be 1 or more; `0` is refused rather than
  silently reinterpreted.

# Actor input Schema

## `mode` (type: `string`):

Search runs a keyword search across Twitch. Category pulls the live streams currently in one or more directory categories. Channels looks up a known list of channel names directly.

## `searchTerms` (type: `array`):

Keywords to search, one per line. Used only in Search mode. Each term is paginated to Twitch's natural end, which is roughly 1,000 channels or 700 videos per term.

## `searchIndex` (type: `string`):

Which Twitch index to search. Channels and Videos paginate deeply. Categories returns a single page of about 29 matches, because that is all Twitch's category index will serve.

## `categories` (type: `array`):

Directory categories to pull live streams from, used only in Category mode. Type them as they appear on Twitch, for example "Just Chatting" or "VALORANT". Slugs ("just-chatting") and directory URLs also work.

## `languages` (type: `array`):

Which broadcaster languages to pull, used only in Category mode. Twitch serves at most 30 streams per request, so this actor asks once per language to get past that cap - more languages means more streams. Leave empty to sweep the twelve languages that carry the most Twitch inventory.

## `channels` (type: `array`):

Channel names or twitch.tv URLs to look up, used only in Channels mode. Returns the same row shape as the other modes, live or offline.

## `maxItems` (type: `integer`):

Hard cap on rows written to the dataset across every term, category or channel in this run. You are billed per row, so this is also your cost ceiling. Must be 1 or more.

## `sortBy` (type: `string`):

Ordering for Category mode. Highest viewers first is the Twitch default; lowest viewers first surfaces small channels, which is what influencer discovery usually wants.

## `onlyLive` (type: `boolean`):

Drop channels that are not broadcasting at the moment this runs. Filtering happens before billing, so you are never charged for rows this removes. Category mode is always live by definition.

## `minViewers` (type: `integer`):

Drop live channels below this concurrent viewer count. Applied before billing. Leave empty for no floor.

## `proxyConfiguration` (type: `object`):

Twitch's public GraphQL endpoint answers datacenter proxies, so the default Apify proxy is enough - residential is not needed and would cost roughly ten times more.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerms": [
    "valorant",
    "speedrun"
  ],
  "searchIndex": "channels",
  "categories": [
    "Just Chatting",
    "VALORANT"
  ],
  "languages": [
    "EN",
    "ES",
    "PT"
  ],
  "channels": [
    "shroud",
    "https://www.twitch.tv/kaicenat"
  ],
  "maxItems": 100,
  "sortBy": "VIEWER_COUNT",
  "onlyLive": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Every Twitch channel, stream, video or category matched by this run, one row each.

## `resultsCsv` (type: `string`):

The same results as a downloadable CSV file.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "searchTerms": [
        "valorant"
    ],
    "searchIndex": "channels",
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("cirkit/twitch-search-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "search",
    "searchTerms": ["valorant"],
    "searchIndex": "channels",
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("cirkit/twitch-search-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "searchTerms": [
    "valorant"
  ],
  "searchIndex": "channels",
  "maxItems": 100
}' |
apify call cirkit/twitch-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cirkit/twitch-search-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/6waiwwhxExKinKT5f/builds/2HpnIwMuUPOyQD3xF/openapi.json
