# WEBTOON Scraper - Series, Episodes, Genres & Rankings (`abotapi/webtoons-com-scraper`) Actor

Scrape the WEBTOON catalog into structured data. Extract series, genres, rankings and complete episode lists, including titles, authors, descriptions, thumbnails and other available series and episode details.

- **URL**: https://apify.com/abotapi/webtoons-com-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 series or episode records

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

## WEBTOON Scraper: Series Catalog, Episode Lists and Details

WEBTOON Scraper turns the public WEBTOON catalog into structured data, no account needed. Search by keyword, browse a genre, weekday schedule, ranking chart or canvas tab, or paste WEBTOON links directly, then optionally expand every discovered series into its episode list. Get series metadata (authors, summary, schedule, view and subscribe counts) and episode rows (episode number, publish date, likes, thumbnails) across seven language editions, then export to JSON, CSV or Excel, or pull results straight into your app through the API. Episode reader (viewer) pages are always out of scope: this actor reads catalog and series-list pages only.

### Why This Scraper?

- **Five ways to find data.** Search by keyword, browse a genre, a weekday schedule, a ranking chart or a canvas tab, or paste WEBTOON links directly. No need to build URLs by hand.
- **Detail enrichment on demand.** Turn on series detail reads to pull authors, summary, schedule text and view/subscribe counters that no catalog card carries.
- **Episode expansion is opt-in.** Switch it on to walk every discovered series' episode list and emit one row per episode, or leave it off for lean, series-only runs.
- **One shared cap.** A single `maxItems` limit spans every series and episode row together, so cost stays predictable, and the series row is always emitted before its episodes for readable grouping.
- **Resume and incremental modes.** Continue an interrupted crawl, or track a scope across scheduled runs and return only new, updated and reappeared series.
- **Loud failures, not silent empties.** A refused run fails with a clear message instead of returning a dataset that looks like an empty search.
- **Reader pages are never fetched.** A pasted episode viewer link is mapped to its series through `title_no`, and that series is scraped instead (the viewer page itself is not downloaded). Earlier versions returned an error for a viewer link; it now returns that series' rows, which are billed like any other series row.

### Use Cases

- **Webtoon and manhwa aggregators:** mirror new releases, genre rankings and episode drops across languages into your own catalog or app.
- **Content and licensing scouts:** track rising titles by rank, likes and subscriber counts before they trend, using the ranking and genre modes.
- **Reading-list and notification tools:** monitor a shortlist of series URLs on a schedule and alert on new episodes with incremental mode.
- **Localization and translation teams:** compare titles, genres and publication schedules across the seven catalog language editions.
- **Market and fandom research:** build historical datasets of view counts, subscribe counts and genre popularity for analysis.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `kind` | `"webtoon"` (also `"episode"`) |
| `recordId` | `"en-title-500001"` (episode rows: `"en-ep-500001-42"`) |
| `titleNo` | `500001` |
| `title` | `"Sample Series Title"` |
| `url` | Public WEBTOON page URL |
| `language` | `"en"` |
| `genre` | `"Fantasy"` (series rows only) |
| `webtoonType` | `"originals"` (also `"canvas"`) |
| `sourcePage` | `"genre"` (also `"daily"`, `"ranking"`, `"search"`, `"canvas"`, `"series"`) |
| `author` / `authors` | `"Sample Author"` / `["Sample Writer", "Sample Artist"]` |
| `summary` | `"A short sample summary of the series premise."` |
| `scheduleText` / `pageSchedule` | `"Every Monday"` / `"Monday"` (`pageSchedule` is set on weekday-schedule and series pages) |
| `rank` / `badge` / `trendingDirection` / `trendingChange` | `12` / `"NEW"` (also `"UP"`) / `"up"` / `26` (ranking pages carry rank and chart movement; badge on cards that show one) |
| `isUnsuitableForChildren` / `thumbnailAlt` / `scrapedAt` | `false` / `"Sample Title"` / `"2026-10-29T10:00:00+00:00"` (card flag and poster alt text when present; collection time) |
| `likeCount` / `likeCountRaw` | `245000` / `"245K"` |
| `viewCount` / `subscribeCount` | `12000000` / `340000` (read from the series page: with Fetch series details, or for a pasted series URL) |
| `latestEpisode` | `{ "episodeNo": 42, "title": "Episode 42", "date": "Sep 20, 2026" }` (series rows only) |
| `seriesRecordId` / `seriesTitleNo` / `seriesTitle` | episode rows only, link back to the parent series |
| `episodeNo` / `date` | `42` / `"Sep 20, 2026"` (episode rows only: number and publish date) |
| `likeCountText` / `isRecentlyUpdated` | `"12.3K"` / `true` (episode rows only) |
| `imageUrl` | Series or episode thumbnail URL |
| `referer` | `"https://www.webtoons.com/"`, required as the HTTP `Referer` header when displaying `imageUrl` |
| `changeType` / `changedFields` | Incremental state classification and changed field names (incremental mode only) |
| `firstSeenAt` / `lastSeenAt` | ISO timestamps for the record's tracked lifetime (incremental mode only) |

