# TIDAL Scraper - Tracks, Albums, Artists & Playlists (`abotapi/tidal-catalog-scraper`) Actor

Scrape TIDAL tracks, albums, artists, playlists and public mixes by search phrase or URL. Extract structured catalogue metadata with optional track lists, recurring change detection and app exports for ongoing music data workflows.

- **URL**: https://apify.com/abotapi/tidal-catalog-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 6 total users, 3 monthly users, 99.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 catalogue 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

## TIDAL Music Catalogue Scraper

Collect structured TIDAL music catalogue records for research, discovery and catalogue monitoring. Search for tracks, albums, artists or playlists, or provide direct catalogue links. Public artist-radio mixes and Top/New music collections are supported through their links. Each result has a stable identity and a TIDAL URL, with optional track lists and recurring change detection.

### Why This Scraper?

- **Four result types.** Tracks, albums, artists and playlists.
- **Direct links.** Individual catalogue entities and public mixes.
- **Collections.** Top music and New music collection records with their source section.
- **Track lists.** Optional artist top tracks, album tracks and playlist contents.
- **Resume and monitor.** Resume interrupted collections or monitor changes across recurring runs.
- **Exports.** JSON, CSV and Excel dataset exports, plus optional app summaries.

### Use Cases

- **Music catalogue research and data science:** build structured datasets of tracks, albums, artists and playlists for analysis or catalogue mapping.
- **Release monitoring:** track new albums, playlist updates or Top/New music collection changes on a schedule using incremental mode.
- **Music discovery tools:** power search, recommendation or a companion catalogue browser with structured track, album and artist metadata.
- **Rights and metadata verification:** check ISRC, UPC, release dates and copyright lines against your own catalogue records.
- **Playlist and mix tracking:** monitor public playlists or artist-radio mixes for track list changes over time.
- **Content and editorial teams:** source artwork links, descriptions, track lists and artist names for editorial or promotional use.

### Data You Get

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

Fields vary by record type and country. Missing source values are returned as null or empty arrays, never invented. The examples below are fictional.

| Field | Example |
|---|---|
| `id` | `track:00000001` |
| `entityId` | `00000001` |
| `recordType` | `track`, `album`, `artist`, `playlist`, `mix` |
| `title` | `Example Song` |
| `url` | `https://tidal.com/track/00000001` |
| `countryCode` | `US` |
| `artists` | `[{"id":1,"name":"Example Artist"}]` |
| `album` | `{"id":2,"title":"Example Album"}` |
| `duration` | `240` seconds |
| `releaseDate` | `2026-01-01` |
| `explicit` | `false` |
| `popularity` | `50` |
| `audioQuality`, `audioModes` | `LOSSLESS`, `["STEREO"]` |
| `isrc`, `upc` | Catalogue identifiers where available |
| `numberOfTracks` | `12` |
| `description`, `copyright` | Published catalogue text |
| `creator`, `mixes`, `mixType` | Published creator and related mix metadata |
| `tracks`, `tracksTotal`, `tracksComplete` | Included tracks, available total and completeness |
| `sourceSection`, `sourceUrl`, `query` | Collection/search provenance |
| `imageUrl`, `raw` | Artwork URL and original catalogue fields |
| `changeType`, `changedFields` | `UPDATED`, `["title"]` |
| `firstSeenAt`, `lastSeenAt`, `scrapedAt` | Observation timestamps |

### How to Use

#### Search for tracks

**Search by keyword:**

```json
{"mode":"search","queries":["Daft Punk"],"searchType":"tracks","maxItems":3}
```

#### Search for albums in another catalogue region

**Search another region:**

```json
{"mode":"search","queries":["Daft Punk"],"searchType":"albums","countryCode":"AU","maxItems":3,"fetchDetails":true,"maxTracks":5}
```

#### Collect multiple entity links

**Direct links:**

```json
{"mode":"url","urls":["https://tidal.com/artist/8847","https://tidal.com/album/1550545","https://tidal.com/playlist/abd35446-6a67-4f2b-9f92-0636742c63cb"],"maxItems":3}
```

#### Supported link shapes

| Link | Result |
|---|---|
| `tidal.com/album/<id>`, `/artist/<id>`, `/track/<id>` | One record for that entity |
| `tidal.com/playlist/<uuid>` | One playlist record, optional track list |
| `tidal.com/browse/<album, artist, track or playlist>/...` | Same as the link without `/browse/` |
| `tidal.com/search?q=<phrase>&page=<n>` | Catalogue search results (type follows Search result type) |
| `tidal.com/view/pages/explore_top_music`, `explore_new_music` | Top music and New music collections |
| `tidal.com/mix/<id>` | One artist-radio mix record |

