# FlexTV Scraper — Catalog, Genres, Episodes & Engagement (`signalbench/flextv-scraper`) Actor

Scrape the FlexTV short-drama catalog: titles, synopses, full genre taxonomy, episode lists, exact view, collect & like counts, badges and comps, in 16 locales. Server-rendered HTML (no browser) — fast and cheap.

- **URL**: https://apify.com/signalbench/flextv-scraper.md
- **Developed by:** [SignalBench](https://apify.com/signalbench) (community)
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 title scrapeds

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

## FlexTV Scraper — Catalog, Genres, Episodes & Engagement

Scrape the **FlexTV** short-drama catalog without the app: **titles, full
synopses, the complete genre taxonomy, per-episode lists, exact view and collect
counts, like counts, Hot/New badges and "Recommended For You" comps**, in
**16 locales**. The Actor reads FlexTV's server-rendered pages directly
(**no browser**), so runs are fast and cheap. Export as **JSON, CSV, Excel or
XML**, or pull the data through the Apify API into your app, agent, notebook or
spreadsheet.

There's no public FlexTV API and, until now, no dedicated structured-data source
for it. FlexTV (YUDER PTE. LTD.) publishes roughly **700 English titles**, plus
separate catalogs for 15 other locales.

> **Never charged for failures.** Flat **$2 per 1,000 titles** ($0.002 each),
> billed only per title actually stored. Empty, invalid and failed pages are
> never stored, so they're never charged.

### What it scrapes

| Field | Description |
|---|---|
| **id / slug / url** | 10-character series ID, URL slug and canonical `/movie/` URL |
| **title / isDubbed** | Title, with FlexTV's `(DUBBED)` prefix split out into `isDubbed` |
| **synopsis** | Full synopsis |
| **genres / genreIds** | Every tag on the title: top-level genre plus timeline, story beat, relationship and identity tags (up to 8) |
| **topLevelGenres** | The subset that are FlexTV browse genres (Romance, Suspense, Fantasy, …) |
| **badge** | `Hot`, `New` or `null` |
| **views** | Exact view count (integer, not the rounded display value) |
| **collects** | Exact collect (save-to-list) count |
| **likes** | Like count |
| **episodeCount / episodes** | Episode count, plus every episode's number, URL, views, duration and publish time |
| **availableLanguages** | Every language the series ships in |
| **comps** | FlexTV's "Recommended For You" set for the title |
| **posterUrl** | Original-size poster |

Each run also saves the **full genre taxonomy** for the locale (55 tags across
Genre, Timeline, Story Beats, Relationships and Identities, each with its browse
URL) to the key-value store as `TAXONOMY`. It's scraped live every run, so it
never goes stale.

### The three engagement counters, and what they mean

FlexTV shows two numbers on catalog cards and a different two on title pages,
which makes it easy to mislabel them. They were checked against FlexTV's own
page data before the fields were named:

- **`views`**: the play-icon number on catalog cards (`122.8K`). The Actor stores
  the exact integer behind it (`122879`), and the display string in `viewsDisplay`.
- **`collects`**: the bookmark-icon number on cards and the "Collect" number on
  title pages (`34.8K` → `34883`). FlexTV's own schema.org markup labels this
  number `LikeAction`. It isn't likes, so don't treat it as likes.
- **`likes`**: the "Like" number, shown only on title pages (`578`). It's only
  published rounded, so it's parsed from the display value. It's `null` when
  **Include episodes** is off, because that skips the title page.

FlexTV's views are real per-title view counts. They still come from FlexTV's own
counting, so compare trends *within* FlexTV rather than averaging them against
other platforms' view counts.

### Modes

- **Genres** (default): walks the genres you list. Any tag works, top-level
  (`Romance`, `Suspense`, `Urban`) or facet (`Rebirth`, `CEO`, `Hidden
  Identity`, `Alpha Wolf`). Names are matched against the live taxonomy, and an
  unknown name is reported with the full list of valid ones. With nothing set,
  it scrapes 25 `Romance` titles.
- **Full catalog**: walks every title in the locale. Set **Max titles** to cap
  size and cost.
- **Title URLs**: scrapes exactly the `/movie/` URLs you give it. Episode URLs
  are collapsed to their series.

**Locales:** `en`, `ja`, `ko`, `th`, `id`, `es`, `fr`, `de`, `pt`, `it`, `ar`,
`tr`, `ru`, `tc` (Traditional Chinese), `sc` (Simplified Chinese), `vi`. Each
locale is its own catalog with its own title IDs and localized genre names. Tag
IDs (`genreIds`) are shared across locales, so use them to line genres up.

### Input example

```json
{
    "mode": "genres",
    "genres": ["Romance", "Rebirth"],
    "maxTitles": 200,
    "includeEpisodes": true,
    "includeComps": false,
    "locale": "en"
}
```

### Output example

```json
{
    "platform": "flextv",
    "id": "KbZ2raxZaP",
    "slug": "my-three-brothers-finally-chose-me",
    "url": "https://www.flextv.cc/movie/my-three-brothers-finally-chose-me-KbZ2raxZaP",
    "locale": "en",
    "title": "My Three Brothers Finally Chose Me",
    "isDubbed": false,
    "synopsis": "In her first life, Selena spent years begging for the love of the family that chose Kylie over her...",
    "genres": ["Rebirth", "Comeback", "Contemporary", "Clapback", "Family", "Real vs Fake Heiress", "Rich Family", "Female-Centric"],
    "topLevelGenres": ["Family"],
    "badge": "Hot",
    "views": 122879,
    "viewsDisplay": "122.8K",
    "collects": 34883,
    "collectsDisplay": "34.8K",
    "likes": 578,
    "likesDisplay": "578",
    "episodeCount": 66,
    "episodes": [
        { "number": 1, "url": "https://www.flextv.cc/episodes/episode-1-my-three-brothers-finally-chose-me-KbZ2raxZaP", "views": 10579, "durationSec": 172, "publishedAt": "2026-09-03T08:12:53.000Z" }
    ],
    "availableLanguages": ["en", "ja", "ko", "th", "id", "es", "fr", "de", "pt", "it", "ar", "tr", "vi", "tc", "sc"],
    "posterUrl": "https://file-cdn.flextv.cc/image/8c/d88feef49b61f8f42fbf42a89b5357.jpg",
    "detailFetched": true,
    "scrapedAt": "2026-09-14T22:14:18.354Z"
}
```

### Who uses this

- **Vertical-drama studios & producers**: see which tropes pull views and saves
  before you greenlight a project. The 55-tag taxonomy is finer-grained than any
  other short-drama platform's.
- **Licensors & distributors**: catalog intelligence per locale.
- **Market & trend analysts**: exact per-title and per-episode view counts over
  time.

### How it works

FlexTV server-renders every page with Nuxt, so one plain request per page carries
all the data. No headless browser, no JavaScript and no private API. Catalog
listings paginate by path (`/dramas/all-dramas/2`). The last page is worked out
from FlexTV's own title count, and every page is checked against the page number
FlexTV says it served.

**Locale-leak protection:** under load, FlexTV's servers sometimes answer one
locale's page with another locale's titles (an English page full of Japanese
titles, for example). The Actor checks each title's own language tag and
retries any page that comes back mixed, so a leak never reaches your data.

The Actor scrapes only **public catalog metadata**. No video streams, no user
data, no accounts. It identifies itself honestly and doesn't impersonate
search-engine crawlers.

### FAQ

**How much does it cost?** $0.002 per stored title, **$2 per 1,000**. The free
Apify plan's $5 monthly credit covers about 2,500 titles.

**Why are `likes` and `episodeCount` null on some records?** You turned
**Include episodes** off, which skips title pages. Or a title page failed after
retries and the Actor kept the catalog-card data rather than dropping the title
(`detailFetched: false` shows which).

**Is scraping this legal?** The Actor extracts publicly available catalog
metadata. As always, consult your own counsel for your specific use case.

### Development

```bash
npm install
npm test                                   # builds, then runs extraction tests against saved pages
./run-test.sh smoke '{"maxTitles":5}'      # local run with its own storage dir
```

# Actor input Schema

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

What to scrape. **Genres** walks only the genres you list. **Full catalog** walks every title in the locale (~700 in English). **Title URLs** scrapes exactly the FlexTV titles you provide.

## `genres` (type: `array`):

Any FlexTV genre or sub-tag, by name as shown on the site — top-level genres (`Romance`, `Suspense`, `Fantasy`, `Urban`, …) and facet tags (`Rebirth`, `Revenge`, `CEO`, `Hidden Identity`, `Alpha Wolf`, …) both work. Resolved live against the site's taxonomy; a name that doesn't exist is reported with the full list. In non-English locales, use the localized name or the tag ID from the TAXONOMY record.

## `titleUrls` (type: `array`):

FlexTV `/movie/{slug}-{id}` URLs. `/episodes/episode-N-…` URLs are collapsed to their series automatically, and a locale prefix (`/ja/movie/…`) is honoured.

## `maxTitles` (type: `integer`):

Maximum number of titles to store. This caps your cost — 1 stored title = 1 charge.

## `includeEpisodes` (type: `boolean`):

Fetch each title's detail page: adds episode count, the per-episode list (number, URL, views, duration, publish time), like count, all tags and available languages. Off = listing data only (faster, but no episodes or likes).

## `includeComps` (type: `boolean`):

Add the comp set FlexTV shows on each title's page (id, URL, title, genres, badge). Fetches the detail page.

## `locale` (type: `string`):

Which localized FlexTV catalog to walk. Each locale is a separate catalog with its own title IDs, titles and localized genre names.

## `maxConcurrency` (type: `integer`):

Parallel requests. Kept modest by default — raise it for big runs if FlexTV isn't rate-limiting.

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

Apify Proxy. If you see 403/429 responses, switch to **Residential**.

## Actor input object example

```json
{
  "mode": "genres",
  "genres": [
    "Romance"
  ],
  "titleUrls": [
    "https://www.flextv.cc/movie/my-three-brothers-finally-chose-me-KbZ2raxZaP"
  ],
  "maxTitles": 25,
  "includeEpisodes": true,
  "includeComps": false,
  "locale": "en",
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped titles in JSON.

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

All scraped titles as a CSV export.

## `taxonomy` (type: `string`):

Every FlexTV genre and facet tag for the locale, with its group and browse URL.

## `viewInConsole` (type: `string`):

Open the run and its dataset in the Apify Console.

# 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 = {
    "titleUrls": [
        "https://www.flextv.cc/movie/my-three-brothers-finally-chose-me-KbZ2raxZaP"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalbench/flextv-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 = {
    "titleUrls": ["https://www.flextv.cc/movie/my-three-brothers-finally-chose-me-KbZ2raxZaP"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("signalbench/flextv-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 '{
  "titleUrls": [
    "https://www.flextv.cc/movie/my-three-brothers-finally-chose-me-KbZ2raxZaP"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call signalbench/flextv-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,signalbench/flextv-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/q8KGvAx2dxhbvuxzR/builds/mqI1rs3kXKVmWT1kM/openapi.json