### How to Use

1. Pick a **mode**: `search` (keyword, genre, schedule or canvas) or `url` (paste WEBTOON links).
2. For search mode, set a keyword or a genre/weekday/canvas selection; for URL mode, paste one or more catalog or series links.
3. Turn on **Fetch series details** and/or **Include episodes** if you need more than the catalog row, and set **Max items** to control run size and cost.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Browse a genre:**

```json
{
  "mode": "search",
  "genre": "drama",
  "language": "en",
  "maxItems": 20
}
```

**Search by keyword (genre rows follow the keyword hits, see below):**

```json
{
  "mode": "search",
  "keyword": "moonlit sample saga",
  "searchType": "all",
  "maxItems": 15
}
```

**Search and expand every discovered series into episode rows. The six-row cap includes the series row and all episode rows together:**

```json
{
  "mode": "search",
  "genre": "drama",
  "includeEpisodes": true,
  "maxItems": 6,
  "maxPages": 1
}
```

**Process a known series URL:**

```json
{
  "mode": "url",
  "urls": ["https://www.webtoons.com/en/fantasy/sample-series/list?title_no=00000000"],
  "includeEpisodes": true,
  "maxItems": 12
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/webtoons-com-scraper").call(run_input={"mode": "search", "genre": "drama", "maxItems": 20})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["kind"], row["title"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/webtoons-com-scraper').call({ mode: 'search', genre: 'drama', maxItems: 20 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### How the shared cap and episode expansion interact

Series rows and episode rows draw from the same `maxItems` budget, and the series row for each discovered title is always emitted before its episodes. A run with `maxItems: 20` can therefore return one series plus nineteen episodes, or up to twenty series-only rows when `includeEpisodes` is off. `maxPages` bounds how many episode-list pages are walked per series, but it never raises `maxItems`. When a series' episode expansion is refused, the actor keeps the catalog row it already had instead of dropping the series entirely.

#### What a search-mode run browses

Search mode reads its scopes in this order: the keyword search (when a keyword is set), the weekday schedule (when a weekday is set), then the genre page. With the `originals` source the genre always has a value (empty falls back to `drama`), so a keyword or weekday run also returns rows from that genre page after the earlier scopes, until `maxItems` is reached. Every row's `sourcePage` names the scope it came from: keep `sourcePage` `"search"` for keyword hits only. With the `canvas` source and no keyword, only the canvas tab is read.

#### Set Max pages per series when you include episodes

Episode lists are read 10 episodes per page. Set `maxPages` to the number of pages you need (for example `5` for the newest 50 episodes). With `maxPages` at `0`, the end of a longer episode list is not always detected, so the run can keep re-reading the last page until `maxItems` or the run timeout stops it; a series with exactly two pages of episodes can also return only its first page.

#### Detail reads back off automatically

When **Fetch series details** is on and **Include episodes** is off, the run turns detail reads off for the rest of that run after 5 failed or refused detail reads in a row, or once more than 60% of detail reads have failed after at least 6 attempts, and ships list-page rows instead. This keeps one blocked batch of detail pages from slowing down the whole run. A failed detail read is never charged; the surcharge applies only to rows whose detail read succeeded.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: point it at a prior run or dataset ID and the actor skips every record already collected there, so you don't pay twice. With **Include episodes** on in search mode (or for pasted genre, schedule, ranking, search or canvas pages), a series whose row was already collected is skipped as a whole, so episodes the interrupted run had not reached are not picked up; paste that series' own URL in URL mode to finish it. It cannot be combined with **Incremental mode** on a run that already has a stored incremental baseline; use one mechanism at a time for a given scope.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same scope. Each record is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED`, or `EXPIRED` (only with `emitExpired` on, and only after a run with no `maxItems` cap hit, no resume, and no refused or errored scope). Episode rows are the exception: episodes beyond `maxPages`, or on an episode page that was refused while expanding a search or catalog-page result, count as gone, so keep `emitExpired` off when you track episodes with `includeEpisodes`. A series or episode is compared on its content fields; these fields are excluded from change detection and still returned: `likeCount`, `likeCountRaw`, `likeCountText`, `viewCount`, `subscribeCount`, `rank`, `badge`, `isRecentlyUpdated`, `imageUrl`, `referer`, `sourcePage`, `trendingDirection`, `trendingChange`, `thumbnailAlt` and `scrapedAt`, so a counter moving alone never reads `UPDATED`. `isCompleted`, `isMature` and `isUnsuitableForChildren` are compared, so a series that completes reads `UPDATED` with that field in `changedFields`; a baseline saved before those fields existed is refreshed once without an `UPDATED` flag. **Ignore fields when detecting changes** (`ignoreFieldsForChanges`) adds more field names to that list; names match at any nesting depth. A pasted URL that starts at `&page=2` or later never produces `EXPIRED` rows. `stateKey` names or shares the stored state. With incremental mode off, output is exactly as before.