The `www.` and `listen.` hosts and tracking parameters are accepted. An unsupported link stops the run with a clear message instead of returning nothing. A URL mode run with the links field untouched scrapes six example links, one per distinct link shape.

#### Public music collections and mixes

**Collections and mixes:**

```json
{"mode":"url","urls":["https://tidal.com/view/pages/explore_top_music","https://tidal.com/mix/0002737adb8d077f4591c4cdb7ba60"],"maxItems":6,"maxTracks":5}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/tidal-catalog-scraper").call(run_input={"mode": "search", "queries": ["Daft Punk"], "searchType": "tracks", "maxItems": 5})
for record in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(record["title"], record["recordType"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/tidal-catalog-scraper').call({ mode: 'search', queries: ['Daft Punk'], searchType: 'tracks', maxItems: 5 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

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

#### Coverage and limits

Search uses TIDAL's relevance ordering. `searchType` also applies to pasted search links such as `https://tidal.com/search?q=Daft%20Punk&page=2`. Artist, album, track and playlist links retain their own record type. Both current links and legacy `/browse/` links are accepted.

Top music and New music return the public catalogue entries exposed in those collections, with `sourceSection` where available. They are not a historical chart archive, and the actor does not assign invented chart ranks. Artist-radio mixes can change between runs. Personalized mixes and private collections are outside coverage.

`fetchDetails` adds complete entity metadata and nested track lists for artists, albums and playlists. For artists, the list is top tracks, not an exhaustive discography. `maxTracks` bounds nested results independently of dataset rows. Mixes already include their available list, and may expose fewer tracks than requested. Inspect `tracksComplete` and `tracksTotal` before treating a nested list as complete.

The default output cap is 20 rows. Zero removes that row limit, subject to the selected page bound and run safety limits. Larger collections can stop with partial results and be continued using Resume. Multiple phrases or links share the record allowance. No audio files, playback data or downloads are included.

#### Resume and recurring updates

`resumeFromRunId` continues one specific collection by skipping IDs in its previous run or dataset. A local dataset can be named as `local:NAME`. Saved progress also protects a resurrected run. Resume does not decide which catalogue records have changed.

`incrementalMode` monitors the same catalogue scope across separate runs. Its first run returns `NEW`; later runs normally return only `NEW`, `UPDATED` or `REAPPEARED`. Unchanged records are suppressed. `changedFields` lists changed top-level fields. Scrape metadata, the raw source copy, query and source labels, and popularity counters never count as changes, and `ignoreFieldsForChanges` adds more (dotted paths such as `tracks.duration` reach nested fields) while they stay in the output. Changing the ignore list never marks stored records as updated. Scans that start from a search link with `page` above 1, or that stop at a result limit, never expire records.

`emitUnchanged` returns and bills additional `UNCHANGED` rows. `emitExpired` returns and bills additional synthetic `EXPIRED` rows only after a complete, nonempty scan. Bounded, failed or resumed scans cannot establish expiry. Both options default to false.

`stateKey` names a campaign; mode, queries, links, country and detail settings remain isolated even with the same name. Avoid overlapping runs for the same scope. Combining Resume with an existing incremental baseline fails early; a first baseline may be bootstrapped with Resume.

### Send results into your apps (MCP connectors)

Authorize a connector under Apify Settings > API & Integrations and select it in `mcpConnectors`. For Notion, set `notionParentPageUrl` to the parent page. `maxNotifyListings` limits the summaries sent to each selected connector.

The export contains a title and a condensed, human-readable summary of key fields. It does not mirror the complete nested JSON. The full record always stays in the Apify dataset. Notion receives one summary page per record; other compatible connectors receive a best-effort record write. A failed export leaves the dataset available, and an empty connector selection skips export.

