TuneIn Scraper at a glance
TuneIn Scraper is a ready-to-run Apify actor from abotapi for collecting web data. Its documented output includes artwork, name, type, change type (incremental mode) and directory id, which you can export as JSON, CSV or Excel or read through the Apify API. It costs $2.00 per 1,000 results on the Apify Free plan and from $1.20 on paid plans, with smaller per-run or optional feature charges listed on Apify.
- Runs on
- Apify cloud: console, API, schedules and integrations
- Data type
- Web data
- Pricing
- $2.00 per 1,000 results (Free plan), from $1.20 on paid plans
- Output
- Documented below · JSON, CSV, Excel
- Inputs
- 28 options
Scrape TuneIn by keyword, category, genre, location or URL. Extract stations and podcasts with artwork, genre, bitrate, frequency, location, language, contact details, playable stream URLs and episodes. Incremental mode tracks changes over time.
What TuneIn Scraper can do
- Three ways to find data. Search by keyword, browse the directory's own categories, genres and locations, or paste station, podcast and category links. No need to build addresses by hand.
- One flat record for every kind. Stations, podcasts and episodes share the same shape, with a field a kind does not have present and null rather than missing, so the dataset opens cleanly as a table.
- Enrichment you switch on. The full published profile, playable stream addresses and episode lists are each a separate toggle, billed only when you turn them on.
- Worldwide coverage. Every genre, category and region the directory publishes, plus a locale-aware Local radio chart for one place's popular stations.
- Built for schedules. Incremental mode classifies each result NEW, UPDATED, REAPPEARED, UNCHANGED or EXPIRED, and ignores what is playing right now so a live station is not re-billed every run.
- Resume without re-paying. Continue one interrupted run and the actor skips everything it already collected there.
Choose what to collect
These inputs let you configure TuneIn Scraper - Radio Stations, Podcasts & Stream URLs. Open the actor on Apify to enter your values and review the current options.
| Input | What it controls |
|---|---|
modestring · required | Choose 'search' to look results up by keyword, 'browse' to walk the directory's own categories, genres and locations, or 'url' to scrape pasted links and directory ids. |
queriesarray | Only used when mode = search, ignored in the other modes. One or more keywords, for example 'jazz', 'BBC Radio 4', 'true crime' or a call sign such as 'KEXP'. Left empty in search mode, the run searches the example keyword 'jazz' so it never ends empty. |
browseCategoriesarray | Only used when mode = browse, ignored in the other modes. Local radio is the directory's own locale popular chart and is the one category that responds to Locale coordinates below. Selecting several returns results from all of them. Left empty with no ids, a browse run uses Local radio. |
browseIdsarray | Optional, browse mode only. Directory ids for a specific genre, category or region, for example g11 (Jazz), c57943 (Top 40 and Pop) or r101216 (Asia). Every result this actor returns carries its own guideId, and the ids shown in the By location and category listings can be pasted straight back in here. |
localeLatLonstring | Optional, browse mode only, and applied to the Local radio category alone because the other categories are worldwide. Latitude and longitude separated by a comma, for example 51.5074,-0.1278 for London or 40.7128,-74.0060 for New York. Leave empty to use the location of the connection the run goes out on. |
urlsarray | Only used when mode = url, ignored in the other modes. Mix freely: tunein.com station, podcast and embed-player addresses, search pages (with or without a type filter), category and genre pages, the radio and podcast hub pages, and bare directory ids such as s34682, p1008289 or c57943. Left untouched, this field scrapes one example of each address shape. Left empty, the run reads one example station link instead of returning nothing. The editor's suggested list also shows podcast and episode address shapes, a podcast id on its own and a region page. In this mode the full profile is always read, so a pasted link never returns an empty shell. |
itemTypesarray | Optional. Keep only these kinds of result. Leave empty for all three. Stations are live radio, podcasts are shows with an episode archive, and episodes are the individual instalments of a podcast. |
Show 21 more inputs
| Input | What it controls |
|---|---|
minBitrateKbpsinteger | Optional. Keep only stations streaming at or above this bitrate, for example 128 for CD-like quality. Applies to stations only: podcasts and episodes publish no bitrate and are never dropped by this filter. Use 0 for no bitrate requirement. |
maxBitrateKbpsinteger | Optional. Keep only stations streaming at or below this bitrate, for example 64 for low-bandwidth listening. Applies to stations only: podcasts and episodes publish no bitrate and are never dropped by this filter. Combine with the minimum to select a band. Use 0 for no upper limit. |
minReliabilityPercentinteger | Optional. Keep only stations whose uptime score is at or above this percentage. The directory scores every station on how often its stream actually answers, so 90 and above is a working station. Applies to stations only. Use 0 for no reliability requirement. |
streamFormatsarray | Optional. Keep only stations offering at least one of these audio formats, written as the directory writes them, for example mp3 or aac. Applies to stations only. Leave empty for any format. |
genreIdsarray | Optional. Keep only results the directory files under one of these genre ids, for example g11 for Jazz. Every result returns its own genreId, so one run's output tells you which ids to filter on next. An episode listing publishes no genre, so with Fetch full profiles off this filter excludes episodes; switch profiles on and episodes carry their own genre and are filtered on it like everything else. |
fetchDetailsboolean | Read each result's published profile: description, slogan, call sign, dial frequency and band, location and coordinates, language, timezone, the broadcaster's own website, its published contact details, and whether the stream is currently available. One extra request per result. Ignored in URL mode, where the profile is always read because a pasted link has nothing else to read, and is not charged there. |
fetchStreamsboolean | Add each station's playable stream addresses with their bitrate, codec and reliability. One extra request per station. Only stations have a stream, so podcasts and episodes are never asked for one and are never charged for it. |
fetchEpisodesboolean | Add each podcast's published episode list with titles, publication labels, running times and per episode links. One extra request per podcast. Only podcasts have an episode list, so stations and episodes are never asked for one and are never charged for it. |
maxEpisodesPerPodcastinteger | Cap on how many episodes are returned inside each podcast record, newest first. Use 0 for every episode the directory publishes for that podcast. |
maxItemsinteger | Maximum number of results to return across the whole run. This is the run's cap, and the only setting that limits how much the run returns or costs. Use 0 for unlimited. |
maxPagesinteger | Maximum number of listing pages read per browsed category or pasted category link. 0 means no limit, which is the default: the walk then stops only at Max results or at the last page the directory publishes. Does not apply to keyword search, which the directory answers in a single page. |
maxBrowseDepthinteger | How many levels of sub-categories the walk may open when a category page holds only sub-categories and no results of its own, which is how most top level categories are published. 0 keeps to the exact addresses you named and returns nothing for those categories. This is a scope choice, not a cost cap: Max results above is what limits the size of the run. |
resumeFromRunIdstring | Paste a previous run ID or dataset ID to continue a large directory pull without returning or charging for results already collected there. Use this after an interrupted run. For recurring daily monitoring of the same scope, use Incremental mode below instead. |
incrementalModeboolean | Turn this on for daily or recurring monitoring. The first run returns every matching result as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED results. What is playing right now and the rolling reliability score drift constantly, so they never count as a change on their own; a station going off air is reported through isAvailable instead. Turn on Emit unchanged or Emit expired only when you also want those rows returned (and billed). State is kept separately for each scope, filter and lookup setup; use State key to name or deliberately share a monitoring campaign. |
stateKeystring | Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to let the actor derive a key automatically from the mode, scope, filters and lookup settings, so different scopes never mix state with each other. With your own State key, the mode needs a keyword, category, id or link to scan: with none the run stops with an input message and collects nothing. A run that falls back to the example never saves or expires state. |
emitUnchangedboolean | Off by default. Turn on to also return results that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you want the full snapshot every run. |
emitExpiredboolean | Off by default. Turn on to also return results that were listed in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked scope, so never when Max results capped it or when Resume was used. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking. |
ignoreFieldsForChangesarray | Optional. Field names that must NOT make a result count as UPDATED, for example "bitrateKbps" or "streams.url" (dotted paths reach nested fields). Ignored fields stay in the output and the saved baseline; they are only left out of the comparison and of changedFields. What is playing right now and the rolling reliability score are always ignored. Leave empty to compare every other field. |
mcpConnectorsarray | Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write or digest. Leave empty to skip; never changes the dataset output. |
notionParentPageUrlstring | URL or id of the Notion page under which result pages are created. Required to enable the Notion export; ignored by other connectors. |
maxNotifyListingsinteger | Cap on results written to each connector per run. Does not affect the dataset. |
Fields in the output
The documented dataset includes the fields below. Availability can depend on the source page and selected mode.
imageArtwork (image)nameNameitemTypeTypechangeTypeChange type (incremental mode) (text)guideIdDirectory IDsubtitleTaglinegenreNameGenregenreIdGenre IDcallSignCall signfrequencyFrequencybandBandlocationLocationShow 27 more fields
languageLanguagebitrateKbpsBitrate (kbps)reliabilityPercentReliability %streamFormatsFormats (array)isAvailableOn air (boolean)nowPlayingNow playingstreamCountStream URLsepisodeCountEpisodesdurationSecondsDuration (s)websiteUrlWebsite (link)emailContact emailphoneContact phoneurlTuneIn page (link)changedFieldsChanged fields (incremental mode) (array)firstSeenAtFirst seen (incremental mode) (date)lastSeenAtLast seen (incremental mode) (date)descriptionDescriptionsloganSloganmailingAddressMailing addresstwitterIdTwitter IDregionIdRegion IDtimezoneTimezonelatitudeLatitudelongitudeLongitudecontentClassificationContent classstreamsstreamsepisodesepisodesExample output
An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.
{
"itemType": "station",
"guideId": "s900001",
"name": "Riverside Jazz Radio",
"subtitle": "Smooth jazz for your afternoon",
"description": "An illustrative record. Values below are made up, not scraped.",
"slogan": "Jazz, all day, every day",
"image": "https://cdn-profiles.tunein.com/s900001/images/logoq.png",
"url": "https://tunein.com/station/?stationId=900001",
"websiteUrl": "https://www.riverside-jazz-example.org/",
"callSign": "WRVJ-HD3",
"frequency": "87.9",
"band": "FM",
"genreId": "g11",
"genreName": "Jazz",
"location": "Millbrook, US",
"latitude": null,
"longitude": null,
"regionId": "r200099",
"timezone": "Eastern",
"timezoneOffsetMinutes": -300,
"language": "English",
"bitrateKbps": 128,
"reliabilityPercent": 98,
"streamFormats": [
"mp3"
],
"isAvailable": true,
"isMusic": true,
"contentClassification": "music",
"nowPlaying": "Sample Track Title",
"currentArtist": "Sample Artist Name",
"email": "studio@example-radio.org",
"phone": "+1-202-555-0100",
"mailingAddress": "12 Sample Avenue, Millbrook",
"twitterId": "RiversideJazzFM",
"streams": [
{
"url": "https://stream.riverside-jazz-example.org/live_128k",
"bitrateKbps": 128,
"mediaType": "mp3",
"reliabilityPercent": 98,
"isDirect": true,
"position": 0
}
],
"streamCount": 1,
"episodes": [],
"episodeCount": 0,
"sourceMode": "search",
"sourceScope": "jazz",
"scrapedAt": "2026-01-01T00:00:00Z"
}Ways to use this data
- Radio directory and aggregator apps: pull live stations with stream URLs, genre, dial position and location to power your own listings.
- Podcast discovery tools: track podcast catalogs and their episode lists inside one topic, category or search term.
- Local media research: chart the stations available near a place with Local radio and Locale coordinates.
- Directory monitoring: schedule incremental runs to catch new stations and podcasts, or ones going off air, without re-paying for what has not changed.
Before you run
Start with a small input and check the resulting records against your expected fields. Source content and actor options can change; the current Apify listing is the reference for availability and pricing.
Questions about this actor
What data does TuneIn Scraper return?
The documented output for TuneIn Scraper includes artwork, name, type, change type (incremental mode), directory id, tagline, genre and genre id. Availability of each field depends on the source page and the selected mode.
How much does TuneIn Scraper cost?
It costs $2.00 per 1,000 results on the Apify Free plan and from $1.20 on paid plans, with smaller per-run or optional feature charges listed on Apify. The Apify listing shows the current rate for every plan.
Can I get only new or changed stations and podcasts 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 results, and unchanged ones are not billed unless you ask for them.
What counts as a change in incremental mode?
Almost everything, except what is playing right now and the rolling reliability score, which drift on their own and are excluded so a live station is not re-billed every run. A station going off air is still reported, through isAvailable flipping to false.
Why did my run fail instead of returning an empty dataset?
If the directory refuses every request, the run stops with a clear message so "nothing matched" is never confused with "nothing could be read". Run it again in a few minutes.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, schedule it from the Schedules tab, or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
