Tunearo Public API
Distribute music programmatically. Create releases, upload artwork and audio, and submit directly to 35+ streaming platforms — all via REST.
Overview
The Tunearo API lets you build distribution workflows directly into your own platform. Every request is scoped to the authenticated user — you can only read and modify your own releases.
All requests and responses use JSON. CORS is open (*) so the API can be called from browsers and native apps.
Authentication
Every request must include an Authorization header with a bearer token:
Authorization: Bearer tn_live_xxxxxxxxxxxxxxxxxxxx
Getting an API key
- Visit tunearo.com/whitelabel and select your desired plan based on track usage
- Click Get Started
- Our team will send over terms and a payment link within 24 hours
- After payment, your API key will be issued within 12 hours
Release Flow
A typical release goes through these steps in order:
rejection_reason field explains why. Fix the issues, PATCH the release, and resubmit.
Artists
Artists are reusable profiles linked to releases. Create an artist once and reference them across multiple releases.
Returns all artists scoped to your API key.
Response 200
{
"artists": [
{
"id": "1809afb0-a85f-4636-9124-81d1efd9e340",
"artist_name": "Aria Vox",
"biography": "Electronic producer based in London.",
"spotify_id": null,
"apple_id": null,
"created_at": "2026-06-25T18:04:41.180609+00:00"
}
]
}
Request body
| Field | Type | Required | Description |
|---|---|---|---|
artist_name | string | Required | Display name of the artist |
biography | string | Optional | Short artist bio |
spotify_id | string | Optional | Spotify artist ID |
apple_id | string | Optional | Apple Music artist ID |
profile_picture_url | string | Optional | Storage path to profile image |
Example
curl -X POST "https://dashboard.tunearo.com/api/public/v1/artists" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"artist_name": "Aria Vox",
"biography": "Electronic producer based in London."
}'
Response 201
{
"artist": {
"id": "1809afb0-a85f-4636-9124-81d1efd9e340",
"artist_name": "Aria Vox",
"biography": "Electronic producer based in London.",
"created_at": "2026-06-25T18:04:41.180609+00:00"
}
}
Returns a single artist by ID. Returns 404 if the artist doesn't exist or belongs to another user.
Response 200
{ "artist": { "id": "...", "artist_name": "Aria Vox", ... } }
Update any artist field. All fields are optional — only pass what you want to change.
Request body
| Field | Type | Description |
|---|---|---|
artist_name | string | Display name |
biography | string | Artist bio |
spotify_id | string | Spotify artist ID |
apple_id | string | Apple Music artist ID |
instagram_id | string | Instagram handle |
soundcloud_id | string | SoundCloud ID |
audiomack_id | string | Audiomack ID |
youtube_channel_id | string | YouTube channel ID |
profile_picture_url | string | Storage path to profile image |
Example
curl -X PATCH ".../artists/1809afb0-..." \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "spotify_id": "4Z8W4fkeB5G5Gg", "biography": "Updated bio." }'
Response 200
{ "artist": { "id": "...", "artist_name": "Aria Vox", "spotify_id": "4Z8W4fkeB5G5Gg", ... } }
Permanently deletes an artist. Returns 404 if not found or not owned by the caller.
Response 200
{ "success": true }
YouTube Official Artist Channel (OAC) Requests 🆕
Submit and track requests for YouTube Official Artist Channel linking. An OAC merges an artist's topic channel with their official channel on YouTube, unlocking the music shelf and verified badge.
Submits a YouTube Official Artist Channel request for the given artist. The artist must have at least 2 live releases and no existing pending or approved OAC request.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
youtube_topic_channel_url | string | Required | The artist's YouTube auto-generated topic channel URL |
youtube_main_channel_url | string | Required | The artist's official YouTube channel URL |
GET /artists or from the artists array returned on any release response.
Example
curl -X POST ".../artists/68d69cf0-.../oac-request" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"youtube_topic_channel_url": "https://www.youtube.com/channel/UC_topic_id",
"youtube_main_channel_url": "https://www.youtube.com/channel/UC_main_id"
}'
Response 201
{
"oac_request": {
"id": "ffbe5e36-fbd5-4e03-a6d2-71860149f84d",
"artist_id": "68d69cf0-f0d9-4b40-9787-9681eeae517a",
"youtube_topic_channel_url": "https://www.youtube.com/channel/UC_topic_id",
"youtube_main_channel_url": "https://www.youtube.com/channel/UC_main_id",
"status": "pending",
"created_at": "2026-07-30T12:17:55.04196+00:00"
}
}
Errors
| Status | Error | Meaning |
|---|---|---|
| 404 | Artist not found | Artist doesn't exist or belongs to another user |
| 409 | An OAC request already exists for this artist | A pending or approved request already exists |
| 422 | Artist must have at least 2 live releases before submitting an OAC request | Not enough live releases |
Returns the current OAC request for the given artist. Poll this endpoint to check if the request has been approved or rejected by the Tunearo team.
Example
curl ".../artists/68d69cf0-.../oac-request" \
-H "Authorization: Bearer tn_live_xxx"
Response 200
{
"oac_request": {
"id": "ffbe5e36-fbd5-4e03-a6d2-71860149f84d",
"artist_id": "68d69cf0-f0d9-4b40-9787-9681eeae517a",
"youtube_topic_channel_url": "https://www.youtube.com/channel/UC_topic_id",
"youtube_main_channel_url": "https://www.youtube.com/channel/UC_main_id",
"status": "pending",
"created_at": "2026-07-30T12:17:55.04196+00:00",
"updated_at": "2026-07-30T12:17:55.04196+00:00"
}
}
Returns 404 if no OAC request exists for this artist.
Releases
Query parameters
| Param | Description |
|---|---|
status | Filter by status: draft pending approved rejected live takedown_requested taken_down |
limit | Number of results (default 50) |
offset | Pagination offset |
Response 200
{
"data": [ ... ],
"count": 11,
"limit": 50,
"offset": 0
}
Request body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Required | Release title |
primary_artist_name | string | Required | Artist display name (used as fallback string) |
artist_ids | array | Optional | Array of Tunearo artist UUIDs. First entry becomes main_primary_artist_id. Supports multiple primary artists. |
release_type | string | Required | Single, Album, or EP (case-sensitive) |
primary_genre | string | Required | Primary genre |
release_date | string | Required | ISO date: 2026-09-01 |
label_name | string | Required | Label or Self-released |
language | string | Optional | Metadata language e.g. English |
explicit_content | boolean | Optional | Default false |
secondary_genre | string | Optional | Secondary genre |
copyright_year | integer | Optional | Copyright year e.g. 2026 |
copyright_holder | string | Optional | Copyright holder name |
licensed_territories_include | string | Optional | Pipe-separated ISO 3166-1 alpha-2 country codes e.g. GB|US|DE, or WORLD (default) |
licensed_territories_exclude | string | Optional | Pipe-separated country codes to exclude e.g. CN|RU |
release_type is case-sensitive. Use Single, Album, or EP — not lowercase.
Multiple primary artists
Pass an array of Tunearo artist UUIDs in artist_ids. The first entry is set as the main primary artist. The full artist objects are returned in the response under artists and main_artist.
Example — single artist
curl -X POST "https://dashboard.tunearo.com/api/public/v1/releases" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Midnight Drive",
"primary_artist_name": "Aria Vox",
"artist_ids": ["1809afb0-a85f-4636-9124-81d1efd9e340"],
"release_type": "Single",
"primary_genre": "Electronic",
"language": "English",
"release_date": "2026-09-01",
"label_name": "Self-released",
"explicit_content": false
}'
Example — multiple primary artists
curl -X POST "https://dashboard.tunearo.com/api/public/v1/releases" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Midnight Drive",
"primary_artist_name": "Aria Vox & DJ Nova",
"artist_ids": [
"1809afb0-a85f-4636-9124-81d1efd9e340",
"d103206d-977d-402e-bb4d-4238f644be75"
],
"release_type": "Single",
"primary_genre": "Electronic",
"language": "English",
"release_date": "2026-09-01",
"label_name": "Self-released",
"licensed_territories_include": "GB|US|DE|FR",
"licensed_territories_exclude": "CN",
"explicit_content": false
}'
Response 201
{
"release": {
"id": "ee5dde1d-7106-4293-8e91-65b020820d2f",
"title": "Midnight Drive",
"artist_name": "Aria Vox & DJ Nova",
"status": "draft",
"release_type": "Single",
"release_date": "2026-09-01",
"main_primary_artist_id": "1809afb0-...",
"key_artist_ids": ["1809afb0-...", "d103206d-..."],
"licensed_territories_include": "GB|US|DE|FR",
"licensed_territories_exclude": "CN",
"artists": [
{ "id": "1809afb0-...", "artist_name": "Aria Vox", ... },
{ "id": "d103206d-...", "artist_name": "DJ Nova", ... }
],
"main_artist": { "id": "1809afb0-...", "artist_name": "Aria Vox", ... },
"created_at": "2026-07-23T13:59:54.770675+00:00"
}
}
Returns the full release object including all metadata fields.
Response 200
{ "release": { ... } }
Update any release field. Only allowed when status is draft or rejected. Attempting to edit a pending or live release returns 409.
Example
curl -X PATCH "https://dashboard.tunearo.com/api/public/v1/releases/ee5dde1d-..." \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "title": "Midnight Drive (Remix)", "primary_genre": "Dance" }'
Lightweight endpoint for polling. Returns status, rejection reason, and UPC once the release is live.
Response 200
{
"id": "ee5dde1d-7106-4293-8e91-65b020820d2f",
"status": "rejected",
"rejection_reason": "Artwork does not meet requirements.",
"upc": null,
"updated_at": "2026-06-25T18:16:37.564131+00:00"
}
Moves a release from draft or rejected → pending. The Tunearo team reviews and either approves or rejects with a reason.
Response 200
{
"release": {
"id": "ee5dde1d-7106-4293-8e91-65b020820d2f",
"status": "pending",
"updated_at": "2026-06-25T18:13:22.316584+00:00"
}
}
Requests a takedown of a live release. Pass a reason for the request.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Required | Reason for takedown request |
Example
curl -X POST "https://dashboard.tunearo.com/api/public/v1/releases/ee5dde1d-.../takedown" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "reason": "Replacing master recording" }'
Update metadata on a release that is already live. On success, the release status is automatically reset to pending for re-review by the Tunearo team before changes are pushed to DSPs.
Use PATCH /releases/{id} for editing draft or rejected releases. This endpoint is exclusively for post-live metadata updates.
409 if the release is not in live status. Also fires a release.submitted webhook event.
Example
curl -X PATCH ".../releases/7b2fea30-.../metadata" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "primary_genre": "Electronic", "label_name": "Tunearo Records" }'
Response 200
{
"release": {
"id": "7b2fea30-...",
"status": "pending",
"primary_genre": "Electronic",
"label_name": "Tunearo Records",
"updated_at": "2026-07-23T08:51:59.67057+00:00",
...
}
}
Permanently deletes a release. Only permitted when status is draft. Returns 409 if the release is in any other status.
Response 200
{ "success": true }
Tracks
Tracks belong to a release and represent individual audio files. Your plan includes a set number of tracks across your entire account — not per release.
POST /releases/{id}/tracks returns 403 Track limit reached (n). Contact support@tunearo.com to upgrade your plan.
Returns all tracks attached to the release, ordered by position.
Response 200
{ "tracks": [ ... ] }
Request body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Required | Track title |
track_number | integer | Required | Position on the release |
primary_artist_name | string | Optional | Track artist (defaults to release artist) |
featured_artist_ids | array | Optional | Array of Tunearo artist UUIDs for featured artists |
featured_artist_names | string | Optional | Fallback string e.g. "feat. Artist A, Artist B" |
explicit_content | boolean | Optional | Default false |
isrc | string | Optional | Existing ISRC if you have one |
language | string | Optional | Track language |
lyrics | string | Optional | Full lyrics text |
composer | string | Optional | Composer name |
producer | string | Optional | Producer name |
mixer | string | Optional | Mix engineer name |
songwriter | string | Optional | Songwriter name |
lyricist | string | Optional | Lyricist name |
mastering_engineer | string | Optional | Mastering engineer name |
publisher | string | Optional | Publisher name |
remixer | string | Optional | Remixer name |
Example
curl -X POST ".../releases/{id}/tracks" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Midnight Drive",
"track_number": 1,
"primary_artist_name": "Aria Vox",
"featured_artist_ids": ["d103206d-977d-402e-bb4d-4238f644be75"],
"songwriter": "John Smith",
"composer": "Aria Vox",
"producer": "DJ Nova",
"publisher": "Nova Publishing Ltd",
"explicit_content": false
}'
Response 201
{
"track": {
"id": "a9190026-...",
"release_id": "ee5dde1d-...",
"title": "Midnight Drive",
"position": 1,
"artist": "Aria Vox",
"featured_artists": "DJ Nova",
"songwriter": "John Smith",
"composer": "Aria Vox",
"producer": "DJ Nova",
"publisher": "Nova Publishing Ltd",
"contributors": [
{ "name": "DJ Nova", "role": "Featured Artist" },
{ "name": "John Smith", "role": "Songwriter" },
{ "name": "Aria Vox", "role": "Composer" },
{ "name": "DJ Nova", "role": "Producer" },
{ "name": "Nova Publishing Ltd", "role": "Publisher" }
],
"isrc": null,
"created_at": "2026-07-23T14:00:23.833755+00:00"
}
}
Returns a single track by ID. Returns 404 if not found or not owned by the caller.
Response 200
{ "track": { "id": "...", "title": "Midnight Drive", ... } }
Update any track field. Only permitted when the release is in draft or rejected status — returns 409 otherwise.
Request body
| Field | Type | Description |
|---|---|---|
title | string | Track title |
track_number | integer | Position on the release |
primary_artist_name | string | Track artist name |
featured_artist_ids | array | Array of Tunearo artist UUIDs for featured artists |
featured_artist_names | string | Fallback string for featured artists |
songwriter | string | Songwriter name |
lyricist | string | Lyricist name |
mastering_engineer | string | Mastering engineer name |
publisher | string | Publisher name |
remixer | string | Remixer name |
explicit_content | boolean | Explicit flag |
isrc | string | ISRC code |
language | string | Track language |
lyrics | string | Full lyrics text |
composer | string | Composer name |
producer | string | Producer name |
Example
curl -X PATCH ".../releases/{id}/tracks/{trackId}" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "title": "Midnight Drive (Edit)", "composer": "Aria Vox", "language": "English" }'
Response 200
{ "track": { "id": "...", "title": "Midnight Drive (Edit)", "composer": "Aria Vox", ... } }
Permanently removes a track from a release. Only permitted when the release is in draft or rejected status.
Response 200
{ "success": true }
File Uploads
Files are uploaded directly to Tunearo's storage (Supabase). The process is two steps: get a pre-signed URL from the API, then PUT the file directly to that URL.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
content_type | string | Required | image/jpeg or image/png |
Response 200
{
"upload_url": "https://xvmaxcwridovgrrrdldp.supabase.co/storage/v1/object/upload/sign/...",
"path": "b2841ad4-.../1782411144951-artwork.jpg",
"bucket": "release-artwork"
}
Then upload the file
# Step 1 — get the URL
UPLOAD=$(curl -s -X POST ".../artwork-upload-url" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{"content_type":"image/jpeg"}')
URL=$(echo $UPLOAD | jq -r .upload_url)
PATH=$(echo $UPLOAD | jq -r .path)
# Step 2 — PUT the file directly to storage
curl -X PUT "$URL" \
-H "Content-Type: image/jpeg" \
--data-binary @cover.jpg
# Step 3 — save the path back to the release
curl -X PATCH ".../releases/{id}" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d "{\"cover_artwork_url\": \"$PATH\"}"
Request body
| Field | Type | Required | Description |
|---|---|---|---|
content_type | string | Required | audio/wav or audio/flac |
extension | string | Required | wav or flac |
Response 200
{
"upload_url": "https://xvmaxcwridovgrrrdldp.supabase.co/storage/v1/object/upload/sign/...",
"path": "b2841ad4-.../ee5dde1d-.../1782411180631-audio.wav",
"bucket": "track-audio"
}
Then upload the file
# Step 1 — get the URL
UPLOAD=$(curl -s -X POST ".../tracks/{trackId}/audio-upload-url" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{"content_type":"audio/wav","extension":"wav"}')
URL=$(echo $UPLOAD | jq -r .upload_url)
# Step 2 — PUT the file directly to storage
curl -X PUT "$URL" \
-H "Content-Type: audio/wav" \
--data-binary @track.wav
Royalties
Query earnings from royalty reports imported by Tunearo. All figures are scoped to your API key's user and returned in GBP. Filters compose freely — combine release_id with period, date ranges, and more.
Returns lifetime and current-month earnings, available balance, and a per-source breakdown. Pass optional filters to narrow the results.
Query parameters
| Param | Type | Description |
|---|---|---|
release_id | uuid | Filter to a single release (must be owned by caller) |
track_id | uuid | Filter to a single track (must be owned by caller) |
period | YYYY-MM | Filter to a calendar month e.g. 2026-05 |
from | YYYY-MM-DD | Range start (inclusive) |
to | YYYY-MM-DD | Range end (inclusive) |
Example — all earnings
curl "https://dashboard.tunearo.com/api/public/v1/royalties" \
-H "Authorization: Bearer tn_live_xxx"
Example — single release, single month
curl "https://dashboard.tunearo.com/api/public/v1/royalties?release_id=095c50ec-...&period=2026-05" \
-H "Authorization: Bearer tn_live_xxx"
Response 200
{
"currency": "GBP",
"lifetime_earnings": 10.39,
"this_month_earnings": 10.39,
"available_balance": 10.39,
"transaction_count": 10,
"by_source": [
{ "source": "Spotify - Stream", "amount": 5.57 },
{ "source": "iTunes - Apple Music", "amount": 2.10 },
{ "source": "YouTube - Subscription", "amount": 1.20 },
{ "source": "TikTok - TikTok", "amount": 0.70 },
{ "source": "Tidal - streaming", "amount": 0.42 },
{ "source": "Amazon - DE", "amount": 0.25 },
{ "source": "Deezer - Deezer", "amount": 0.15 }
],
"first_reported_at": "2026-05-01T00:00:00Z",
"last_reported_at": "2026-05-31T00:00:00Z",
"filters": {
"release_id": null,
"track_id": null,
"period": null,
"from": null,
"to": null
}
}
available_balance is only returned on the unfiltered summary call — it reflects your total account-wide balance. Filtered calls return null for this field.
Returns a detailed breakdown for a single release including per-source, per-territory, and per-track splits.
Query parameters
| Param | Type | Description |
|---|---|---|
period | YYYY-MM | Filter to a calendar month |
from | YYYY-MM-DD | Range start (inclusive) |
to | YYYY-MM-DD | Range end (inclusive) |
Example
curl "https://dashboard.tunearo.com/api/public/v1/releases/095c50ec-.../royalties" \
-H "Authorization: Bearer tn_live_xxx"
Response 200
{
"currency": "GBP",
"lifetime_earnings": 10.39,
"this_month_earnings": 10.39,
"transaction_count": 10,
"by_source": [
{ "source": "Spotify - Stream", "amount": 5.57 }
],
"by_territory": [
{ "territory": "GB", "amount": 6.77 },
{ "territory": "US", "amount": 2.52 }
],
"by_track": [
{ "track_id": "a9190026-...", "title": "Rrf", "amount": 10.39 }
],
"first_reported_at": "2026-05-01T00:00:00Z",
"last_reported_at": "2026-05-31T00:00:00Z"
}
Webhooks 🆕
Webhooks let you receive real-time push notifications when events happen on your account — no polling required. Register a publicly accessible HTTPS endpoint and Tunearo will POST a signed JSON payload to it whenever a subscribed event fires.
secret is returned once on creation — store it securely. Use it to verify the X-Tunearo-Signature header on every incoming request.
Supported events
| Event | Fired when |
|---|---|
release.submitted | A release is submitted for review (including metadata updates on live releases) |
release.approved | A release is approved by the Tunearo team |
release.rejected | A release is rejected — check data.rejection_reason |
release.live | A release goes live on DSPs |
release.takedown_completed | A takedown is completed across all platforms |
royalties.imported | New royalty data has been imported for your account |
Payload shape
{
"event": "release.submitted",
"timestamp": "2026-07-23T08:49:41.259Z",
"data": {
"id": "1cc9611b-...",
"title": "Webhook Test Release",
"status": "pending",
...
}
}
Signature verification
Every webhook request includes an X-Tunearo-Signature header — an HMAC-SHA256 hex digest of the raw request body, signed with your endpoint's secret. Always verify this before processing.
// Node.js example
const crypto = require('crypto');
function verifySignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Retries
If your endpoint returns a non-2xx response, Tunearo retries delivery up to 3 times with exponential backoff: 1 minute, 5 minutes, then 15 minutes. After 3 failed attempts the delivery is marked as failed.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Required | Publicly accessible HTTPS URL |
events | array | Required | List of events to subscribe to |
Example
curl -X POST ".../webhooks" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourplatform.com/webhooks/tunearo",
"events": ["release.submitted","release.approved","release.rejected","release.live","royalties.imported"]
}'
Response 201
{
"webhook": {
"id": "06f77d6c-d589-4b07-b6df-88127f06b4bd",
"url": "https://yourplatform.com/webhooks/tunearo",
"events": ["release.submitted","release.approved","release.rejected","release.live","royalties.imported"],
"active": true,
"secret": "whsec_2cfb45d4fcd01f205ff4130be2f6cb1aa0cd559f...",
"created_at": "2026-07-23T08:47:18.295975+00:00"
}
}
secret is only returned once on creation. Store it immediately — it cannot be retrieved again.
Returns all registered webhook endpoints for your account. Secrets are not included in list responses.
Response 200
{ "webhooks": [ { "id": "...", "url": "...", "events": [...], "active": true } ] }
Returns a single webhook endpoint by ID.
Response 200
{ "webhook": { "id": "...", "url": "...", "events": [...], "active": true } }
Permanently removes a webhook endpoint. No further deliveries will be attempted.
Response 200
{ "success": true }
ISRC & UPC Codes
ISRCs (International Standard Recording Codes) are assigned per track, and UPCs (Universal Product Codes) are assigned per release. Tunearo manages a pool of codes on your behalf.
Requesting codes
To obtain ISRCs or UPCs, email support@tunearo.com with the number of codes you need. Our content team will provide them within 2 hours during business hours.
Applying an ISRC to a track
curl -X PATCH ".../releases/{id}/tracks/{trackId}" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "isrc": "GBxxxxxxxxxxxx" }'
Applying a UPC to a release
curl -X PATCH ".../releases/{id}" \
-H "Authorization: Bearer tn_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "upc": "5063720xxxxxx" }'
Errors
All errors return JSON with an error field and an appropriate HTTP status code.
| Status | Error | Meaning |
|---|---|---|
| 401 | Missing or invalid Authorization header | No key or wrong key format |
| 403 | API access disabled | Key exists but API access not enabled for this user |
| 403 | Account suspended | Account has been suspended |
| 404 | Release not found | ID doesn't exist or belongs to another user |
| 409 | Cannot edit release in status '...' | Release is not in an editable state |
| 422 | validation + issues array | Request body failed validation |
| 403 | Track limit reached (n). Contact support to increase your API quota. | Your plan's track limit has been reached — contact support to upgrade |
Example error response
{
"error": "validation",
"issues": [
{
"field": "release_type",
"message": "Invalid enum value. Expected 'Single' | 'Album' | 'EP'"
}
]
}
Release Statuses
A release moves through these statuses during its lifecycle:
| Status | Description | Editable? |
|---|---|---|
draft | Created but not yet submitted | ✓ Yes |
pending | Submitted, awaiting review | ✗ No |
approved | Approved, being delivered to stores | ✗ No |
live | Live on streaming platforms | ✗ No |
rejected | Rejected — check rejection_reason, fix, and resubmit | ✓ Yes |
takedown_requested | Takedown in progress | ✗ No |
taken_down | Removed from all platforms | ✗ No |
Supported Stores
When a release goes live, it is delivered to all 34 platforms below. Store-level delivery status is not currently available via the API — the release status field reflects overall distribution status.
| # | Platform |
|---|---|
| 1 | iTunes / Apple Music + Shazam |
| 2 | Spotify |
| 3 | YouTube Music |
| 4 | Deezer |
| 5 | Tidal |
| 6 | Amazon Music |
| 7 | AMI Entertainment |
| 8 | Qobuz |
| 9 | Pandora |
| 10 | KKBOX |
| 11 | 7Digital + Snapchat |
| 12 | iMusica |
| 13 | SoundCloud |
| 14 | iHeartRadio |
| 15 | Anghami |
| 16 | SoundExchange |
| 17 | JioSaavn |
| 18 | AWA |
| 19 | NetEase |
| 20 | Sirius XM |
| 21 | YouTube Content ID |
| 22 | Facebook Audio Library |
| 23 | Facebook Rights Manager |
| 24 | Mixcloud |
| 25 | TikTok |
| 26 | Boomplay |
| 27 | Peloton |
| 28 | Audiomack |
| 29 | Trebel |
| 30 | Kuack |
| 31 | Kuaishou |
| 32 | Audible Magic |
| 33 | Tuned Global / Line Music |
| 34 | Lissen |
Rate Limits
There are no enforced rate limits on the API in v1. That said, please follow these guidelines to keep the service healthy for everyone:
| Operation | Recommended limit |
|---|---|
Status polling (GET /releases/{id}/status) | Once per minute per release |
List endpoints (GET /releases, GET /artists) | Once per 30 seconds |
| Write operations (POST, PATCH) | No specific limit — use sensibly |
X-RateLimit-Remaining headers is on the roadmap. We'll communicate limits in advance before enforcing them.
Bandwidth notes
Standard API calls (JSON responses) are tiny and effectively free at any realistic scale. File uploads — audio and artwork — go directly to Tunearo's storage layer and do not pass through the API server, so large files don't affect API performance.
Versioning
The current API version is v1, reflected in the base URL: /api/public/v1.
| Policy | Detail |
|---|---|
| Backward compatibility | We will not make breaking changes to v1 without prior notice |
| New fields | New optional fields may be added to responses at any time — build tolerantly |
| Deprecation | Deprecated endpoints will be flagged with a header and given a minimum 90-day migration window before removal |
| New versions | Breaking changes will be introduced as /v2 — v1 will continue to work in parallel |
API keys
- Keys do not expire — rotate them manually from the dashboard whenever needed
- One key per user in v1 — revoke and reissue from Admin → Users → API
- Keys are scoped to the issuing user's data only — no cross-user access is possible
- Multiple keys per user and audit logs are planned for a future release
Roadmap
Features planned for future API versions:
| Feature | Notes |
|---|---|
| Rate limiting | Formal limits with X-RateLimit-Remaining response headers |
| Idempotency keys | Idempotency-Key header support on POST endpoints to safely retry requests |
| Per-store delivery status | Granular delivery status per platform |
| Multiple API keys | Issue and manage multiple keys per user with audit logs |
| Key scopes/permissions | Read-only keys, submission-only keys etc. |
Recently shipped ✅
| Feature | Shipped |
|---|---|
Webhooks — release.submitted, release.approved, release.rejected, release.live, release.takedown_completed, royalties.imported | July 2026 |
| GET / PATCH / DELETE artists | July 2026 |
| GET / PATCH / DELETE tracks | July 2026 |
| DELETE draft releases | July 2026 |
Live metadata updates via PATCH /releases/{id}/metadata | July 2026 |
Multiple primary artists via artist_ids array with full artist objects in response | July 2026 |
Featured artists via featured_artist_ids on tracks | July 2026 |
| Expanded contributor roles — songwriter, lyricist, mastering engineer, publisher, remixer | July 2026 |
Territory controls — licensed_territories_include and licensed_territories_exclude | July 2026 |
YouTube OAC requests — POST and GET /artists/{id}/oac-request | July 2026 |