REST API · OpenAPI 3.1 · MCP server

Developer API

Turn files, links and text into chords and lyrics, then read and fill your songbook from a script or an assistant.

  • Free tier
  • Same engine as the app
  • Official MCP server
Base URL https://jam-jam.org/api/v1 openapi.json llms.txt

What the API does

Each capability is unlocked by a scope you tick when creating your key.

Conversion

Audio, PDF, images, text or a link to songbook, PDF, MIDI, MusicXML and more.

convert:writeconvert:read

Private songbook

List, read, create, edit and organize your songs and playlists.

carnet:readcarnet:write

Public profiles

Read only: profiles, published songs, followers. No soundtrack.

social:read

Stem separation

A conversion (output=stems) splits vocals from accompaniment; the /stem-tool tool splits into two or four stems. With an API key, both require a Premium account.

stems:write Premium API key

Audio queue

Chord and lyric analysis in the same queue as the app.

audio:writequeue:read

AI music detection

A generation hint for a file, a Spotify link, a direct audio link or an ISRC.

Limits, quotas and privacy

AI-generated music is increasingly common. Jam Jam helps you look for signs of generation: paste a link or upload a file for an indicative result.

100 MB max · 10 min max · Spotify, direct audio link or ISRC. YouTube is not supported. Spotify links may be analysed from a preview only.

One queued or running file per account. Premium accounts have priority in the queue.

Guest: 2 scans/day and 5/month. Free account: 5/day and 50/month. Premium: 30/day and 300/month. Shared across website, app and API; failed scans are not charged. Resets at midnight UTC and on the first of each month UTC.

Audio is sent to our detection provider SH Labs for analysis. It states that audio is not retained or used to train models. If unavailable, a file may be analyzed locally when fallback is available. Our temporary files are deleted after processing, failure or cancellation. Your account keeps the filename or link, date, status and result for 90 days. Guest results remain available for 24 h. Guest limits use a token and a pseudonymized network identifier, without retaining the plain IP address.

This index is neither a probability nor proof. A low index does not guarantee human origin.

Explore the tool
audio:writequeue:read

Supported formats

The full, current list, with exact limits and what this server actually serves, is always at GET /formats. In short:

Input formats

  • Audio and video files
  • PDF: chords, lyrics, sheet music, tab, scanned songbook
  • Images: chords, lyrics, sheet music, tab
  • Plain text, ChordPro, OnSong
  • Links and web search (page-link, web-search)
  • MusicXML, MIDI
  • JamSong, mixed .zip, songbook (pivot)

Output formats

  • Songbook (JSON pivot, always available)
  • PDF: chords, lyrics, sheet music, tab, grid
  • PNG, HTML, plain text, ChordPro
  • chords-lab, chords-csv, key and BPM
  • MusicXML, MIDI (Premium)
  • Synced lyrics: .lrc, .srt
  • Karaoke video (.mp4), backing audio, JamSong

Get started in four steps

Conversion is asynchronous: submit, track, fetch.

  1. Create a key

    Settings › API keys. jj_live_ prefix, shown once, 5 keys per account, revocable.

    Manage my API keys
  2. Submit

    A file, a URL or text, with the output format you want.

    POST /jobs
  3. Track

    Poll while honoring Retry-After, or open a Server-Sent Events stream.

    GET /jobs/{id}GET /jobs/{id}/events
  4. Fetch

    The result stays available for 24 hours, then 410 Gone.

    GET /jobs/{id}/result

Examples

Complete calls, ready to copy as is.

List the formats this server servescurl

curl https://jam-jam.org/api/v1/formats

Convert a file to PDFcurl

curl -X POST https://jam-jam.org/api/v1/jobs \
  -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  -F [email protected] \
  -F inputFormat=pdf-songbook \
  -F output=pdf-songbook

Convert a URL to a songbookcurl

curl -X POST https://jam-jam.org/api/v1/jobs \
  -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/tabs/amazing-grace", "output": "songbook"}'

