JioHotstar Scraper - Movies, Shows, Sports & Episodes
Pricing
from $1.70 / 1,000 catalog records
JioHotstar Scraper - Movies, Shows, Sports & Episodes
Scrape the JioHotstar catalog by content type or URL. Extract shows, movies, episodes, sports, clips and live content, with one structured record per catalog URL including title, show name, content IDs and source URL.
Pricing
from $1.70 / 1,000 catalog records
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
3
Total users
0
Monthly active users
a day ago
Last modified
Categories
Features
Share
JioHotstar Scraper: Catalog URLs, Titles, Show & Episode IDs
JioHotstar Scraper turns the hotstar.com catalog into a clean dataset of shows, movies, episodes, sport pages, clips, channels, genres and languages. It reads the catalog dataset the platform itself publishes (the sitemap URL lists linked from its own robots.txt), so it needs no login and no session of any kind. Walk a whole catalog type page by page, or paste content URLs one at a time, then export to JSON, CSV or Excel, or pull the results straight into your app through the API. Recurring change tracking (what got added or removed) is built in.
Why This Scraper?
- One record per catalog URL. The row kind, humanized title, parent show title, show id and content id are all read straight from the URL the platform itself publishes for its index, so there is no guessing about what a page might contain.
- Walk the catalog by type. Episodes, shows, movies, sport landings, live pages, clips, channels, genres and languages are all selectable, and the episode surface alone carries hundreds of thousands of rows.
- Anonymous by design. The catalog list is public: no account, no session, no cookie jar. What sits behind a login (playback, descriptions, durations, reviews) is out of scope on purpose, so a run never fails because a session expired.
- Stable, resolvable identity. Every record is a real URL you or your own systems can revisit, join against, or fetch again later.
- Built for change tracking. Incremental mode remembers a previous walk of the same catalog scope: the first run returns everything as new, later runs return only what the platform added or dropped.
- Resume a capped or interrupted walk. Point a new run at a previous run id and it skips everything already collected there, so a very large catalog type does not have to be paid for twice.
- Optional MCP export. Pipe a summary of each record into Notion, Linear, Airtable or Apify without changing what lands in the dataset.
Use Cases
- Streaming catalog monitoring: track what the platform adds or removes, by show, movie or episode, on a recurring schedule.
- App and index builders: power your own search or deep-link feature with a lightweight id and title index instead of maintaining your own catalog crawl.
- Content research and archiving: build a historical record of what was listed, and when a title disappeared from the catalog.
- Catalog QA teams: confirm that a newly published show or episode URL is actually listed in the site's own catalog before announcing it elsewhere.
- Data pipelines and joins: feed stable show and content ids into a downstream system that already tracks the same titles by name.
Data You Get
Sample shape: values are illustrative placeholders, not from a live listing.
| Field | Example |
|---|---|
recordId | "1000200000" (the episode, show or movie's own numeric id) |
recordId (no numeric segment) | "slug:cricket" (sport, genre, language and channel landing pages) |
url | the catalog URL itself, kept exactly as published |
kind | "episode" (also "show", "movie", "sport", "sport_match", "clip", "live_tv", "category", "channel", "genre", "language"). News live /watch pages stay "episode". The CHANNELS, GENRES, LANGUAGES and HOME_PAGE_TRAYS catalog types return "category" rows (see below) |
title (episode row) | "Sample Episode Title" |
title (show or movie row) | "Sample Show Name" (the show's or movie's own title) |
showTitle (episode row) | "Sample Show Name" (the parent show) |
showTitle (show or movie row) | same value as title, no separate parent |
showId (episode row) | "500000" (the parent show's numeric id) |
showId (show or movie row) | same value as contentId, no separate parent |
contentId | "1000200000" (the row's own numeric id, same value as recordId) |
scrapedAt | "2026-01-01T00:00:00Z" |
changeType | "NEW" (also "UPDATED", "UNCHANGED", "REAPPEARED", "EXPIRED"; incremental mode only) |
changedFields | [] (incremental mode only) |
firstSeenAt | "2026-01-01T00:00:00Z" (incremental mode only) |
lastSeenAt | "2026-01-01T00:00:00Z" (incremental mode only) |
changedFields, firstSeenAt and lastSeenAt are populated only when Incremental mode is on. Landing-page URLs with no numeric id segment (for example a sport, genre or language landing) get a slug: record id and carry null for showTitle, showId and contentId.
How to Use
- Pick a mode:
sitemap(walk a catalog type) orurl(paste content links). - In sitemap mode, choose a catalog type; in URL mode, paste one or more catalog URLs.
- Set Max items (and, in sitemap mode, optionally Max maps per run) to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Walk the episode catalog (the default):
{"mode": "sitemap","mapType": "EPISODE","maxItems": 50}
A different catalog type:
{"mode": "sitemap","mapType": "SHOWS","maxItems": 100}
Paste catalog URLs: URL mode with the prefilled list scrapes one example per URL shape (a show, an episode, a movie, a browse category, a sport landing page and a sport match highlights /watch page). Replace the list with your own links to scrape those instead. Supported shapes also include clip, movie, live TV and sport match /watch pages (rows are labelled clip, movie, live_tv, sport_match; these were previously labelled episode) and category pages (category). A sport match page looks like https://www.hotstar.com/in/sports/cricket/match-name/1540000000/video/highlights/watch; live (/video/live/watch), replay (/video/replay/watch) and highlights pages are all read the same way. Live match links disappear once the event ends, so use highlights or replay links for anything you want to keep.
New in this version, category rows: the CHANNELS, GENRES and LANGUAGES catalog types (sitemap mode) now return rows of kind category, with the numeric browse id as recordId (for example the id in a /browse/category/<name>/<id> page); the HOME_PAGE_TRAYS type returns category rows with a slug recordId of the form slug:persona-collection/<name>/<id>. These four catalog types previously returned no rows at all, so rows are labelled category, not channel, genre or language. Clip, movie, live TV and sport match /watch rows are labelled clip, movie, live_tv and sport_match; this changes only the kind value, every other field and a stored row's change status stay as before.
URL mode input:
{"mode": "url","urls": ["https://www.hotstar.com/in/shows/sample-show/500000","https://www.hotstar.com/in/movies/sample-movie/500001"]}
Daily change watch of a mapped catalog surface:
{"mode": "sitemap","mapType": "EPISODE","maxItems": 0,"incrementalMode": true,"emitExpired": true}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/hotstar-com-scraper").call(run_input={"mode": "sitemap", "mapType": "SHOWS", "maxItems": 50})for record in client.dataset(run["defaultDatasetId"]).iterate_items():print(record["kind"], record["title"], record["url"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/hotstar-com-scraper').call({ mode: 'sitemap', mapType: 'SHOWS', maxItems: 50 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
How Max items and Max maps shape a sitemap walk
The episode surface alone holds hundreds of thousands of rows spread across about 10 catalog map files. A run walks map files in order and stops once Max items is reached; set it to 0 to walk every map file for that catalog type. Max maps per run is a separate, optional safety bound on how many map files to download in one run, useful for spreading a very large catalog type across several runs; leave it at 0 to let Max items be the only limit.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted or capped run: paste its run or dataset id and this run skips everything already collected there, so you don't pay twice for the same catalog type. - Incremental mode (
incrementalMode) is for walking the same catalog scope again and again, for example daily. Each record is classifiedNEW,REAPPEAREDorEXPIREDagainst the previous walk (the catalog is static per URL, so a previously seen row never reportsUPDATEDunless its title text itself changed).UNCHANGEDrows are suppressed and not billed unlessemitUnchangedis on;EXPIREDrows are only produced after a run that scanned the whole scope with no cap, and only withemitExpiredon. In sitemap mode, Max items counts every catalog row the run reads, including unchanged ones it does not return, so set it to0for a change watch; a smaller value only ever re-checks the first rows of the catalog type.stateKeynames or shares the stored state; leave it empty and the actor derives one automatically from the mode, catalog type and URLs, so two different walks never mix state.ignoreFieldsForChangeslists fields that must never count as a change (they stay in the output, but never make a row UPDATED or appear inchangedFields);scrapedAtanddetailCarriedForwardare always ignored. Stored baselines are compared against the current ignore list, so editing it never re-flags the whole catalog. Do not reuse onestateKeyacross different catalog types or URL lists withemitExpiredon: each walk would report the other walk's rows as removed.
Send results into your apps (MCP connectors)
Optional: the run pipes a condensed summary per record (title, kind, URL) into a connector you authorize under Apify, Settings, API and Integrations. This is an extra delivery step after the scrape; the Apify dataset is never changed.
- 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 records should be created.
Each connector receives a condensed summary per record, not the full record; the complete record always stays in the Apify dataset. Set maxNotifyListings to cap how many records flow per connector per run. Leave mcpConnectors empty to skip this entirely, which never changes what the dataset returns.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | sitemap | sitemap walks the site's own published catalog lists. url processes pasted catalog URLs. |
mapType | string | EPISODE | Sitemap mode only: which catalog dataset to walk (EPISODE, SHOWS, MOVIE, MOVIE_WATCH_PAGES, SPORT, SPORT_LIVE, SPORT_REPLAY, SPORT_MATCH_HIGHLIGHTS, CLIPS, SHOW_LIVE, NEWS_LIVE, LIVE_TV, CHANNELS, GENRES, LANGUAGES, HOME_PAGE_TRAYS, FIRST_LEVEL_LANDING_PAGES, SECOND_LEVEL_LANDING_PAGES, LANGUAGE_GENRE_MOVIES_PAGES, LANGUAGE_GENRE_SHOWS_PAGES). |
urls | array | one example per URL shape | URL mode only: hotstar.com catalog URLs. Each pasted URL returns one record. |
maxItems | integer | 20 | Stop after collecting this many records across all catalog maps and URLs. 0 = unlimited. |
maxPages | integer | 0 | Safety bound on how many catalog map files (each up to 50,000 rows) to download per run. 0 = no map limit; the run then stops at Max items instead. |
resumeFromRunId | string | (none) | Continue one interrupted or capped run from its previous run id or dataset id. |
incrementalMode | boolean | false | Recurring monitoring of the same catalog walk: later runs return only what changed (NEW, plus EXPIRED and REAPPEARED when Emit expired is on) instead of the full snapshot. |
stateKey | string | (none) | Optional name for the incremental state. Leave empty to get a separate state per mode, catalog type and URL list; set it only to name a campaign or deliberately share state. |
emitUnchanged | boolean | false | Incremental mode only: also return (and bill) rows that have not changed, marked UNCHANGED. |
emitExpired | boolean | false | Incremental mode only: also return (and bill) rows the previous run tracked that are no longer in the mapped catalog. Only after a complete, uncapped scan. |
ignoreFieldsForChanges | array | [] | Incremental mode only: field names that never count as a change and never appear in changedFields. A plain name matches that field at any depth; a dotted path such as a.b targets one nested field. Ignored fields are still returned. Changing the list never causes a one-time wave of UPDATED rows. |
mcpConnectors | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which record pages are created. |
maxNotifyListings | integer | 50 | Cap on records written to each connector per run. Does not affect the dataset. |
proxy | object | India residential | Connection settings. The catalog answers an India connection, so the default pins an India exit. |
Output Example
Sample shape: values are illustrative placeholders, not from a live listing.
Sample output record:
{"recordId": "1000200000","url": "https://www.hotstar.com/in/shows/sample-show/500000/sample-episode-title/1000200000/watch","kind": "episode","title": "Sample Episode Title","showTitle": "Sample Show Name","showId": "500000","contentId": "1000200000","scrapedAt": "2026-01-01T00:00:00Z"}
Plan Requirement
An India residential connection is required. The site answers any non-India or datacenter connection with a generic page instead of the catalog lists, so datacenter proxies do not work. The default pins an India residential exit; Apify free plans do not include residential proxies, so on a free plan either upgrade or supply your own India proxy URLs. Some exits fail at the connection level; the actor retries on a fresh session automatically.
If the selected connection is unavailable the run 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." and later "Served over the backup connection (the default connection was unavailable)." If nothing can be read the run fails with one of: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required", "No results: residential proxies are unavailable on this run ... and the backup connection could not load the requested Hotstar pages", or "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search." Setting the environment variable HOTSTAR_COM_ALTERNATE_CONNECTION=0 disables the backup connection.
FAQ
How much does it cost?
You pay per record returned, plus the platform's standard per-run start event. The Pricing tab shows the current rates. Rows suppressed as unchanged are not returned and not billed. Use Max items to cap the cost of any run.
Is it legal to scrape JioHotstar?
This actor reads only the catalog URL list the platform itself publishes in its sitemap, advertised in its own robots.txt. You are responsible for how you use the results: follow the platform's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution.
Can I get only new or removed titles on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode, with Emit expired records on if you also want to know what was removed. Unchanged rows are not billed.
Why does this actor not return descriptions, durations or ratings?
Those fields sit behind the platform's own session gate (a signup flow this actor does not fake), and the public page shell serves an empty template to automated readers. This actor reads the catalog index only: the row kind, title, show and ids, all taken from the URL itself.
Why did my pasted URL come back empty?
URL mode only understands catalog content pages (shows, movies, episodes, sport, clips, channels, genres, languages). Account pages and the site's search results are not catalog URLs and are reported as unread rather than guessed at.
Why did my run fail instead of returning an empty dataset?
If the site refuses every request, the run stops with a clear message so "nothing in this catalog type" is never confused with "nothing could be read". Run it again in a few minutes. If only some catalog map files could not be read, you still get what was found.
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.
🔗 Want more sports data?
Pair this actor with these related scrapers from the same team:
| ⚽ BookMyShow Scraper Scrape BookMyShow movies and live events by city, category or URL. Extract genres... | ⚽ Whatnot Scraper Scrape Whatnot live and upcoming shows, queued items and lots, and seller profiles... |
| ⚽ Globo Esporte Scraper Scrape public sports content from ge.globo.com, including news, videos, matches and feed... | ⚽ bet365 Odds Scraper Scrape bet365 live in-play and pre-match odds across 20+ sports. Extract fixture... |
| ⚽ FanDuel Odds Scraper Scrape FanDuel sportsbook odds, including moneylines, spreads, totals, player props and... | ⚽ Strava Data Scraper Scrape public Strava data: clubs (members, location, sport, settings), athlete profiles... |
💬 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.