#### URL mode: supported URL shapes

| Shape | Example |
|---|---|
| Genre page with sort order | `https://www.webtoons.com/en/genres/drama?sortOrder=MANA`, `https://www.webtoons.com/en/genres/fantasy?sortOrder=UPDATE` |
| Localized genre page | `https://www.webtoons.com/es/genres/drama` |
| Weekday schedule (path) | `https://www.webtoons.com/en/originals/monday` |
| Weekday schedule (query) | `https://www.webtoons.com/en/originals?weekday=monday` (the site ignores the query, so the actor applies the weekday itself) |
| Legacy daily schedule | `https://www.webtoons.com/en/dailySchedule?weekday=MONDAY` (mapped to the weekday page) |
| Ranking | `https://www.webtoons.com/en/ranking/trending` |
| Keyword search | `https://www.webtoons.com/en/search?keyword=sample%20title` |
| Canvas list (all, genre tab, sorted) | `https://www.webtoons.com/en/canvas/list?genreTab=ALL`, `...?genreTab=ROMANCE`, `...?genreTab=ALL&sortOrder=LIKEIT` |
| Series list | `https://www.webtoons.com/en/fantasy/sample-series/list?title_no=00000000` |
| Series list, later page | `https://www.webtoons.com/en/fantasy/sample-series/list?title_no=00000000&page=2` (partial scope: the walk starts at that page, and the same applies to a canvas list `&page=N`; single-page lists such as genre, weekday and ranking pages ignore the number; never produces `EXPIRED` rows) |
| Legacy numeric id | `https://www.webtoons.com/episodeList?titleNo=00000000` |
| Mobile host | `https://m.webtoons.com/en/fantasy/sample-series/list?title_no=00000000` (read from the main site) |
| Episode viewer | `https://www.webtoons.com/en/fantasy/sample-series/ep-1/viewer?title_no=00000000&episode_no=1` (mapped to its series via `title_no`; the viewer page itself is not fetched) |

