# Kick Live Status & Viewer Count Monitor (`neverempty/kick-live-status-monitor`) Actor

Check whether Kick streamers are live right now: viewer count, stream title, category, tags, start time and follower count. Turn monitoring on and a run returns a channel only when it goes live, ends a stream or starts a new one, for a small fee per channel checked.

- **URL**: https://apify.com/neverempty/kick-live-status-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Social media, Videos, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.29 / 1,000 channel status row returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Kick Live Status & Viewer Count Monitor - go-live alerts for Kick streamers, priced per check

Check whether Kick channels are live right now - viewer count, stream title, category, tags, language, start time, how long they have been live and follower count - and, with monitoring on, **receive a row only when something happens**: a channel goes live, ends a stream, or starts a new one. A poll on which nothing changed returns a single free `no-change` row.

In monitoring mode you pay a small fee per channel checked, **$0.30 per 1,000 checks**, and the normal row price only for the rows that report a change. Watching 20 streamers every 5 minutes is 172,800 checks a month, which is **$51.84** in check fees, plus the change rows you receive.

### Best for

- **Go-live alerts** - schedule a run every few minutes, send the dataset to Slack, Discord, Telegram or a webhook, and every row you receive is an event worth sending.
- **Sponsored-stream verification** - confirm that a paid Kick streamer actually went live, in which category, for roughly how long, and the highest viewer count seen while you were watching.
- **Creator and competitor tracking** - log when a roster of Kick channels streams, what they stream and how big those streams get, next to their follower counts.
- **"Who is live now" lists** - check a roster once with `onlyLive` and receive only the channels that are live.

### What each row contains

- **What you asked for and what Kick returned** - `inputChannel`, `slug`, `channelId`, `userId`, `username`, `channelUrl`.
- **Live status** - `isLive`, `streamId`.
- **Only while live** - `title`, `category`, `categoryId`, `startedAt`, `liveDurationSeconds`, `viewerCount`, `language`, `isMature`, `tags`, `thumbnailUrl`.
- **Channel** - `followerCount`, `isVerified`, `isAffiliate`, `isBanned`, `subscriptionEnabled`, `profileImageUrl`.
- **Monitoring** - `change`, `isFirstCheck`, `previousCheckedAt`, `previousIsLive`, `previousStreamId`, and for a stream that ended: `endedAfter`, `endedBefore`, `streamDurationSecondsAtLeast`, `streamDurationSecondsAtMost`, `peakViewerCountSeen`, `checksWhileLive`.

Measured on 2026-10-19: iceposeidon was live in IRL with `viewerCount` 17054 and 264743 followers; the stream had started at 17:35 UTC the day before.

Every row is matched to the channel you asked for by the channel name Kick returns, so a row is never filled from another channel's answer. `startedAt` is converted to ISO 8601 in UTC, and `followerCount` is a number.

#### Viewer count

`viewerCount` is the number Kick returns at the moment of the request. Kick does not publish a stream's peak viewer count, so `peakViewerCountSeen` is **the highest value your monitoring runs saw** while the stream was live.

#### What is deliberately not returned for a channel that is not live

`title`, `category`, `tags`, `viewerCount`, `language` and `startedAt` are filled **only while the channel is live**. For a channel that is not live you get `isLive: false` with the channel details and follower count, and these fields are `null` rather than old values presented as current.

### Monitoring, and what it costs

Turn **monitoring mode** on and the Actor remembers each channel's live status. On later runs a channel comes back only with one of these `change` values:

| `change` | Meaning |
|---|---|
| `first-check` | The channel had not been seen before. Every channel is returned once on the first run, to set the baseline. |
| `went-live` | Not live at the previous run, live now. |
| `ended` | Live at the previous run, not live now. The row says the stream ended between `endedAfter` (the previous run) and `endedBefore` (this run), gives the duration as a range (`streamDurationSecondsAtLeast` to `streamDurationSecondsAtMost`), and the highest viewer count seen by your runs while it was live. |
| `new-stream` | Live at both runs, but with a different stream id: the previous stream ended and a new one started in between. |

A channel whose status did not change is **not returned**, so it costs no row price; the check fee for reading it still applies.

The duration of a stream is a range, not a single number, because the Actor only knows the status at the moments it runs. Run it more often for a narrower range.