Follow progress (SSE or polling)curl

# Server-Sent Events
curl -N -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  https://jam-jam.org/api/v1/jobs/jb_123/events

# Polling
curl -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  https://jam-jam.org/api/v1/jobs/jb_123

Download the resultcurl

curl -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  -OJ https://jam-jam.org/api/v1/jobs/jb_123/result

Python (requests)python

import time
import requests

API = "https://jam-jam.org/api/v1"
HEADERS = {"Authorization": "Bearer jj_live_xxxxxxxxxxxxxxxx"}

# 1. Discover formats
formats = requests.get(f"{API}/formats").json()

# 2. Submit a conversion
resp = requests.post(
    f"{API}/jobs",
    headers=HEADERS,
    json={"url": "https://example.com/tabs/amazing-grace", "output": "songbook"},
)
resp.raise_for_status()
job = resp.json()

# 3. Poll until done (or use GET /jobs/{id}/events for SSE)
while job["phase"] not in ("done", "failed", "cancelled"):
    time.sleep(job.get("etaS", 2))
    job = requests.get(f"{API}/jobs/{job['id']}", headers=HEADERS).json()

# 4. Fetch the result
if job["phase"] == "done":
    songbook = requests.get(f"{API}/jobs/{job['id']}/result/songbook", headers=HEADERS).json()
    print(songbook["songs"][0]["title"])
else:
    print("failed:", job.get("problem"))

Analyze a track with AI music detectioncurl

# Availability and remaining quota (no key needed)
curl https://jam-jam.org/api/v1/ai-music/availability

# Analyze a file
curl -X POST https://jam-jam.org/api/v1/ai-music/jobs \
  -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  -F "[email protected]"

# Analyze a Spotify link
curl -X POST https://jam-jam.org/api/v1/ai-music/jobs \
  -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://open.spotify.com/track/2JUDGXTQYGQZrVVUgq40Ke"}'

# History, current analysis, one analysis
curl -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  https://jam-jam.org/api/v1/ai-music/history
curl -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  https://jam-jam.org/api/v1/ai-music/jobs
curl -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  https://jam-jam.org/api/v1/ai-music/jobs/JOB_ID

# Cancel
curl -X DELETE -H "Authorization: Bearer jj_live_xxxxxxxxxxxxxxxx" \
  https://jam-jam.org/api/v1/ai-music/jobs/JOB_ID

Code examples

Ready-to-run examples in curl, JavaScript and Python, open source under the MIT licence:

jam-jam-api-examples

Assistants and MCP

An assistant reads the contract and builds its own calls. The official MCP server gives it 42 ready-made tools.

For AI agents

Read GET /openapi.json first: it documents the recommended call sequence (discover formats, submit, track, fetch), authentication and quotas in an x-agent-guide block meant to be parsed by a model. llms.txt on this site lists each conversion capability as its own entry.

MCP server

Key read from JAM_JAM_API_KEY: only the tools its scopes allow appear. It follows no one, likes nothing and publishes nothing on its own.

Claude DesktopClaude CodeCursor
In detail

An official Model Context Protocol server exposes the API to an assistant as 42 tools prefixed jj_: file and text conversion, reading and organising your private notebook, reading public profiles, stem separation, audio analysis and the job queue, AI music detection and Mastering. It reads your API key from the JAM_JAM_API_KEY environment variable and only shows the tools that the scopes of that key allow. It handles polling, quota errors and file results for you, so an assistant like Claude Desktop, Claude Code or Cursor can convert a file or organise your notebook without you writing any HTTP code. It never follows anyone, likes anything or publishes a song on its own.

Installshell
npx -y @jam-jam/convert-mcp

Package page on npm: npmjs.com/package/@jam-jam/convert-mcp

Configuration (Claude Desktop, Claude Code, Cursor)json
{
  "mcpServers": {
    "jam-jam": {
      "command": "npx",
      "args": ["-y", "@jam-jam/convert-mcp"],
      "env": {
        "JAM_JAM_API_KEY": "jj_live_xxxxxxxxxxxxxxxx"
      }
    }
  }
}

