WEBTOON Scraper - Series, Episodes, Genres & Rankings
Pricing
from $1.00 / 1,000 series or episode records
WEBTOON Scraper - Series, Episodes, Genres & Rankings
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.
Pricing
from $1.00 / 1,000 series or episode records
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
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
maxItemslimit 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
- Pick a mode:
search(keyword, genre, schedule or canvas) orurl(paste WEBTOON links). - For search mode, set a keyword or a genre/weekday/canvas selection; for URL mode, paste one or more catalog or series links.
- 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.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Browse a genre:
{"mode": "search","genre": "drama","language": "en","maxItems": 20}
Search by keyword (genre rows follow the keyword hits, see below):
{"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:
{"mode": "search","genre": "drama","includeEpisodes": true,"maxItems": 6,"maxPages": 1}
Process a known series URL:
{"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:
from apify_client import ApifyClientclient = 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:
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 classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on),REAPPEARED, orEXPIRED(only withemitExpiredon, and only after a run with nomaxItemscap hit, no resume, and no refused or errored scope). Episode rows are the exception: episodes beyondmaxPages, or on an episode page that was refused while expanding a search or catalog-page result, count as gone, so keepemitExpiredoff when you track episodes withincludeEpisodes. 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,thumbnailAltandscrapedAt, so a counter moving alone never readsUPDATED.isCompleted,isMatureandisUnsuitableForChildrenare compared, so a series that completes readsUPDATEDwith that field inchangedFields; a baseline saved before those fields existed is refreshed once without anUPDATEDflag. 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=2or later never producesEXPIREDrows.stateKeynames 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.
- 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 | 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.
{"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:
| 🧩 PineDrama Scraper Scrape Pine Drama series by keyword, category or URL across 17 languages. Extract titles... | 🧩 NetShort Scraper Scrape the NetShort short-drama catalog: series with synopses, like and follow counters... |
| 🧩 ShortMax Scraper Scrape the ShortMax short-drama catalog: titles, descriptions, creators, view and follow... | 🧩 DramaBox Scraper Scrape DramaBox short-drama series by browsing the catalogue or entering a series ID... |
| 🧩 GoodShort Scraper Scrape the GoodShort short-drama catalog: titles with exact views, follows, genres and... | 🧩 MyAnimeList Scraper Scrape MyAnimeList anime and manga by keyword, ranking chart or URL. Extract titles... |
💬 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.