The status is remembered **per channel, not per list**, so adding a channel does not reset the channels already being watched. In monitoring mode `maxChannels` does not cut changes: every change among the channels read is returned. If the run's maximum total charge stops a change from being delivered, that change is not remembered, so the channel still counts as changed the next time it is checked.

**Do not put the same channel in two schedules that can run at the same time.** Each run writes back only the channels it checked, merged into the statuses as they are at that moment, then reads them again and writes once more if another run overwrote them - so two overlapping runs over different channels keep each other's history. But Apify's key-value store has no atomic update, so this cannot be made watertight: runs that finish at the same instant, or check the same channel at the same time, can still overwrite each other's record, and the next run may return a `first-check` or a change a second time.

#### What monitoring costs

- **$0.30 per 1,000 channel checks** - charged in monitoring mode for every channel whose status was read, whether or not it changed. Names Kick has no channel for and bot checks are not charged.
- **The row price** - charged only for the rows returned, which in monitoring mode means `first-check` rows and changes.

The Actor reads only as many channels as the run's maximum total charge can pay for with both a check and a change row each, so a low limit cannot use up the budget on check fees and leave nothing for the rows. Channels beyond that are not checked; a `budget-reached` row says how many, and raising the maximum total charge lets them be checked. Runs with monitoring off are charged per row only.

### Rows that are never charged

When there is nothing to return, this Actor says why instead of returning an empty result:

- `no-such-channel` - Kick has no channel under that name. Kick gives the same answer for a channel that was banned or removed, so the row does not claim which.
- `blocked` - Kick answered with a bot check, a rate limit or an empty response. The channel is not reported as offline. Requests go out directly first, and only this answer makes the run switch to a proxy session and try again.
- `unreadable` - the answer could not be read: Kick sent an error (its message is included), answered with a different channel, or sent a stream that is not marked as live, which is not translated.
- `robots-disallowed` - Kick's robots.txt does not allow the request for that channel, so it was not made. If robots.txt itself cannot be read, no channel is requested and every channel gets a `blocked` row.
- `no-change` - monitoring ran and no channel changed. The row lists the channels that are still live.
- `no-match` - `onlyLive` is on and none of the channels is live right now.
- `invalid-input` - the text is not a Kick channel name or channel URL, so nothing was requested.
- `budget-reached` - the run's maximum charge was reached: the row says how many ready rows fit, or how many channels were not checked in monitoring mode.
- `not-checked` - some channels were not checked or not returned, because `maxChannels` had already been reached (monitoring off), or because more than 1,000 channels were given. The row says how many.

### How fast

Each channel is one request, with a 0.3-second pause between requests, so 100 channels take well under a minute. Measured from Apify on 2026-10-19, 60 requests in a row all succeeded with an average answer time of 82 ms.

### Input

| Field | What it does |
|---|---|
| `channels` | The channels to check: `name`, `@name`, or a channel URL (`https://kick.com/name`). Names are sent in lower case, so `XQC` and `xqc` are the same channel and are checked once. If empty, the example channels xqc and iceposeidon are checked. |
| `maxChannels` | How many charged rows to return in total (up to 1,000). |
| `onlyLive` | Return only the channels that are live right now. |
| `monitoringMode` | Remember the status and return a channel only when it changes. |
| `resetMonitoringState` | Forget every remembered status and start a fresh baseline. |
| `useProxy` | Retry through a proxy if Kick answers with a bot check or a rate limit. Every request goes out directly first. |

### Questions

**How do I get an alert when a Kick streamer goes live?**
Turn on `monitoringMode`, schedule the Actor, and send the run's dataset to your channel with an Apify integration or webhook. How often is a trade-off between how fast you hear about a stream and the check fee: 20 channels every 5 minutes is 172,800 checks a month ($51.84); every 15 minutes is 57,600 ($17.28). Rows with `change: "went-live"` are go-live events; `ended` rows tell you the stream is over.

**Does monitoring charge me when nothing changed?**
Unchanged channels are not returned and cost no row price. Each channel checked costs the check fee ($0.30 per 1,000), changed or not - that is what pays for polling.

**Do I need a Kick account or API key?**
No. The Actor reads the public channel data Kick's website loads, with no login.

**Can I get the viewer count of a Kick stream over time?**
Yes. Run it on a schedule with monitoring off and every run returns each channel's current `viewerCount` with a `scrapedAt` time; with monitoring on you get the highest value seen in each `ended` row.