Tools and required scopes 42

Conversion

formats, files, text and job status.

  • jj_list_formatsno scope
  • jj_convert_fileconvert:write
  • jj_convert_textconvert:write
  • jj_get_jobconvert:read
Quota no scope

what the key may do and how much is left.

  • jj_check_usage
Private notebook, reading carnet:read

songs and playlists.

  • jj_list_songs
  • jj_get_song
  • jj_list_playlists
  • jj_get_playlist
Private notebook, writing carnet:write

create, update and delete songs and playlists.

  • jj_create_song
  • jj_update_song
  • jj_delete_song
  • jj_create_playlist
  • jj_update_playlist
  • jj_delete_playlist
Public profiles, reading social:read

profile cards, published songs and playlists, followers and following.

  • jj_get_profile
  • jj_list_profile_songs
  • jj_list_profile_playlists
  • jj_list_followers
  • jj_list_following
Stem separation

capabilities, jobs and files.

  • jj_stems_capabilitiesconvert:read
  • jj_stems_separatestems:write
  • jj_stems_get_jobconvert:read
  • jj_stems_list_jobsconvert:read
  • jj_stems_get_fileconvert:read
  • jj_stems_cancel_jobstems:write
Audio analysis and queue

analyse a file, then list, read and cancel jobs.

  • jj_audio_analyzeaudio:write
  • jj_queue_listqueue:read
  • jj_queue_get_jobqueue:read
  • jj_queue_cancel_jobaudio:write
AI music detection

availability, analysis, history and jobs.

  • jj_ai_music_availabilityqueue:read
  • jj_ai_music_analyzeaudio:write
  • jj_ai_music_currentqueue:read
  • jj_ai_music_get_jobqueue:read
  • jj_ai_music_historyqueue:read
  • jj_ai_music_cancel_jobaudio:write
Mastering

capabilities, presets, render, job status and cancellation.

  • jj_mastering_capabilitiesno scope
  • jj_list_mastering_presetsmastering:read
  • jj_create_mastering_presetmastering:write
  • jj_master_audiomastering:write
  • jj_mastering_get_jobmastering:read
  • jj_mastering_cancel_jobmastering:write

Endpoint reference

Every route served, by family, with its scope and quota. The full contract is authoritative.

Conversion (jobs) 6

Method Path Scope Quota
POST /jobsSubmit a conversion: a file (multipart/form-data), a URL or raw text/ChordPro (application/json). output=stems also needs stems:write and a Premium API key, with its own quota (200/UTC day). convert:write quota: conversion
GET /jobs/{id}Job status (phase, percent, steps). Poll and respect the Retry-After header. convert:read quota: none
DELETE /jobs/{id}Cancel a job and erase its files; refunds the quota only if it was still queued. convert:write quota: none
GET /jobs/{id}/eventsServer-Sent Events alternative to polling: state, done, error and keep-alive ping events. convert:read quota: none
GET /jobs/{id}/resultDownload the main artifact (or a specific one via ?artifact=). Available 24 hours after the job was created. convert:read quota: none
GET /jobs/{id}/result/songbookThe canonical songbook JSON pivot (chords, lyrics, structure), always available once the job is done. convert:read quota: none

Formats 1

Method Path Scope Quota
GET /formatsRegistry of the input/output pairs this server actually serves, with limits (maxBytes, maxPages, maxDurationS). public (no key needed) quota: none

Usage & quotas 1

Method Path Scope Quota
GET /me/usageYour quota balance (conversion, read, write, stems) and the scopes your identity can actually exercise, in one call. convert:read quota: none

Notebook, reading 4

