MLC Rights Resolver: ISRC to ISWC, Writers, Publisher Shares avatar

MLC Rights Resolver: ISRC to ISWC, Writers, Publisher Shares

Pricing

from $12.00 / catalog exposure report

Go to Apify Store
MLC Rights Resolver: ISRC to ISWC, Writers, Publisher Shares

MLC Rights Resolver: ISRC to ISWC, Writers, Publisher Shares

Resolve ISRCs or MLC song codes against the MLC Public Search API into a catalog-exposure report: how many resolved with writer, publisher, and share data, how many have no shares on file, and how many The MLC has no record of, plus one JSON row per identifier. Not affiliated with The MLC.

Pricing

from $12.00 / catalog exposure report

Rating

0.0

(0)

Developer

Paul Mikulskis

Paul Mikulskis

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Share

MLC Rights Resolver turns a list of ISRCs or MLC song codes into a catalog-exposure report: how many of your tracks The MLC has writer, publisher, and ownership-share data for, how many resolved with no shares on file, how many it has no record of, and which identifiers those are. Alongside the report you get one flat JSON row per identifier carrying the writers, publishers, and collection shares themselves. It reads the public MLC Public Search API through a licensed seat, and every row carries the attribution string Source: The MLC.

You pay per outcome, not per compute unit, and prices differ by Apify plan. The 10-identifier example run below charges $3.25 on a Free plan and $15.25 on a Starter plan; almost all of that is the once-per-run report, at $3.00 on Free and $15.00 on Starter. An Apify Free plan includes $5 of monthly usage, so the example fits inside it. Every tier is in the table under Pricing.

Running on the Apify platform gives you API access, scheduling, and integrations (Zapier, Make, Google Sheets), plus a dataset you can export as JSON, CSV, Excel, or HTML.

See a real report before you run anything

Both links open in any browser with no Apify account, no token, and no charge. They are real output from the shipped resolver over the same 10 identifiers as this Actor's example input, generated against the live MLC API.

