API Reference Β· v1
DistroShield API
REST API for pre-upload music quality control. Each /v1/analyze request runs four modules in parallel β AI detection, duplicate check, metadata validation, and recording fingerprint β returning a combined recommendation (pass / review / block) before your track reaches any DSP. Built for distributors' ingest pipelines.
https://api.distroshield.com
distroshield-v7c
Model-specific attribution (live since 2026-05-04)
The API returns a classification_origin field that identifies the generator. When classification is ai, this field is one of suno_unlicensed, licensed_ai (Udio / ElevenLabs), or unknown_ai. When classification is human or hybrid, the field mirrors that value. Additive and backward-compatible.
Attribution is informational β the operational decision is yours. Common practice for indie distributors: distribute AI tracks to DSPs but withhold Content ID monetization claims until licensing clarity. Some DSPs demonetize AI streams automatically; Believe and TuneCore block proactively. Pick the policy that fits your DSP relationships and catalog mix.
Authentication
Every request (except GET /health) requires a Bearer API key issued by DistroShield. Keep your key secret β it's your client identity and billing binding.
Authorization: Bearer ds_YOUR_API_KEY_HERE
Keys are provisioned manually during onboarding. Missing or invalid key β 401 unauthorized.
Quickstart
Minimal call that analyzes a track and returns classification. Replace YOUR_API_KEY and AUDIO_URL.
curl -X POST https://api.distroshield.com/v1/analyze \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"audio_url": "AUDIO_URL",
"metadata": { "title": "Song A", "artist": "Artist A" },
"client_track_id": "internal-123"
}'
Typical latency: 3β6 seconds (audio download + inference). For high throughput, use POST /v1/batch.
/v1/analyze
Analyze a track
Synchronously analyze one audio URL. Returns classification, AI score, and the derived recommendation (pass / review / block).
Request body
| Field | Type | Description |
|---|---|---|
audio_url | string, required | Publicly fetchable HTTPS URL to the audio file (MP3, WAV, FLAC, M4A). Signed Firebase / S3 URLs are fine. |
metadata | object, optional | Track metadata (see below). Helps local signals. |
metadata.title | string | Track title. |
metadata.artist | string | Artist name. Names matching AI-generator patterns boost local_score. |
metadata.isrc | string | ISRC code. |
metadata.duration_seconds | integer | Track duration in seconds. |
client_track_id | string, optional | Your internal track ID (β€255 chars). Echoed back for correlation. |
Example
POST https://api.distroshield.com/v1/analyze
Authorization: Bearer ds_...
Content-Type: application/json
{
"audio_url": "https://firebasestorage.googleapis.com/.../track.wav?token=...",
"metadata": {
"title": "Mi Bello Puerto",
"artist": "Juan PΓ©rez",
"isrc": "USRC17607839",
"duration_seconds": 187
},
"client_track_id": "mhm-2026-03296"
}
Response 200 OK
The response combines four independent quality signals β AI detection, duplicate check, metadata validation, and recording fingerprint β into a single recommendation. Details on each module below.
{
"analysis_id": "an_8_2HwDlNC1f_",
"db_id": 42,
// ---- Module 1: AI detection ----
"ai_score": 0.0655,
"classification": "human",
"classification_origin": "human", // mirrors classification when not ai
"origin_confidence": 0.9775,
"confidence": 0.9775,
"signals": {
"local_score": 0.15,
"local_reasons": ["missing_artist"],
"model_score": 0.0373,
"weights": { "local": 0.25, "model": 0.75 }
},
"model_version": "distroshield-v7c",
"attribution_model_version": null, // populated only when classification == "ai"
// ---- Module 2: Duplicate check (only if metadata.isrc or title+artist given) ----
"duplicate_score": 0,
"duplicate_matches": [],
"sources_checked": ["spotify", "deezer", "youtube"],
// ---- Module 3: Metadata validation (only if metadata given) ----
"metadata_validation": {
"score": 0.95,
"summary": { "high": 0, "medium": 0, "low": 1 },
"issues": [
{ "severity": "low", "type": "ddex_missing_recommended",
"detail": "Recommended field \"genre\" is missing" }
]
},
// ---- Module 4: Recording fingerprint (only if audio_url given) ----
"recording_fingerprint": {
"matched": false,
"matches": [],
"highest_score": 0,
"distinct_artists_at_perfect_score": 0,
"submitted_isrc_matched": false,
"submitted_artist_matched": false,
"review_reason": null
},
// ---- Combined decision ----
"recommendation": "pass",
"analyzed_at": "2026-04-24T16:27:01.742Z"
}
Response fields
| Field | Meaning |
|---|---|
ai_score | Final score 0.0β1.0 (ensemble of model + local signals). Higher = more likely AI. |
classification | Derived from ai_score against your client thresholds: human, hybrid, or ai. |
classification_origin | Sub-attribution when AI is detected. One of: human, hybrid, suno_unlicensed (Suno-style β Warner deal Nov 2025, UMG/Sony pending), licensed_ai (Udio / ElevenLabs β major-label deals), unknown_ai (MusicGen / Stable Audio / Riffusion / unknown). When classification is human or hybrid, this mirrors that value. Informational only β the distribution / Content ID decision belongs to your policy. |
origin_confidence | Confidence (0β1) in the classification_origin attribution. |
attribution_model_version | Optional. Identifier of the attribution model that produced classification_origin (e.g. distroshield-v8-attribution). null when only the primary binary model ran. |
confidence | How confident the model is in the classification (0.0β1.0). |
signals | Breakdown: model_score (pure ML), local_score (metadata rules), weights used. |
recommendation | pass (ship it), review (human-in-the-loop), or block (do not deliver to DSP). Upgraded from pass to review when a strong duplicate, high-severity metadata issue, or recording-fingerprint match is detected. |
review_reason | Set when recommendation was upgraded from pass. One of: duplicate_detected, metadata_issue, recording_fraud_match, cross_distributor_recording_fraud. |
duplicate_score | 0β1. Highest confidence that this track already exists somewhere public. See Duplicate check. |
duplicate_matches | Up to 10 ranked matches from Spotify, Deezer, YouTube. |
sources_checked | Which DSPs were queried successfully. |
metadata_validation | Per-field issues + overall score. See Metadata validation. |
recording_fingerprint | Audio-hash match against 70M+ commercial recordings. Catches re-uploads with metadata changed. See Recording fingerprint. |
db_id | Internal integer ID. Use this to PATCH reviews or fetch later. |
model_version | Detector version that scored this track. Pin to monitor drift. |
recommendation: "block" should enter a review queue, not be deleted.
Response modules
Each /v1/analyze response is produced by four independent quality-control modules running in parallel. Each module is optional β fields appear only when the module ran:
Acoustic analysis of the audio itself (wav2vec2 fine-tuned on real AI / human catalogs). Always runs. Returns ai_score, classification, signals.
Cross-references ISRC and title+artist against Spotify, Deezer, YouTube in parallel. Runs when metadata has ISRC or title+artist. Returns duplicate_score, duplicate_matches.
ISRC format validation, DDEX completeness, identity fraud detection (ISRC belongs to someone else), artist-name impersonation checks. Runs when metadata is provided. Returns metadata_validation.
Audio-hash match against 70M+ commercial recordings. Catches re-uploads with metadata changed and cross-distributor identity fraud. Runs when an audio URL is provided. Returns recording_fingerprint.
Module 2 Β· Duplicate check
Checks whether the track already exists in major public music sources. Executes when metadata.isrc OR both metadata.title and metadata.artist are provided. Skipped otherwise.
Sources
| Source | ISRC lookup | Title+artist search |
|---|---|---|
| Spotify Web API | β exact | β fuzzy |
| Deezer public API | β exact | β fuzzy |
| YouTube Data API v3 | β | β fuzzy |
Scoring
- Exact ISRC match on any source β
match_score: 1.0 - Fuzzy title+artist match (≥0.5) β
match_score: 0.5β1.0 duplicate_scoreis the top match's score + 5% boost per additional source that also matched
Example match
{
"source": "spotify",
"match_type": "isrc", // or "title_artist"
"match_score": 1.0,
"id": "7qiZfU4dY1lWllzX7mPBI3",
"title": "Shape of You",
"artist": "Ed Sheeran",
"url": "https://open.spotify.com/track/7qiZfU4dY1lWllzX7mPBI3"
}
Module 3 Β· Metadata validation
Validates the declared metadata across four dimensions. Runs locally (<10 ms) when metadata is provided.
Checks performed
| Check | What it catches |
|---|---|
| ISRC format | Structure (12 chars: 2 letters + 3 alnum + 7 digits), country code validity, future-year detection. |
| DDEX completeness | Mandatory fields (title, artist) + recommended DDEX 4 fields (isrc, duration_seconds, album, genre, language, release_date). Missing recommended fields are consolidated into a single issue to avoid noise. |
| Identity fraud | Cross-references the declared title + artist against what Spotify / Deezer have registered under that ISRC. Catches the "I claim this ISRC as my new song" fraud where someone submits a track using another artist's ISRC. |
| Artist impersonation | Fuzzy match against a curated list of globally-famous + LATAM-top artists. Flags suspicious near-matches like "3d Sheeran" vs "Ed Sheeran". |
| Duration anomaly checks | Flags duration-based fraud and anomaly patterns (including known Content ID abuse). Specific thresholds are kept private to avoid tipping off bad actors β legitimate tracks are not affected. |
Issue structure
{
"severity": "high", // high | medium | low
"type": "isrc_identity_artist_mismatch",
"detail": "ISRC is registered on spotify as artist 'The Weeknd' but you declared 'Mi Artista'",
"registered": { // present on identity-mismatch only
"source": "spotify",
"artist": "The Weeknd",
"title": "Blinding Lights",
"url": "https://open.spotify.com/track/..."
}
}
Scoring
metadata_validation.score starts at 1.0 and subtracts penalties: high -0.40, medium -0.15, low -0.05. Clamped to [0, 1]. Any high severity issue upgrades the overall recommendation from pass to review.
Module 4 Β· Recording fingerprint
Hashes the submitted audio and matches it against a catalog of 70M+ commercial recordings. Catches the case other modules are structurally blind to: a fraudster re-uploading someone else's exact recording with the metadata changed.
Executes when an audio_url is provided. Skipped otherwise.
Fields
| Field | Meaning |
|---|---|
matched | True when at least one recording in the catalog matches. |
matches | Up to 10 ranked matches. Each contains title, artists, album, label, score (0β100), release_date, isrc, upc, plus direct URLs to spotify_url, deezer_url, youtube_url when available. |
highest_score | Top match's score (0β100). 100 = perfect audio match. |
distinct_artists_at_perfect_score | How many different artist identities own a near-perfect (β₯95) match. β₯2 means the same recording is registered under multiple identities across distributors β per-se evidence of cross-distributor identity fraud. |
submitted_isrc_matched | True when the ISRC in your metadata matches one of the ISRCs returned by the fingerprint match. Surfacing-only β does NOT downgrade the flag (a fraudster can copy any ISRC). |
submitted_artist_matched | Same idea, for artist name. |
review_reason | Set to cross_distributor_recording_fraud when 2+ distinct artists match at score β₯95, or recording_fraud_match when at least one match is at score β₯80. Otherwise null. |
error | Optional. Set when the fingerprint backend returned an error (e.g. timeout, billing issue). The pipeline continues without this signal in that case. |
Routing
- β’ A single match at score β₯80 sets
review_reasontorecording_fraud_matchand upgradesrecommendationfrompasstoreview. - β’ Two or more matches at score β₯95 with different artists takes precedence and sets
review_reasontocross_distributor_recording_fraudβ the highest-confidence form of fraud the API surfaces. - β’ The signal never auto-blocks. Human-in-the-loop is mandatory.
/v1/batch
Batch analyze (async)
Submit many tracks at once. Returns a batch ID immediately; tracks are processed asynchronously by the worker queue. Results delivered via webhook (recommended) or polled via GET /v1/batch/:id.
Request body
{
"tracks": [
{ "audio_url": "...", "metadata": {...}, "client_track_id": "..." },
{ "audio_url": "...", "metadata": {...}, "client_track_id": "..." }
],
"webhook_url": "https://your-server.com/distroshield-webhook"
}
Response 200 OK
{
"batch_id": 17,
"track_count": 2,
"status": "queued"
}
Each track result is POSTed to webhook_url with HMAC-SHA256 signature in X-DistroShield-Signature header.
/v1/analysis/:id
Retrieve a single analysis
Fetch a full analysis row by its db_id. Scoped to your client β you can only fetch analyses for tracks you own.
GET https://api.distroshield.com/v1/analysis/42
Authorization: Bearer ds_...
β {
"id": 42,
"track_id": 58,
"ai_score": "0.0655",
"classification": "human",
"confidence": "0.9775",
"signals_json": "{...}",
"model_version": "distroshield-v7c",
"review_status": "none",
"review_verdict": null,
"reviewer_email": null,
"reviewed_at": null,
"review_notes": null,
"analyzed_at": "2026-04-24T16:27:01.000Z",
"client_id": 1
}
/v1/analyses
List analyses (review queue)
Paginated list of analyses for your client. Use the status query param to filter β this is how your review queue UI fetches pending items.
Query parameters
| Param | Default | Values |
|---|---|---|
status | pending | none, pending, reviewed, overridden, all |
classification | β | human, hybrid, ai |
limit | 50 | Integer 1β200 |
offset | 0 | Integer β₯0 |
Example
GET /v1/analyses?status=pending&classification=hybrid&limit=20
Authorization: Bearer ds_...
β {
"count": 20,
"limit": 20,
"offset": 0,
"items": [
{
"id": 42,
"ai_score": "0.5231",
"classification": "hybrid",
"model_version": "distroshield-v7c",
"review_status": "pending",
"analyzed_at": "2026-04-24T16:27:01.000Z",
"track_id": 58,
"title": "Track title",
"artist": "Artist",
"source_url": "https://..."
},
...
]
}
/v1/analyses/:id/review
Record human verdict
After a reviewer decides whether a flagged track is actually AI or human, persist their verdict. These labels feed into future model retraining.
Request body
| Field | Type | Description |
|---|---|---|
verdict | string, required | human β track is fully human-madeai β track is fully AI-generateduncertain β reviewer cannot decidehybrid_confirmed β reviewer confirms the track legitimately mixes AI and human elements (e.g. AI-generated instruments with human vocals). Future model retraining uses this as a soft label so the model learns to produce intermediate scores instead of forcing every track into the binary human/ai decision. |
notes | string, optional, nullable | Free-text note about why (β€2000 chars). May be omitted or sent as null. |
reviewer_email | string, optional, nullable | Email of the person reviewing. May be omitted or sent as null; defaults to the client's contact_email. |
PATCH https://api.distroshield.com/v1/analyses/42/review
Authorization: Bearer ds_...
Content-Type: application/json
{
"verdict": "human",
"notes": "Heavy autotune but definitely human β female reggaeton vocal",
"reviewer_email": "j@mhmusik.com"
}
β {
"analysis_id": 42,
"original_classification": "ai",
"human_verdict": "human",
"review_status": "overridden",
"reviewer_email": "j@mhmusik.com",
"reviewed_at": "2026-04-24T17:30:12.000Z"
}
review_status becomes overridden when the verdict disagrees with the model's original classification, else reviewed.
/v1/webhooks
Register a webhook
DistroShield will POST async analysis results to your URL. The secret returned is used to sign each delivery with HMAC-SHA256 β verify signatures to reject forgeries.
POST /v1/webhooks
Authorization: Bearer ds_...
{
"url": "https://your-server.com/distroshield-webhook",
"events": ["analysis.completed", "batch.completed"]
}
β {
"webhook_id": 3,
"secret": "whsec_abc...", // shown ONCE β store it securely
"url": "https://...",
"events": [...]
}
Each delivery retries with exponential backoff (30s β 4d, up to 8 attempts) until your endpoint responds 2xx.
/v1/usage
Usage metrics
Current period API usage for your client. Use to monitor your quota.
GET /v1/usage
Authorization: Bearer ds_...
β {
"period_start": "2026-04-01T00:00:00Z",
"period_end": "2026-04-30T23:59:59Z",
"requests": { "analyze": 312, "batch": 4, "total": 316 },
"analyses_by_classification": {
"human": 287, "hybrid": 18, "ai": 11
}
}
Error codes
Errors return JSON with an error code. Some include additional context.
| Status | Code | Meaning |
|---|---|---|
400 | invalid_body | Request body didn't match the schema. Check the issues array. |
400 | invalid_query | Query parameters didn't validate. |
400 | invalid_analysis_id | The :id path parameter wasn't a positive integer. |
401 | unauthorized | Missing, malformed, or invalid Bearer token. |
404 | analysis_not_found | Analysis ID doesn't exist OR doesn't belong to your client. |
415 | unsupported_audio_format | The audio file at audio_url could not be decoded. Response includes supported_formats array. Supported: WAV, FLAC, OGG, MP3, M4A, AAC. |
429 | rate_limited | You exceeded your rate limit window. Retry after the period resets. |
500 | internal_error | Unexpected server error. Contact support with the request ID in response headers. |
502 | audio_fetch_failed | We could not fetch the audio from the URL you provided. Check that the URL is reachable and not expired (e.g. signed URL TTL). |
502 | inference_unavailable | The inference service is temporarily unavailable. Retry shortly. |
Rate limits
Default: 300 requests per minute per API key. Response headers expose your current state:
X-RateLimit-Limit: 300 X-RateLimit-Remaining: 297 X-RateLimit-Reset: 60
Exceeding returns 429 rate_limited. Need higher limits? Contact us for Pro/Enterprise tiers.
Detection model
Current production version: distroshield-v7c. Trained on a curated mix of: real distributor-verified AI tracks, Suno / MusicGen-generated samples, and over 1,500 human tracks from a real distributor catalog spanning reggaetΓ³n, latin pop, dembow, regional mexicano, cristiano/gospel, electronic, hip-hop, bachata, cumbia, folk and other Latin American styles. Continuously improved from production review verdicts.
Scores are probabilistic. Human-in-the-loop review is mandatory. Never auto-block tracks without a human override path β the API is a gate, not a judge.