Method Path Scope Quota
GET /me/songsList the songs in your private notebook, paginated by opaque cursor; use updatedSince for incremental export. carnet:read quota: read
GET /me/songs/{songId}One full song from your notebook, chords, lyrics and structure included. carnet:read quota: read
GET /me/playlistsList the playlists in your notebook, flat and multi-membership, same pagination as songs. carnet:read quota: read
GET /me/playlists/{playlistId}One playlist and its songs, in order, in summary form. carnet:read quota: read

Notebook, writing 8

Method Path Scope Quota
POST /me/songsAdd a song to your notebook, the same pivot payload a conversion returns. Set Idempotency-Key to avoid duplicates. carnet:write quota: write
PUT /me/songs/{songId}Replace a song entirely (declarative); creates it under that id if it does not exist yet. carnet:write quota: write
PATCH /me/songs/{songId}Update only the fields you send (key, capo, bpm, playlistIds...); also publishes or unpublishes the song to your profile. carnet:write quota: write
DELETE /me/songs/{songId}Remove a song from your notebook; soft, idempotent, and it leaves every playlist too. carnet:write quota: write
POST /me/playlistsCreate a playlist; songIds is the ordered list of songs it contains, kept exactly as sent. carnet:write quota: write
PUT /me/playlists/{playlistId}Replace a playlist entirely (declarative); songIds is required. carnet:write quota: write
PATCH /me/playlists/{playlistId}Rename, change the cover emoji or fully reorder a playlist's songs. carnet:write quota: write
DELETE /me/playlists/{playlistId}Delete the playlist itself; its songs stay in your notebook and in their other playlists. carnet:write quota: write

Social, reading only 5

Method Path Scope Quota
GET /users/{handle}Public profile header of an account by handle; a private account you do not follow returns a closed card (no bio, no counts). social:read quota: read
GET /users/{handle}/songsSongs an account has published on its profile; audioPlayable is only true when you follow each other mutually. social:read quota: read
GET /users/{handle}/playlistsPlaylists an account has published; songCount only counts what you can actually see. social:read quota: read
GET /me/followersAccounts that follow you; status=accepted (default) or requested. social:read quota: read
GET /me/followingAccounts you follow; status=requested lists the private-account requests still pending. social:read quota: read

Audio queue & stems 4

Method Path Scope Quota
POST /audioQueue automatic chord and lyrics analysis, then alignment, for a song already in your notebook. Same queue as the app; songId must be yours. audio:write quota: none
GET /queueYour account's audio processing queue: active, queued (with rank) and the last 7 days done. queue:read quota: none
GET /queue/{id}One audio queue job, yours only; same indistinct 404 for an unknown or someone else's job. queue:read quota: none
DELETE /queue/{id}Cancel one of your audio queue jobs; refunds only if the analysis had not completed yet. audio:write quota: none

Stem separation 6

Method Path Scope Quota
GET /stem-tool/capabilitiesCheck availability, limits, models and the caller’s quota for stem separation. convert:read quota: none
GET /stem-tool/jobsList the caller’s private stem-separation jobs, with an optional pagination cursor. convert:read quota: none
POST /stem-tool/jobsUpload an audio file and start separating it into two or four stems. API keys require Premium. stems:write quota: stems
GET /stem-tool/jobs/{id}Read the status, progress and available files of one of the caller’s jobs. convert:read quota: none
DELETE /stem-tool/jobs/{id}Cancel one of the caller’s stem-separation jobs. stems:write quota: none
GET /stem-tool/jobs/{id}/files/{name}Get a short-lived signed download URL for an MP3 stem or ZIP archive. convert:read quota: none

With an API key, every one of these routes requires a Premium account; otherwise the response is a 403 (api-premium-required). The caller’s limits are in GET /stem-tool/capabilities.

Mastering 8