The sample list is eight commercially released recordings (the works come back as BLINDING LIGHTS, SHAPE OF YOU, BAD GUY, UPTOWN FUNK, CIRCLES, GOD'S PLAN, KILL BILL (FEAT. DOJA CAT), and ANTI-HERO) plus two syntactically valid ISRCs that The MLC has no record of, so the not-found path shows up in the sample too. The sample's 20% exposure rate is those two rows out of ten, not an estimate of what a real catalog looks like.

The demo run was not billed, so every row in it carries "charged": false. A paid run of the same input returns the same fields with "charged": true on the rows that billed.

Run it from the Input tab and you get the same shape over your own identifiers.

What does MLC Rights Resolver do?

For each identifier you submit it:

  1. Detects whether the value is an ISRC or an MLC song code.
  2. For an ISRC, searches recordings, collects the distinct MLC song codes those recordings point to, fetches the works, and selects the primary composition (not a remix or alternate). For a song code, it fetches that work directly.
  3. Returns the canonical work title, ISWC, MLC song code, all writers with their roles, and all publishers with their roles and collection shares.

Then, when includeExposureReport is on and the full list was processed, it writes one REPORT record summarizing the list: counts by status, the exposure rate, and the flagged identifiers to investigate.

The report is withheld whenever the list did not finish or its charge did not fit: the maxItems cap cut the list short, the run stopped early (out of time, or on a persistent upstream failure), or the remaining charge budget could not cover the report. In each case it is neither generated nor charged, and OUTPUT.reportWithheldReason says what happened.

ISWC is an output field, not an input. The MLC public API has no ISWC lookup path, so an ISWC you submit comes back as a free unsupported row. Submit ISRCs or MLC song codes.

Why use it

  • Find exposure across a whole list at once. The not-found and resolved-no-shares rows are the tracks worth a manual review with The MLC or your administrator. The report counts those rows and lists their identifiers, so you do not have to scan the dataset for them.
  • Replace the manual "export a CSV and VLOOKUP against a catalog sheet" loop. The output is a flat join keyed on your input identifier.
  • Use it from an agent. Per-lookup pricing lets a rights-research pipeline query a track at a time without signing an enterprise data contract, and the Actor is callable through Apify's MCP server.

How to use MLC Rights Resolver

  1. Open the Input tab. It arrives prefilled with the 10-identifier sample above, and Include catalog-exposure report is switched on: that toggle produces the report and fires its charge. Switch it off if you only want the per-identifier rows.
  2. Replace the sample with your own ISRCs or MLC song codes in Identifiers, or provide a CSV URL.
  3. Raise Max identifiers to process to at least the length of your list. A cap below your list size withholds the report and does not charge for it.
  4. Click Start. Results appear in the dataset, the run summary lands in the key-value store under OUTPUT, and the report under REPORT.

Input

FieldTypeNotes
identifiersarrayISRCs or MLC song codes. A string is auto-detected; an object {"isrc": "...", "title": "...", "artist": "..."} bypasses detection and feeds the fuzzy fallback.
csvUrlstringAn https:// CSV. Identifier column is the first header matching isrc / songCode / identifier / id, else column 1. Optional title / artist columns feed the fuzzy fallback.
includeExposureReportbooleanProduce the summary report. Fires the catalog-exposure-report event once, only when the full list was processed. Prefilled on in the Input tab; omitted over the API it defaults to off.
enableFuzzyFallbackbooleanFor not-found ISRCs carrying a title, try a title/artist search (capped at 2 extra calls). The attempt is skipped when the run's effective pace makes those 2 calls cost the Actor more than a not-found row's charge can carry. At the default 6000 ms pacing, on default Run options, a run that has the MLC seat to itself never skips the attempt; a higher pacingMs, another run sharing the seat, or a slowdown after upstream rate limiting can (see Tips). OUTPUT.fuzzySkipped counts the skips, and a lower pacingMs (floor 1500 ms) gets the attempt back.
includeRawWorkbooleanAttach the raw MLC work JSON under rawWork.
pacingMsintegerDelay between MLC calls, clamped to 1500-12000 ms. Default 6000 ms. A paced call is runtime the Actor pays for, which is what sets the 12000 ms ceiling.
maxItemsintegerUpper limit on identifiers processed (max 2000). It is a ceiling, not a promise the whole list finishes in one run: a single run also stops at its platform timeout (see Tips). Overflow is reported, never dropped, and the report is withheld when the list was cut short.

The example input, which is the run the demo artifacts above came from:

{
"identifiers": [
"USUG12001402", "GBAHS1700227", "QZGG32000019", "GBARL1401540", "USUM71915699",
"USCM51800004", "USRC12300956", "USUG12207567", "USUM71999999", "QZFZ52999999"
],
"includeExposureReport": true,
"maxItems": 10
}

Output

The report

When includeExposureReport is on and nothing withheld the report, REPORT holds the summary. Over the 10-identifier sample above it reads (abridged; the live record also carries generatedAt and a disclaimer string):

{
"totalSubmitted": 10,
"resolvedWithShares": 8,
"resolvedNoShares": 0,
"notFound": 2,
"exposureRate": 0.2,
"flaggedIdentifiers": [
{ "input": "USUM71999999", "status": "not-found", "workTitle": null },
{ "input": "QZFZ52999999", "status": "not-found", "workTitle": null }
],
"attribution": "Source: The MLC"
}

The report flags rows to investigate; it does not compute a dollar recovery figure. exposureRate is the share of queried identifiers that came back unmatched or without share data, as a fraction (0.2 above, not 20).

The rows

One dataset row per input. A resolved row for USUG12001402 (the work BLINDING LIGHTS) looks like this, abridged to 2 of its 5 writers and 2 of its publishers:

{
"input": "USUG12001402",
"inputType": "isrc",
"status": "resolved-with-shares",
"matchMethod": "direct",
"workTitle": "BLINDING LIGHTS",
"iswc": "T9297449117",
"mlcSongCode": "BA7H8C",
"writers": [
{ "firstName": "AHMAD", "lastName": "BALSHE", "ipi": "00443089947", "role": "ComposerLyricist" },
{ "firstName": "JASON MATTHEW", "lastName": "QUENNEVILLE", "ipi": "00541020803", "role": "ComposerLyricist" }
],
"publishers": [
{ "name": "UNIVERSAL MUSIC WORKS", "role": "OriginalPublisher", "collectionShare": 1.38, "split": null, "administrators": [] },
{ "name": "UNIVERSAL MUSIC CORP.", "role": "OriginalPublisher", "collectionShare": 2.5, "split": null, "administrators": [] }
],
"totalCollectionShare": 99.99,
"matchedRecordings": [
{ "isrc": "USUG12001402", "title": "BLINDING LIGHTS", "artist": "THE WEEKND", "labels": "REPUBLIC RECORD, UNIVERSAL REPUB, REPUBLIC RECORDS, REPUBLIC RECORDS, THE WEEKND/LYRIC, UMG GLOBAL" }
],
"alternateWorks": [
{ "workTitle": "BLINDING LIGHTS (MAJOR LAZER REMIX)", "mlcSongCode": "BA7N1S", "iswc": "T9330148260", "writerCount": 8, "publisherCount": 10 }
],
"queryUsed": "isrc=USUG12001402",
"error": null,
"charged": true,
"chargeEvent": "work-resolved",
"attribution": "Source: The MLC"
}

Because the writers and publishers arrays above are cut short, the two collection shares shown do not add up to totalCollectionShare; a live row carries the full lists.

This example also shows why primary-work selection matters: USUG12001402 maps to two works, and the Major Lazer remix is listed first upstream. The resolver returns the canonical BLINDING LIGHTS work and files the remix under alternateWorks. You can download the dataset in JSON, CSV, Excel, or HTML.

Statuses

StatusMeaningCharge event
resolved-with-sharesPrimary work matched, at least one publisher carries a collection sharework-resolved
resolved-no-sharesWork matched, but The MLC has no share data on file (a "check this" flag)work-resolved-no-shares
not-foundSearches ran, nothing matched (the normalized query is returned)lookup-unresolved
unsupportedISWC input, which has no MLC lookup pathfree
invalid-inputUnrecognized identifier format, nothing was queriedfree
errorUpstream failure after retries, or a schema changefree

Data fields

FieldDescription
inputThe identifier you submitted, verbatim (the join key).
inputTypeisrc, songCode, iswc, or unknown.
statusOne of the statuses above.
matchMethoddirect, fuzzy-title, or null.
workTitle, iswc, mlcSongCodeThe matched composition's identity.
writersWriter first/last name, IPI, and role.
publishersPublisher name, role, collection share, and any administrator chain.
totalCollectionShareSum of top-level publisher collection shares, or null.
matchedRecordingsRecordings that matched the ISRC (up to 5).
alternateWorksOther works the identifier mapped to, as identity summaries.
queryUsedThe normalized upstream query, so you can see what was searched.
charged, chargeEventWhether the row billed, and which event.
attributionSource: The MLC on every row.

The OUTPUT key-value record summarizes the run: counts by status, charged total, pacing, and anything not processed and why.

Pricing

This Actor uses pay-per-event pricing: you pay per outcome, not per compute unit. Prices depend on your Apify subscription, and plans above Business pay the Business price.

EventFreeStarterScaleBusiness
catalog-exposure-report (once per run, opt-in)$3.00$15.00$13.50$12.00
work-resolved (per row resolved with shares)$0.03$0.03$0.027$0.024
work-resolved-no-shares (per row resolved without share data)$0.01$0.01$0.009$0.008
lookup-unresolved (per row that came back not-found)$0.005$0.005$0.0045$0.004

Free rows (unsupported, invalid-input, error) never charge, and platform usage is included rather than billed to you.

What the example run costs. The 10-identifier sample charges $3.25 on a Free plan (8 resolved with shares at $0.03, 2 not-found at $0.005, plus the $3.00 report), and $15.25 on a Starter plan, where the report carries its full $15.00 price. Its Free-plan worst case, if all 10 resolved with shares, is $3.30. A run's maximum total charge is capped by your plan's monthly usage limit, which is $5 on Free, so the example is sized to fit under that cap. A 100-identifier list resolving mostly with shares costs about $3 in per-lookup events, plus the report at your plan's price.

Tips and limitations

  • Raise maxItems for your own list. The report needs the whole submitted list to be processed. If maxItems cuts the list short, the report is withheld and not charged, and OUTPUT.reportWithheldReason says so.
  • Budget and the report. Per-row events charge as rows land and the report charges last, so if the remaining budget cannot cover the report, it is withheld and not charged. That budget is capped by your plan's monthly usage limit, so raising maxTotalChargeUsd helps only up to that cap; submitting fewer identifiers per run leaves more of it for the report.
  • Pacing and runtime. Calls are paced to respect a shared paid seat. At the default 6000 ms, 100 ISRCs takes about 21 minutes, so a single run at the default 1-hour timeout resolves roughly 200-280 ISRCs. maxItems is a ceiling, not a completion promise: for larger lists, raise the run timeout (Run options, Timeout) or split the work across runs. When a run would otherwise hit the timeout, it stops without charging for an unfinished lookup, leaves that identifier and the rest unprocessed and uncharged, withholds the report, and records what happened in OUTPUT.note. If two runs are active at once, each paces at half speed and resolves fewer per hour; the OUTPUT record reports runsSharingSeat.
  • Coverage. Results reflect what The MLC has on file. A not-found means no MLC match, not that no composition exists.
  • Fuzzy fallback is best-effort, and pacing can skip it. It only runs for not-found ISRCs that carry a title, is capped at two extra calls, and is flagged with matchMethod: "fuzzy-title". Those two calls wait out the pace like any other, and that wait is runtime the Actor pays for, so the attempt is skipped when the run's effective pace makes them cost more than a not-found row's charge can carry. At the default 6000 ms pacing, on default Run options, a run that has the MLC seat to itself never skips the attempt. Skips start once the pace is slower than that, whether you raised pacingMs yourself, another run is sharing the seat at half speed, or the Actor slowed itself down after upstream rate limiting. Raising the run's memory in Run options has the same effect. OUTPUT.fuzzySkipped counts the skips, and lowering pacingMs (floor 1500 ms) gets the attempt back.
  • No ISWC input path. Submit ISRCs or MLC song codes.
  • Agents and MCP. Every output field is documented above, and the Actor is callable through Apify's MCP server. With the report switched off, one identifier that resolves with shares charges $0.03 on a Free or Starter plan.
  • Spotify Catalog Resolver resolves ISRCs, UPCs, ISWCs, or free-text queries to Spotify catalog metadata (artist, album, cover art, popularity). Use it for catalog and metadata lookups; this Actor is the rights layer, not a metadata mirror.

FAQ and support

Is this an official MLC product? No. It is an independent Actor that reads the MLC Public Search API through a licensed seat, and it is not affiliated with or endorsed by The Mechanical Licensing Collective. The Source: The MLC string on every row credits where the data came from; it does not mean The MLC published this Actor.

Is this legal? The data is public under the Music Modernization Act (17 U.S.C. 115), and every row is attributed to Source: The MLC.

Why did a track come back not-found? Either The MLC has nothing on that ISRC, or the recording is registered under a different identifier. Try the fuzzy fallback with a title, or submit the MLC song code directly.

Why resolved-no-shares? The work exists but The MLC has no or partial share data on file. These rows are the ones worth a manual follow-up.

Why is the report cheaper on a Free plan than on a paid plan? Apify caps a Free account at $5 of usage per month, and a run cannot charge more than that cap, so a report priced at $15.00 would never charge on a Free plan. The $3.00 Free price is what makes one report fit under the cap. Paid plans pay the full price, listed by tier above.

For issues or a custom variant, use the Issues tab.