**Default behavior change:** in `url` mode, if you leave **Page URLs** untouched (or omit `urls` through the API), the actor now scrapes one example of each shape above instead of a single genre page. Set `maxItems` to bound the run; every delivered row is billed as usual. Pass your own `urls` to scrape only those. In `url` mode each row takes its language from the pasted link's own path (`/es/...` gives `language: "es"` and ids that start with `es-`), whatever the `language` field says; a saved incremental baseline from before this change is carried over to the new ids once, without re-reporting those rows as `NEW`.

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

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to 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 | `search` | Search the catalog or process pasted URLs |
| `keyword` | string | empty | Title keyword to search; with the `originals` source the genre page is also read after the keyword hits |
| `searchType` | string | `all` | Keyword index: `all`, `originals`, or `canvas` |
| `language` | string | `en` | Catalog language edition |
| `source` | string | `originals` | Catalog to browse when no keyword is set: `originals` or `canvas` |
| `genre` | string | `drama` | Genre (or canvas tab) to browse, typed as on the site (`Drama`, `Slice of life`); case and spacing are ignored. Empty means `drama` (or `ALL` for a canvas source); in search mode an unknown genre fails the run and lists the valid names (url mode ignores the field) |
| `sortOrder` | string | `MANA` | Genre ordering: `MANA` (popularity), `LIKEIT` (likes), or `UPDATE` (recency) |
| `weekday` | string | empty | Weekday schedule to browse (`Monday` to `Sunday`, abbreviations such as `mon` accepted; in search mode an unknown day fails the run); the genre page is read after it |
| `urls` | array | one example per URL shape | URLs in URL mode (see the table above); left untouched, one example of each shape is scraped |
| `fetchDetails` | boolean | `false` | Read each series page for authors, summary and counters; adds a per-series surcharge |
| `includeEpisodes` | boolean | `false` | Emit one row per episode for every discovered series; shares the `maxItems` cap with series rows |
| `maxItems` | integer | `20` | Total cap across series and episode rows (the only default cap) |
| `maxPages` | integer | `0` | Episode-list pages per series (10 episodes each); `0` means the built-in hard cap of 1000 pages per series (`maxItems`, capped at 1000000, still applies). Set it when you include episodes |
| `resumeFromRunId` | string | empty | Continue an interrupted crawl from a previous run or dataset ID |
| `incrementalMode` | boolean | `false` | Track this scope across runs and stamp `NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED`, `EXPIRED` |
| `stateKey` | string | empty | Custom incremental state key; leave empty to derive one from the full scope |
| `emitUnchanged` | boolean | `false` | Also return (and bill) `UNCHANGED` rows |
| `emitExpired` | boolean | `false` | Also return (and bill) `EXPIRED` rows, only after a complete, uncapped scan |
| `ignoreFieldsForChanges` | array | `[]` | Extra field names that never count as a change in incremental mode (any nesting depth); the built-in list in the incremental paragraph is always excluded; `isCompleted`, `isMature` and `isUnsuitableForChildren` are compared unless you add them |
| `mcpConnectors` | array | empty | Optional: send a summary of each record to apps you authorized under Integrations |
| `notionParentPageUrl` | string | empty | Notion connector only: page under which records are created |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run |
| `proxy` | object | Apify datacenter | Apify Proxy connection; defaults to datacenter exits and accepts overrides |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "kind": "webtoon",
  "recordId": "en-title-500001",
  "titleNo": 500001,
  "title": "Sample Series Title",
  "url": "https://www.webtoons.com/en/fantasy/sample-series-title/list?title_no=500001",
  "language": "en",
  "genre": "Fantasy",
  "webtoonType": "originals",
  "sourcePage": "ranking",
  "rank": 12,
  "badge": "NEW",
  "likeCountRaw": "245K",
  "likeCount": 245000,
  "author": "Sample Author",
  "authors": ["Sample Writer", "Sample Artist"],
  "summary": "A short sample summary of the series premise.",
  "scheduleText": "Every Monday",
  "pageSchedule": null,
  "viewCount": 12000000,
  "subscribeCount": 340000,
  "latestEpisode": {
    "episodeNo": 42,
    "title": "Episode 42",
    "date": "Sep 20, 2026"
  },
  "imageUrl": "https://webtoon-phinf.pstatic.net/sample_thumb.jpg",
  "referer": "https://www.webtoons.com/"
}
```

Image display requirement: send the record's `referer` value as the HTTP `Referer` request header when downloading or displaying `imageUrl`.

Episode rows carry a different shape: `kind: "episode"`, plus `seriesRecordId`, `seriesTitleNo`, `seriesTitle`, `episodeNo`, `date`, `likeCountText`, `likeCount`, `isRecentlyUpdated`, `imageUrl`, `referer` and `language`. In incremental mode, any record can also carry `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`.

#### Output views

The dataset has four views: **Overview** (series and catalog rows), **Episodes** (episode rows), **Series detail** (rows enriched with **Fetch series details**) and **Changes** (the incremental `changeType` and `changedFields` columns). Comments and reader reactions are out of scope and are not collected; the viewer pages are not fetched.

### Plan Requirement

The default proxy setting works out of the box on Apify datacenter exits; this was measured against the live site (genre, ranking, search, canvas, series and episode-list pages all load without a challenge page from a datacenter-class connection). Residential access is not required. If the datacenter connection fails, the run moves to the next connection automatically. If residential access is refused for your plan, it logs: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." Pages served that way are noted with: "Served over the backup connection (the default connection was unavailable)."

If no page can be loaded at all, the run fails with one of three messages instead of returning an empty dataset: the proxy connection was refused for this account (a paid plan is required, or check your own proxy URLs); residential proxies are unavailable and the backup connection could not load the pages; or the requests failed with a connection problem and nothing was collected. Each fetch is bounded to 25 seconds across all connections. A paid Apify plan may be useful for larger runs. Keep `fetchDetails` and `includeEpisodes` off for the lowest-cost, series-only behavior.

### FAQ

#### How much does it cost?

You pay per record returned, plus an optional per-series detail-read surcharge when **Fetch series details** is on. Every start of the actor is billed as an actor-start event per GB of run memory, each delivered record as a dataset item, and each series whose detail read succeeded as one extra `detail-enrichment` event, charged once per series after the row is pushed. Failed, suppressed or unchanged rows are never charged. The **Pricing** tab shows the current rates. Use **Max items** to cap the cost of any run.

#### Is it legal to scrape WEBTOON?

This actor collects only publicly available catalog data: no account, and no reader (viewer) pages, which are not fetched. You are responsible for how you use the data: follow WEBTOON's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Titles, artwork and summaries may be subject to third-party rights.

#### Can I get only new or changed series on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new, updated and reappeared records; unchanged ones are suppressed and not billed unless **Emit UNCHANGED rows** is on.

#### Why did Include episodes return fewer rows than I expected?

Series rows and episode rows share one **Max items** cap, and the series row is emitted first, so a low cap can be used up before every episode is read. Raise **Max items**, or use **Max pages per series** to bound how many episode-list pages each series walks instead.

#### Why did my run fail instead of returning an empty dataset?

If every scope is refused, the run stops with a clear message so "no matches" is never confused with "nothing could be read". Run it again in a few minutes. If only some scopes could be read, you still get what was found and the run succeeds; the run log names each scope that was refused or errored.

#### Why does my keyword search also return genre rows?

With the `originals` source, search mode reads the genre page (default `drama`) after the keyword results, and fills the rest of **Max items** from it. Filter the dataset on `sourcePage` `"search"` to keep only keyword hits, or lower **Max items**.

#### Can I use resume and incremental mode together?

Not on the same scope. `resumeFromRunId` continues one interrupted run without re-billing already-collected records. `incrementalMode` tracks a scope across scheduled runs instead. Combining both on a run that already has a stored incremental baseline is rejected; use one mechanism at a time.

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

### 🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🧩 <a href="https://apify.com/abotapi/pinedrama-scraper"><b>PineDrama Scraper</b></a><br>Scrape Pine Drama series by keyword, category or URL across 17 languages. Extract titles...</td><td>🧩 <a href="https://apify.com/abotapi/netshort-scraper"><b>NetShort Scraper</b></a><br>Scrape the NetShort short-drama catalog: series with synopses, like and follow counters...</td></tr>
<tr><td>🧩 <a href="https://apify.com/abotapi/shortmax-scraper"><b>ShortMax Scraper</b></a><br>Scrape the ShortMax short-drama catalog: titles, descriptions, creators, view and follow...</td><td>🧩 <a href="https://apify.com/abotapi/dramabox-com-scraper"><b>DramaBox Scraper</b></a><br>Scrape DramaBox short-drama series by browsing the catalogue or entering a series ID...</td></tr>
<tr><td>🧩 <a href="https://apify.com/abotapi/goodshort-catalog-scraper"><b>GoodShort Scraper</b></a><br>Scrape the GoodShort short-drama catalog: titles with exact views, follows, genres and...</td><td>🧩 <a href="https://apify.com/abotapi/myanimelist-scraper"><b>MyAnimeList Scraper</b></a><br>Scrape MyAnimeList anime and manga by keyword, ranking chart or URL. Extract titles...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/webtoons-com-scraper/issues/open). 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.

# Actor input Schema

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

How to start the scrape. 'search' queries the catalog (keyword, genre, schedule or canvas index). 'url' processes the WEBTOON URLs you paste: genre, schedule, ranking, search and canvas pages are walked like a search, and a series list page emits the series (plus one row per episode when Include episodes is on). The output and resume sections below apply to both modes.

## `keyword` (type: `string`):

Title keyword to search for (for example tower of god). Leave empty to browse the genre or schedule instead.

## `searchType` (type: `string`):

Which catalog index the keyword searches (all, originals, canvas).

## `language` (type: `string`):

Catalog language edition (en, es, fr, de, id, th, zh-hant).

## `source` (type: `string`):

Catalog to browse when no keyword is set: originals or canvas.

## `genre` (type: `string`):

Catalog genre to browse when no keyword is set. Type it as you read it on the site (Drama, Slice of life, Romance); case and spacing are ignored. In search mode an unknown genre fails the run with the valid list; URL mode ignores this field.

## `sortOrder` (type: `string`):

Genre page ordering: MANA (popularity), LIKEIT (likes), UPDATE (recency).

## `weekday` (type: `string`):

Weekday schedule to browse (Monday to Sunday, abbreviations such as mon accepted). Leave empty to browse the genre instead.

## `urls` (type: `array`):

One or more webtoons.com URLs: genre (any sort order, any language), originals weekday schedule (path or ?weekday=), legacy dailySchedule, ranking, keyword search, canvas list (tab and sort order), series list pages (incl. \&page=N), legacy episodeList?titleNo=, the mobile host (m.) and episode viewer links (mapped to their series). Each shipped example points at a different series or list, so every example returns its own rows. Left untouched in URL mode, one example of each shape is scraped.

## `fetchDetails` (type: `boolean`):

Read each series page for authors, summary and counters. Adds a per-series surcharge.

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

Open every discovered series page in search or URL mode and emit one row per episode. The series row and episode rows share the maxItems total cap.

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

Maximum total rows to return across all series and episode records (values above 1000000 are treated as 1000000). In search mode, series rows are emitted first and episode rows consume the remaining cap. This is the only default cap.

## `maxPages` (type: `integer`):

Maximum episode-list pages per series (0 means the built-in hard cap of 1000 pages per series; larger values are clamped to 1000; maxItems still applies).

## `proxy` (type: `object`):

Apify Proxy connection. Defaults to Apify datacenter proxies, which the catalog serves fine; if they are refused, the run falls back to other connections automatically. Choose a group here only to override.

## `resumeFromRunId` (type: `string`):

Continue an interrupted crawl from a previous run ID or dataset ID. See incrementalMode.

## `incrementalMode` (type: `boolean`):

Track this scope across runs and stamp NEW, UPDATED, UNCHANGED, REAPPEARED. A series or episode is compared on its content fields (title, author, genre, summary, schedule, urls, completed and mature flags, latest episode number and date). These fields are excluded from change detection and still returned: likeCount, likeCountRaw, likeCountText, viewCount, subscribeCount, rank, badge, isRecentlyUpdated, imageUrl, referer, sourcePage, trendingDirection, trendingChange, thumbnailAlt and scrapedAt. A counter moving alone never reads UPDATED. See resumeFromRunId.

## `stateKey` (type: `string`):

Custom incremental state key. Leave empty to derive one from the full scope.

## `emitUnchanged` (type: `boolean`):

Also return UNCHANGED rows (suppressed by default). Billed like any row.

## `emitExpired` (type: `boolean`):

Also return EXPIRED rows for tracked series gone from the scope. Billed like any row.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Extra field names that must NOT count as a change in incremental mode, on top of the built-in list (likeCount, likeCountRaw, likeCountText, viewCount, subscribeCount, rank, badge, isRecentlyUpdated, imageUrl, referer, sourcePage, trendingDirection, trendingChange, thumbnailAlt and scrapedAt). isCompleted, isMature and isUnsuitableForChildren are compared unless you add them here. A row whose only differences are in these fields is UNCHANGED, so it is not returned as UPDATED. Names match at any nesting depth. The fields still appear on rows that are returned.

## `mcpConnectors` (type: `array`):

Connector IDs to forward results to (optional).

## `notionParentPageUrl` (type: `string`):

Parent page for the Notion connector (only used when a Notion connector is selected).

## `maxNotifyListings` (type: `integer`):

Maximum rows forwarded to each selected connector.

## Actor input object example

```json
{
  "mode": "search",
  "searchType": "all",
  "language": "en",
  "source": "originals",
  "sortOrder": "MANA",
  "urls": [
    "https://www.webtoons.com/en/genres/drama?sortOrder=MANA",
    "https://www.webtoons.com/en/genres/fantasy?sortOrder=UPDATE",
    "https://www.webtoons.com/es/genres/drama",
    "https://www.webtoons.com/en/originals/monday",
    "https://www.webtoons.com/en/originals?weekday=tuesday",
    "https://www.webtoons.com/en/dailySchedule?weekday=WEDNESDAY",
    "https://www.webtoons.com/en/ranking/trending",
    "https://www.webtoons.com/en/search?keyword=tower%20of%20god",
    "https://www.webtoons.com/en/canvas/list?genreTab=ALL",
    "https://www.webtoons.com/en/canvas/list?genreTab=ROMANCE",
    "https://www.webtoons.com/en/canvas/list?genreTab=ALL&sortOrder=LIKEIT",
    "https://www.webtoons.com/en/fantasy/tower-of-god/list?title_no=95",
    "https://www.webtoons.com/en/super-hero/unordinary/list?title_no=679&page=2",
    "https://www.webtoons.com/en/canvas/talks-of-life/list?title_no=1134428",
    "https://www.webtoons.com/episodeList?titleNo=1320",
    "https://m.webtoons.com/en/romance/truebeauty/list?title_no=1436"
  ],
  "fetchDetails": false,
  "includeEpisodes": false,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `episodes` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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",
    "searchType": "all",
    "language": "en",
    "source": "originals",
    "sortOrder": "MANA",
    "urls": [
        "https://www.webtoons.com/en/genres/drama?sortOrder=MANA",
        "https://www.webtoons.com/en/genres/fantasy?sortOrder=UPDATE",
        "https://www.webtoons.com/es/genres/drama",
        "https://www.webtoons.com/en/originals/monday",
        "https://www.webtoons.com/en/originals?weekday=tuesday",
        "https://www.webtoons.com/en/dailySchedule?weekday=WEDNESDAY",
        "https://www.webtoons.com/en/ranking/trending",
        "https://www.webtoons.com/en/search?keyword=tower%20of%20god",
        "https://www.webtoons.com/en/canvas/list?genreTab=ALL",
        "https://www.webtoons.com/en/canvas/list?genreTab=ROMANCE",
        "https://www.webtoons.com/en/canvas/list?genreTab=ALL&sortOrder=LIKEIT",
        "https://www.webtoons.com/en/fantasy/tower-of-god/list?title_no=95",
        "https://www.webtoons.com/en/super-hero/unordinary/list?title_no=679&page=2",
        "https://www.webtoons.com/en/canvas/talks-of-life/list?title_no=1134428",
        "https://www.webtoons.com/episodeList?titleNo=1320",
        "https://m.webtoons.com/en/romance/truebeauty/list?title_no=1436"
    ],
    "proxy": {
        "useApifyProxy": true
    },
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/webtoons-com-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",
    "searchType": "all",
    "language": "en",
    "source": "originals",
    "sortOrder": "MANA",
    "urls": [
        "https://www.webtoons.com/en/genres/drama?sortOrder=MANA",
        "https://www.webtoons.com/en/genres/fantasy?sortOrder=UPDATE",
        "https://www.webtoons.com/es/genres/drama",
        "https://www.webtoons.com/en/originals/monday",
        "https://www.webtoons.com/en/originals?weekday=tuesday",
        "https://www.webtoons.com/en/dailySchedule?weekday=WEDNESDAY",
        "https://www.webtoons.com/en/ranking/trending",
        "https://www.webtoons.com/en/search?keyword=tower%20of%20god",
        "https://www.webtoons.com/en/canvas/list?genreTab=ALL",
        "https://www.webtoons.com/en/canvas/list?genreTab=ROMANCE",
        "https://www.webtoons.com/en/canvas/list?genreTab=ALL&sortOrder=LIKEIT",
        "https://www.webtoons.com/en/fantasy/tower-of-god/list?title_no=95",
        "https://www.webtoons.com/en/super-hero/unordinary/list?title_no=679&page=2",
        "https://www.webtoons.com/en/canvas/talks-of-life/list?title_no=1134428",
        "https://www.webtoons.com/episodeList?titleNo=1320",
        "https://m.webtoons.com/en/romance/truebeauty/list?title_no=1436",
    ],
    "proxy": { "useApifyProxy": True },
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/webtoons-com-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",
  "searchType": "all",
  "language": "en",
  "source": "originals",
  "sortOrder": "MANA",
  "urls": [
    "https://www.webtoons.com/en/genres/drama?sortOrder=MANA",
    "https://www.webtoons.com/en/genres/fantasy?sortOrder=UPDATE",
    "https://www.webtoons.com/es/genres/drama",
    "https://www.webtoons.com/en/originals/monday",
    "https://www.webtoons.com/en/originals?weekday=tuesday",
    "https://www.webtoons.com/en/dailySchedule?weekday=WEDNESDAY",
    "https://www.webtoons.com/en/ranking/trending",
    "https://www.webtoons.com/en/search?keyword=tower%20of%20god",
    "https://www.webtoons.com/en/canvas/list?genreTab=ALL",
    "https://www.webtoons.com/en/canvas/list?genreTab=ROMANCE",
    "https://www.webtoons.com/en/canvas/list?genreTab=ALL&sortOrder=LIKEIT",
    "https://www.webtoons.com/en/fantasy/tower-of-god/list?title_no=95",
    "https://www.webtoons.com/en/super-hero/unordinary/list?title_no=679&page=2",
    "https://www.webtoons.com/en/canvas/talks-of-life/list?title_no=1134428",
    "https://www.webtoons.com/episodeList?titleNo=1320",
    "https://m.webtoons.com/en/romance/truebeauty/list?title_no=1436"
  ],
  "proxy": {
    "useApifyProxy": true
  },
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/webtoons-com-scraper --silent --output-dataset

```

## MCP server setup

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