Method Path Scope Quota
GET /mastering/capabilitiesLimits, setting ranges, loudness targets, built-in presets and render right. public (no key needed) quota: none
GET /mastering/presetsList your mastering presets. mastering:read quota: none
POST /mastering/presetsCreate a mastering preset (Idempotency-Key required). mastering:write quota: none
DELETE /mastering/presets/{id}Delete one of your mastering presets. mastering:write quota: none
POST /mastering/jobsUpload audio and start a mastering render with a preset or settings. mastering:write quota: mastering (3/week free, unlimited Premium)
GET /mastering/jobs/{id}Get the status of a mastering job. mastering:read quota: none
DELETE /mastering/jobs/{id}Cancel a mastering job. mastering:write quota: none
GET /mastering/jobs/{id}/resultDownload the mastered WAV. mastering:read quota: none

AI music detection 7 + 1 internal

Method Path Scope Quota
GET /ai-music/availabilityTool availability, accepted formats and the remaining quota of the account or visitor. public (no key needed) quota: none
POST /ai-music/jobsSubmit an audio file, a Spotify link, a direct audio link or an ISRC. One unfinished job per account; YouTube is not supported. audio:write quota: AI detection
GET /ai-music/jobsFind your current or most recent analysis. queue:read quota: none
GET /ai-music/jobs/{id}Follow an analysis and read its report. queue:read quota: none
DELETE /ai-music/jobs/{id}Cancel an analysis and delete its temporary files. audio:write quota: none
GET /ai-music/historyPrivate history of the last 90 days, in pages of 30; account required, no audio kept. queue:read quota: none
DELETE /ai-music/history/{id}Delete a scan from your history; the quota of a successful analysis is not refunded. audio:write quota: none
GET /ai-music/source/{token}/{filename}Temporary address reserved for the detection provider, valid for 180 seconds at most: no client calls it. internal quota: none

Health & specification 2

Method Path Scope Quota
GET /healthService status and readiness per engine (OMR, audio, video...); no authentication required. public (no key needed) quota: none
GET /openapi.jsonThis contract, as machine-readable JSON. public (no key needed) quota: none

Full machine-readable contract, always in sync with this table:

https://jam-jam.org/api/v1/openapi.json OpenAPI contract

Quotas and limits

The exact balance is in GET /me/usage and in the X-Quota-* headers of each response.

Conversion

Free account
6 / week
Premium
500 / month
No key, no session
3 / 24 h

Same counter as songbook export in the app.

Reads and writes

per UTC dayFree accountPremium
Reads (songbook, profiles)2,00020,000
Writes (songbook)2005,000

X-Read-Quota-* · X-Write-Quota-*

Stems from a conversion

POST /jobs · output=stems

Premium API key required: without Premium, the response is a 403 (premium-required).

Premium
200 per UTC day

By calling these routes you confirm you hold the rights to the audio you send.

In detail
  • POST /jobs with output=stems returns a .zip: vocals, accompaniment and manifest.json.
  • Required scopes: stems:write and convert:write.
  • A separate counter from conversions, counted per UTC day (not a rolling 24 hours).
  • Not to be confused with the Stem separation tool (/stem-tool/*): an API key there requires a Premium account, with its own limits (GET /stem-tool/capabilities).

Getting an API key

Create a free Jam Jam account, then open Settings, API keys in the app (mobile or web) and create a key. It starts with jj_live_ and is shown once: copy it right away. Up to 5 keys per account, revocable at any time.

Errors

Every error, on every route, is an application/problem+json object (RFC 9457) with a stable code field a client can branch on: quota-exhausted, rate-limited, unsupported-pair, not-found... The full catalogue is in the OpenAPI schema (components.schemas.Problem).

Retention

Uploaded files and generated results are deleted 24 hours after the job is created. Nothing is kept longer, including failed jobs. Fetching a result past that window returns 410 Gone.

Open-source credits

This API stands on real open-source engines. Used honestly, credited by name:

Models and data

Speaker detection (Studio): WeSpeaker ResNet34-LM voice embedding model, WeNet project (github.com/wenet-e2e/wespeaker), trained on VoxCeleb (VGG, University of Oxford), licence CC BY 4.0 (creativecommons.org/licenses/by/4.0/). Modified by Jam Jam: weights converted to half precision.