### Good to know

- The rows come from the channel endpoint that Kick's own website uses (`kick.com/api/v2/channels/<name>`). It is not a documented developer API, so **Kick can change it without notice**; if it does, the run returns free `unreadable` rows with the message Kick sent instead of guessing.
- For a channel that is not live, Kick's channel data does not include its most recent past stream, so no last-stream title or time is returned.
- A stream that starts and ends entirely between two monitoring runs is not seen. Run more often if short streams matter to you.
- `viewerCount` is whatever Kick reported at the moment of the request; it is not an average or a peak.
- Underscore and hyphen are part of the name: `ice_poseidon` and `ice-poseidon` are two different Kick channels, so names are sent exactly as typed apart from lower case.
- Up to 1,000 channels per run; split longer lists across runs.
- This Actor reports status, stream details and counts; it does not record or download streams and does not read chat.

### Example input

```json
{
  "channels": ["xqc", "iceposeidon", "https://kick.com/adinross"],
  "monitoringMode": true,
  "onlyLive": false,
  "maxChannels": 100
}
```

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

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

Channels to check: name, @name, or a channel URL (https://kick.com/name, with or without the scheme). Names are sent in lower case and duplicates are checked once. Underscore and hyphen are kept as typed, because ice_poseidon and ice-poseidon are different Kick channels. Up to 1,000 per run, one request per channel. If you leave this empty, the example channels xqc and iceposeidon are checked and every row says so in inputChannel.

## `maxChannels` (type: `integer`):

How many charged rows to return in total when monitoring is off: reading stops once this many channels have been read (with onlyLive, once this many live channels have been found), and a free row says how many were not checked or not returned. In monitoring mode it does not limit the changes returned - every change among the channels read comes back.

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

On = channels that are not live are checked but not returned, so they cost no row price (in monitoring mode the check fee still applies). Handy for a 'who is live now' list.

## `monitoringMode` (type: `boolean`):

Off = every channel you listed comes back with its current status, charged per row. On = the Actor remembers each channel and, on later runs, returns it only when it went live, ended a stream, or started a new stream. The first run returns every channel once to set the baseline. **In monitoring mode every channel checked costs $0.30 per 1,000 checks, changed or not** (names Kick has no channel for and bot checks are free), plus the row price for the rows returned. Example: 20 channels every 5 minutes = 172,800 checks a month = $51.84. The Actor reads only as many channels as the run's maximum total charge can pay for with a check and a change row each. The status is remembered per channel; do not put the same channel in two schedules that can run at the same time.

## `resetMonitoringState` (type: `boolean`):

Clears every remembered live status for this Actor, so the next monitoring run returns each channel once again. This affects all your monitoring runs, because the status is stored per channel rather than per list.

## `useProxy` (type: `boolean`):

Every request goes out directly first, which worked from Apify on every attempt measured on 2026-10-19 (150 of 150). If Kick answers with a bot check, a rate limit or an empty response, this switches to a proxy session and tries again. With it off, that answer is returned as free 'blocked' rows and no proxy is paid for.

## Actor input object example

```json
{
  "channels": [
    "xqc",
    "iceposeidon"
  ],
  "maxChannels": 100,
  "onlyLive": false,
  "monitoringMode": false,
  "resetMonitoringState": false,
  "useProxy": true
}
```

# Actor output Schema

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

One row per Kick channel: the name you asked for and the channel Kick returned (slug, channel id, display name), whether it is live right now, and for live channels the stream id, title, category, tags, language, start time, how long it has been live, current viewer count and preview image; follower count and verified, affiliate and banned flags; and, in monitoring mode, what changed since the last run (went live, ended or new stream) with the bounds of the stream's duration and the highest viewer count seen. Names Kick has no channel for and bot checks come back as their own free rows; in monitoring mode unchanged channels are not returned.

# 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 = {
    "channels": [
        "xqc",
        "iceposeidon"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/kick-live-status-monitor").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 = { "channels": [
        "xqc",
        "iceposeidon",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/kick-live-status-monitor").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 '{
  "channels": [
    "xqc",
    "iceposeidon"
  ]
}' |
apify call neverempty/kick-live-status-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/kick-live-status-monitor"
        }
    }
}
```

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/KC4TOOm818SnbFCNP/builds/fS4ixW2isjRCcIeKx/openapi.json