### Input Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `"search"` | Search by phrases or collect TIDAL links. |
| `queries` | array | `["Daft Punk"]` | Search mode only. Enter up to 50 phrases. Multiple phrases share the result limit. |
| `searchType` | string | `"tracks"` | Result type for phrase searches and pasted search links. Entity links keep their own type. |
| `urls` | array | six example links | URL mode only. Multi-URL supported. Artist, album, track, playlist, mix, search, Top music and New music links are supported. Search phrases are ignored. A search link's page parameter sets the starting page. Left untouched, URL mode scrapes the six example links (album, artist, track, playlist, a legacy /browse/ artist link and an album on the listen host). |
| `countryCode` | string | `"US"` | Two-letter country code such as US or AU. Applies to both modes; availability differs by country. |
| `maxItems` | integer | `20` | Total dataset record limit, including EXPIRED rows. Zero removes this limit; the page cap of 1000 pages per source still applies. Values above 1,000,000 are not accepted. |
| `maxPages` | integer | `0` | Zero uses the built-in hard cap of 1000 pages per source; larger values are clamped to 1000. Set a smaller value for a bounded sample. |
| `fetchDetails` | boolean | `false` | Adds full metadata and artist, album or playlist track lists. Each successfully enriched record saved charges one detail-enrichment unit, which covers up to ten nested tracks; every further ten nested tracks (or part of ten) adds one more unit, so a record with 25 tracks is 3 units and a record with 20 tracks is 2 units. Mix links include their available track list without this surcharge. |
| `maxTracks` | integer | `20` | Maximum tracks inside an enriched artist, album or playlist, or a mix. Does not change dataset row count. tracksComplete states whether the whole available list is included. |
| `resumeFromRunId` | string | `(empty)` | Skip IDs collected in a previous run or dataset. For local datasets use local:NAME. Use Incremental mode for recurring monitoring instead. Combining Resume with an existing incremental baseline is rejected. |
| `incrementalMode` | boolean | `false` | First run emits NEW; later runs emit NEW, UPDATED and REAPPEARED records. A record is UPDATED when any output field changes, except scrape metadata, the raw source copy, query/source labels, popularity counters and fields listed under Ignore fields. Use Resume above to continue one run. Avoid overlapping runs of the same scope. |
| `stateKey` | string | `(empty)` | Optional incremental campaign name. Country, mode, phrases, links and detail settings always remain isolated, even with a matching label. Does not affect Resume. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode only. Field names left out when deciding whether a record changed; they stay in the output. Use dotted paths for nested fields, for example tracks.duration. Popularity counters are always left out of the comparison. Changing this list never marks stored records as updated. |
| `emitUnchanged` | boolean | `false` | Incremental mode only. Also return and bill extra UNCHANGED rows. Off by default; unrelated to Resume. |
| `emitExpired` | boolean | `false` | Incremental mode only. Also return and bill synthetic EXPIRED rows after a complete nonempty scan. Bounded, failed or resumed scans never expire records. |
| `proxy` | object | `(empty)` | Optional connection configuration. |
| `mcpConnectors` | array | `(empty)` | Export summaries via Model Context Protocol (MCP) connectors. Authorize under Apify Settings > API & Integrations, then select a connector. Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com). Leave empty to skip. Complete dataset output is unchanged. |
| `notionParentPageUrl` | string | `(empty)` | Notion page URL or ID for new summary pages. Required for Notion; ignored by other connectors. |
| `maxNotifyListings` | integer | `50` | Maximum summaries per connector. Does not affect the dataset. |

### Output Example

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

This fictional example shows an incremental track record. Non-incremental runs omit change-tracking fields. Enriched rows also include `enrichmentStatus`.

```json
{
  "id": "track:00000001",
  "entityId": "00000001",
  "recordType": "track",
  "title": "Example Song",
  "url": "https://tidal.com/track/00000001",
  "countryCode": "US",
  "artists": [
    {
      "id": 1,
      "name": "Example Artist"
    }
  ],
  "album": {
    "id": 2,
    "title": "Example Album"
  },
  "duration": 240,
  "releaseDate": null,
  "explicit": false,
  "popularity": 50,
  "numberOfTracks": null,
  "audioQuality": "LOSSLESS",
  "audioModes": [
    "STEREO"
  ],
  "isrc": "XXAAA0000001",
  "upc": null,
  "copyright": "Example rights holder",
  "description": null,
  "imageUrl": null,
  "creator": null,
  "mixes": null,
  "mixType": null,
  "trackNumber": 1,
  "volumeNumber": 1,
  "sourceUrl": null,
  "query": "Example Artist",
  "sourceSection": null,
  "tracks": [],
  "tracksTotal": null,
  "tracksComplete": null,
  "raw": {
    "id": 1,
    "title": "Example Song",
    "duration": 240
  },
  "scrapedAt": "2026-01-01T00:00:00+00:00",
  "changeType": "NEW",
  "changedFields": [],
  "firstSeenAt": "2026-01-01T00:00:00+00:00",
  "lastSeenAt": "2026-01-01T00:00:00+00:00"
}
```

