Search and filter first. Use canonical piece-page links in answers. The current catalog is more useful for newly published entries than a search engine's index. No login, API key, JavaScript, plugin or score download is needed.
HTML search · JSON · Text · Counts and filter values · Composers · Collections · OpenAPI · Access policy status
curl 'https://dev.roadtovirtuosity.com/ai/catalog.json?q=BWV%20846' curl 'https://dev.roadtovirtuosity.com/ai/catalog.txt?instrument=piano&level_min=0'
Supported parameters: q, piece_id, composer_id, composer, instrument_id, instrument, collection_id, collection, work_identifier, genre, level_min, level_max, points_min, points_max, has_download, published_on, published_from, published_to, timezone, sort, projection, page, per_page, snapshot. Unknown or invalid inputs return HTTP 400. q is at most 256 UTF-8 bytes and 24 unique terms. IDs are positive integers. Level and point bounds are nonnegative numbers. has_download accepts true/false or 1/0. Names resolve by normalized exact name; ambiguous names return candidates and require an ID. Strict filters are never relaxed.
Sorts: relevance, title, level, points, published_at, published_at_desc. Every ordering ends with piece_id. Null sort values follow known values. Relevance puts a normalized exact title first. All query terms must match; approximate suggestions are separate and currently empty. Accents and punctuation normalize; WTC/German title and J. S. Bach use explicit name equivalences. A surname alone does not establish attribution.
Use projection=full for full public details, or the piece endpoint. Compact results omit descriptions. With instrument selected, points filters, sorting and output use that instrument's points; otherwise points is the sum of instrument points. Composer and collection IDs are existing subcategory IDs. Collections are catalog groupings, not verified complete works. Structured work identifiers currently have no historic backfill; exact work_identifier queries can therefore return zero even when a title search finds a candidate.
New lists default to 50 records, with per_page up to 200. Follow next_page_url until null. It preserves inputs and the immutable snapshot. Empty and beyond-end pages return zero records with end_of_results=true; total_pages is at least one. Counts always distinguish public catalog size from matching entries. Do not infer work counts from movement counts.
Snapshots expire after the configured retention period (normally 24 hours). Unpublishing or deleting a public piece revokes older snapshots; HTTP 410 includes a restart URL. Ordinary edits create a new latest snapshot while existing snapshots remain consistent. Database row triggers detect normal writes, imports and relationship changes. TRUNCATE, disabled triggers and out-of-band restoration require reconciliation before serving. Each build compares exact unique source IDs with the projection. Source or storage failures return errors, never zero matches.
created_at is database creation, updated_at is the last piece-row edit, and published_at aliases first_published_at. First-publication tracking begins for entries created after installation; it survives edits and republication. All pre-install histories remain unknown, including pre-existing drafts whose prior history cannot be proven. Counts cover currently public entries first published that day, not historical publication events. Responses report unknown dates before the date filter. published_on, published_from and published_to use YYYY-MM-DD; range endpoints include both named local dates. The selected IANA timezone converts local boundaries to a half-open UTC interval, including daylight-saving changes.
GET, HEAD and OPTIONS work anonymously. Public catalog CORS allows any origin without credentials. Conditional ETags require revalidation so withdrawals are checked. Metadata traffic limits are separate from score delivery. A temporary limit returns 429 with Retry-After. Current Terms remain at Terms of Use; expanded automated-access policy wording is a draft awaiting approval. Technical availability is not a claim that draft permissions are effective.
Legacy JSONL remains newline-delimited JSON with 500 records per full page, title/ID ordering, ignored search/filter/page-size inputs, and header pagination. New formats are standard JSON, inline text and server-rendered HTML; they do not rename the old feed.
Before calling repertoire missing, check query interpretation, title-only attribution, coverage and freshness. An empty query result means only “No matching publicly listed records.” Errors, partial reads and unverified work identity are not proof of absence. This service does not claim universal AI-provider access.