AI Music Detection API

Submit a track by URL, then poll for the result. The API uses the same credits and returns the same report as the web app. Base URL: https://musicdetector.org/api/v1.

Authentication

Create a key under Dashboard → API keys and send it as a bearer token on every request:

Authorization: Bearer YOUR_API_KEY

Keep keys server-side. Anyone with a key can spend its credits.

Billing

1 credit per started 5 minutes of audio, taken from duration_seconds: up to 5:00 costs 1 credit, 5:01–10:00 costs 2, 10:01–15:00 costs 3. Tracks longer than 15 minutes are rejected — split them first. If a detection fails, its credits are refunded automatically. See API pricing.

Create a detection

POST /api/v1/detections

FieldTypeDescription
audio_urlstring, requiredPublic or pre-signed https:// URL we can download. MP3, WAV, FLAC, M4A, AAC, OGG, OPUS or AIFF. Signed URLs should stay valid for at least 1 hour.
duration_secondsnumber, requiredTrack length in seconds, 5–900. Used for billing.
file_namestring, optionalYour label for the track (max 200 characters), echoed back in results and history.
curl -X POST https://musicdetector.org/api/v1/detections \
  -H "Authorization: Bearer $AIMD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audio_url": "https://cdn.example.com/deliveries/8841/track01.wav",
    "duration_seconds": 214,
    "file_name": "track01.wav"
  }'
HTTP/1.1 202 Accepted
{
  "code": 200,
  "message": "accepted",
  "data": {
    "id": "tsk-3f9a61c0b2d4e8a17c55mfz2k1",
    "status": "processing",
    "credits": 1,
    "poll_url": "/api/v1/detections/tsk-3f9a61c0b2d4e8a17c55mfz2k1"
  }
}

Get a detection

GET /api/v1/detections/{id} — poll every 2–3 seconds until status is completed or failed. Most tracks finish in under a minute.

curl https://musicdetector.org/api/v1/detections/tsk-3f9a61c0b2d4e8a17c55mfz2k1 \
  -H "Authorization: Bearer $AIMD_API_KEY"
{
  "code": 200,
  "message": "success",
  "data": {
    "id": "tsk-3f9a61c0b2d4e8a17c55mfz2k1",
    "status": "completed",
    "created_at": "2026-09-24 12:41:07",
    "file_name": "track01.wav",
    "duration_seconds": 214,
    "credits": 1,
    "result": {
      "verdict": "ai",
      "ai_probability": 97.6,
      "likely_source": "Suno",
      "source_confidence": "clear",
      "sources": [
        { "source": "Suno", "probability": 96 },
        { "source": "Mureka", "probability": 0.8 },
        { "source": "Udio", "probability": 0.6 }
      ],
      "segments": [
        { "start": 0, "end": 107, "prediction": "ai_generated", "ai_probability": 98.1, "likely_source": "Suno" },
        { "start": 107, "end": 214, "prediction": "ai_generated", "ai_probability": 96.9, "likely_source": "Suno" }
      ],
      "stems": [
        { "stem": "vocals", "prediction": "ai_generated", "ai_probability": 98.3, "likely_source": "Suno" },
        { "stem": "accompaniment", "prediction": "ai_generated", "ai_probability": 91.0, "likely_source": "Suno" }
      ],
      "duration_seconds": 214,
      "model_version": "lv1axtcv"
    },
    "error": null
  }
}

Result fields

FieldDescription
verdictai (≥90), likely_ai (65–90), uncertain (35–65), likely_human (10–35), human (<10).
ai_probability0–100 for the full mix.
likely_sourceBest-matching generator, or null when the track reads as human.
source_confidenceclear when one generator dominates, unclear when scores are close, none for human reads.
sourcesScore per generator (0–100), highest first.
segmentsPer-section reads with start/end in seconds.
stemsSeparate vocals / accompaniment reads when available. prediction: "no_vocals" means no vocal was found.
model_versionDetection model identifier. Store it with the result for audits.

List detections

GET /api/v1/detections?limit=20&cursor=… returns your detections newest first with the same item shape. Pass next_cursor from the previous page to continue.

Errors

Errors use the same envelope: {"code": 400, "message": "…", "data": {"error": "INVALID_DURATION"}}.

HTTPerrorMeaning
400INVALID_AUDIO_URLaudio_url is missing, not https, or points to a private host.
400INVALID_DURATIONduration_seconds missing or outside 5–900 seconds.
401UNAUTHORIZEDMissing or invalid API key.
402 / 403INSUFFICIENT_CREDITSNot enough credits for this track. Buy a pack or wait for daily free credits.
404NOT_FOUNDUnknown id, or the detection belongs to another account.
429DETECTION_REJECTEDRate limited. Back off and retry.
502UPSTREAM_UNAVAILABLETemporary error. Retry with exponential backoff.

Examples

Python

import os, time, requests

BASE = "https://musicdetector.org/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['AIMD_API_KEY']}"}

def detect(audio_url: str, duration_seconds: float, file_name: str = "") -> dict:
    r = requests.post(f"{BASE}/detections", headers=HEADERS, json={
        "audio_url": audio_url,
        "duration_seconds": duration_seconds,
        "file_name": file_name,
    }, timeout=30)
    r.raise_for_status()
    detection_id = r.json()["data"]["id"]

    for _ in range(120):                      # up to ~5 minutes
        time.sleep(2.5)
        d = requests.get(f"{BASE}/detections/{detection_id}", headers=HEADERS, timeout=30).json()["data"]
        if d["status"] == "completed":
            return d["result"]
        if d["status"] == "failed":
            raise RuntimeError(d["error"])      # credits are refunded automatically
    raise TimeoutError(detection_id)

report = detect("https://cdn.example.com/deliveries/8841/track01.wav", 214, "track01.wav")
if report["verdict"] in ("ai", "likely_ai"):
    print("route to AI policy queue:", report["likely_source"], report["ai_probability"])

Node.js

const BASE = 'https://musicdetector.org/api/v1'
const headers = { Authorization: `Bearer ${process.env.AIMD_API_KEY}`, 'Content-Type': 'application/json' }

export async function detect(audioUrl, durationSeconds, fileName = '') {
  const created = await fetch(`${BASE}/detections`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ audio_url: audioUrl, duration_seconds: durationSeconds, file_name: fileName }),
  }).then(r => r.json())
  if (created.code !== 200) throw new Error(created.message)

  for (let i = 0; i < 120; i++) {
    await new Promise(r => setTimeout(r, 2500))
    const { data } = await fetch(`${BASE}/detections/${created.data.id}`, { headers }).then(r => r.json())
    if (data.status === 'completed') return data.result
    if (data.status === 'failed') throw new Error(data.error)
  }
  throw new Error('Timed out waiting for detection')
}

Recommended routing

  • human, likely_human → continue delivery.
  • uncertain → send to a human listener; heavy processing and partly AI tracks land here.
  • likely_ai, ai → apply your AI policy (label, ask the uploader, or hold). Keep the JSON report.

Detection is probabilistic. Do not use a single result as the only basis for takedowns or withholding payouts.

Limits & data

  • Tracks: 5 seconds to 15 minutes. Files are downloaded from your URL once.
  • Audio is deleted within 24 hours of the detection and never used for training.
  • Batch endpoints and webhooks are on the roadmap. Need them now? Email [email protected].