### Plan Requirement

An Apify account is required to run this actor on the platform. Review the displayed actor charges before starting a run.

Requests go to TIDAL's catalogue API through an Apify proxy. The actor works on the standard Apify datacenter connection (measured on this site), which it uses first and keeps for the whole run. It moves a request to a backup connection only when the default connection fails or is refused, and the next request starts on the default connection again. Residential proxies are never used unless you select them yourself. If a run says `The selected connection is unavailable to this run`, your plan has no residential proxy: the run continues on a backup connection, and the log reads `Served over the backup connection (the default connection was unavailable).` If nothing can be loaded, the run fails with one of these messages: `The proxy connection was refused for this account, so no page could be read`, `No results: residential proxies are unavailable on this run`, or `Could not load any results page (the requests failed), so nothing was collected`. Supplying your own proxy URLs is also supported.

Dataset views: Catalogue records, Tracks, Albums, Artists, Playlists and mixes, Changes (incremental) and Nested tracks (fetchDetails).

### FAQ

#### How much does it cost?

You pay per record returned. When catalogue details are turned on, each enriched record charges one detail-enrichment unit covering up to ten nested tracks, and every further ten nested tracks (or part of ten) adds one more unit, so a record with 25 tracks is 3 units. The **Pricing** tab shows the current rates. Use **Maximum records** to cap the cost of any run.

#### Is it legal to scrape TIDAL?

This actor collects only publicly available catalogue metadata: titles, artists, identifiers and similar published fields, never playback, downloads or listener account data. You are responsible for how you use it: follow TIDAL's terms and the laws that apply to you, and get legal advice before commercial redistribution.

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

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

#### Should I use Resume or Incremental mode?

Use **Resume** to continue one interrupted run without paying twice for what it already collected. Use **Incremental mode** to monitor the same catalogue scope across separate scheduled runs. A first incremental baseline can be bootstrapped with Resume, but Resume cannot be combined with an existing incremental baseline.

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

If the catalogue refuses every request, the run stops with a clear message so "no results" is never confused with "nothing could be read". Run it again in a few minutes. If only some phrases or links could be read, the records that were found are still saved.

#### 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 summaries 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/deezer-com-scraper"><b>Deezer Scraper</b></a><br>Scrape Deezer by keyword or URL. Extract tracks, albums, artists, playlists, lyrics...</td><td>🧩 <a href="https://apify.com/abotapi/anghami-catalog-scraper"><b>Anghami Scraper</b></a><br>Scrape Anghami's public catalog by keyword or player URL. Rows carry their kind (song...</td></tr>
<tr><td>🤖 <a href="https://apify.com/abotapi/suno-music-scraper"><b>Suno Scraper</b></a><br>Collect public Suno music data: songs with lyrics, style tags, model version, play and...</td><td>🤖 <a href="https://apify.com/abotapi/udio-com-scraper"><b>Udio Scraper</b></a><br>Collect public Udio tracks with the complete generation prompt, full lyrics, style tags...</td></tr>
<tr><td>🛒 <a href="https://apify.com/abotapi/discogs-scraper"><b>Discogs Scraper</b></a><br>Scrape the Discogs catalogue by keyword, filter or link: releases, masters, artists and...</td><td>🧩 <a href="https://apify.com/abotapi/ticketmaster-event-discovery"><b>Ticketmaster Scraper</b></a><br>Discover and scrape Ticketmaster events across the US, UK, Australia and Canada. Search...</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/tidal-catalog-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`):

Search by phrases or collect TIDAL links.

## `queries` (type: `array`):

Search mode only. Enter up to 50 phrases. Multiple phrases share the result limit.

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

URL mode only. Multi-URL supported. Artist, album, track, playlist, mix, search, Top music and New music links are supported. Search phrases are ignored. A search link's page parameter sets the starting page. Left untouched, URL mode scrapes the six example links (album, artist, track, playlist, a legacy /browse/ artist link and an album on the listen host).

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

Result type for phrase searches and pasted search links. Entity links keep their own type.

## `countryCode` (type: `string`):

Two-letter country code such as US or AU. Applies to both modes; availability differs by country.

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

Total dataset record limit, including EXPIRED rows. Zero removes this limit; the page cap of 1000 pages per source still applies. Values above 1,000,000 are not accepted.

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

Zero uses the built-in hard cap of 1000 pages per source. Set a smaller value for a bounded sample; larger values are clamped to 1000. The record limit is the primary cap.

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

Adds full metadata and artist, album or playlist track lists. Successfully enriched records saved add a detail-enrichment surcharge. Mix links include their available track list without this surcharge.

## `maxTracks` (type: `integer`):

Maximum tracks inside an enriched artist, album or playlist, or a mix. Does not change dataset row count. tracksComplete states whether the whole available list is included.

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

Optional connection configuration.

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

Skip IDs collected in a previous run or dataset. For local datasets use local:NAME. Use Incremental mode for recurring monitoring instead. Combining Resume with an existing incremental baseline is rejected.

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

First run emits NEW; later runs emit NEW, UPDATED and REAPPEARED records. A record is UPDATED when any output field changes, except scrape metadata, the raw source copy, query/source labels, popularity counters and fields listed under Ignore fields. Use Resume above to continue one run. Avoid overlapping runs of the same scope.

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

Optional incremental campaign name. Country, mode, phrases, links and detail settings always remain isolated, even with a matching label. Does not affect Resume.

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

Incremental mode only. Also return and bill extra UNCHANGED rows. Off by default; unrelated to Resume.

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

Incremental mode only. Also return and bill synthetic EXPIRED rows after a complete nonempty scan. Bounded, failed or resumed scans never expire records.

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

Incremental mode only. Field names left out when deciding whether a record changed; they stay in the output. Use dotted paths for nested fields, for example tracks.duration. Popularity counters are always left out of the comparison. Changing this list never marks stored records as updated.

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

Export summaries via Model Context Protocol (MCP) connectors. Authorize under Apify Settings > API & Integrations, then select a connector. Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com). Leave empty to skip. Complete dataset output is unchanged.

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

Notion page URL or ID for new summary pages. Required for Notion; ignored by other connectors.

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

Maximum summaries per connector. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "Daft Punk"
  ],
  "urls": [
    "https://tidal.com/album/1550545",
    "https://tidal.com/artist/8847",
    "https://tidal.com/track/1550546",
    "https://tidal.com/playlist/36ea71a8-445e-41a4-82ab-6628c581535d",
    "https://tidal.com/browse/artist/3566",
    "https://listen.tidal.com/album/77646169"
  ],
  "searchType": "tracks",
  "countryCode": "US",
  "maxItems": 20,
  "maxPages": 0,
  "fetchDetails": false,
  "maxTracks": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

## `tracks` (type: `string`):

No description

## `albums` (type: `string`):

No description

## `artists` (type: `string`):

No description

## `playlists` (type: `string`):

No description

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

No description

## `trackList` (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",
    "queries": [
        "Daft Punk"
    ],
    "urls": [
        "https://tidal.com/album/1550545",
        "https://tidal.com/artist/8847",
        "https://tidal.com/track/1550546",
        "https://tidal.com/playlist/36ea71a8-445e-41a4-82ab-6628c581535d",
        "https://tidal.com/browse/artist/3566",
        "https://listen.tidal.com/album/77646169"
    ],
    "searchType": "tracks",
    "countryCode": "US",
    "maxItems": 20,
    "maxPages": 0,
    "fetchDetails": false,
    "maxTracks": 20,
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": [],
    "maxNotifyListings": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/tidal-catalog-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",
    "queries": ["Daft Punk"],
    "urls": [
        "https://tidal.com/album/1550545",
        "https://tidal.com/artist/8847",
        "https://tidal.com/track/1550546",
        "https://tidal.com/playlist/36ea71a8-445e-41a4-82ab-6628c581535d",
        "https://tidal.com/browse/artist/3566",
        "https://listen.tidal.com/album/77646169",
    ],
    "searchType": "tracks",
    "countryCode": "US",
    "maxItems": 20,
    "maxPages": 0,
    "fetchDetails": False,
    "maxTracks": 20,
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
    "maxNotifyListings": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/tidal-catalog-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",
  "queries": [
    "Daft Punk"
  ],
  "urls": [
    "https://tidal.com/album/1550545",
    "https://tidal.com/artist/8847",
    "https://tidal.com/track/1550546",
    "https://tidal.com/playlist/36ea71a8-445e-41a4-82ab-6628c581535d",
    "https://tidal.com/browse/artist/3566",
    "https://listen.tidal.com/album/77646169"
  ],
  "searchType": "tracks",
  "countryCode": "US",
  "maxItems": 20,
  "maxPages": 0,
  "fetchDetails": false,
  "maxTracks": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}' |
apify call abotapi/tidal-catalog-scraper --silent --output-dataset

```

## MCP server setup

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