TrackTag API: auto-tag any music catalog

    One REST API for AI audio analysis: BPM, key, genres, moods, instruments, energy and 35+ metadata fields per track. Submit a URL, get a webhook back. Same credits as Studio, from $0.10 per track.

    Quickstart

    Analyze your first track in 3 steps

    The TrackTag API analyzes audio and returns up to 35 metadata fields (BPM, key, genres, moods, instruments, energy, a written description and a ready-to-use tag list). Both tiers run the same engine and are equally accurate; the tier sets how many fields come back. Core returns 9 fields for 1 credit per track (including the keyword tag list), Ultra returns all 35 for 2. You never upload files to a TrackTag library: pass a URL where the audio lives (your CDN, an S3 signed URL) and we download it, analyze it, and discard it. Nothing is stored except the resulting metadata.

    Submit a track
    curl -X POST https://aaeabanvqnndwgrqsmhg.supabase.co/functions/v1/api-v1/v1/analyze \
      -H "Authorization: Bearer tt_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "audio_url": "https://your-cdn.com/track.mp3",
        "model": "core",
        "track_name": "Sunset Drive"
      }'
    
    # → 202
    # { "id": "9b2e…", "object": "job", "status": "queued",
    #   "model": "core", "credits_charged": 1, … }
    Poll until done (typically 5–15 seconds)
    curl https://aaeabanvqnndwgrqsmhg.supabase.co/functions/v1/api-v1/v1/jobs/9b2e… \
      -H "Authorization: Bearer tt_live_YOUR_KEY"
    
    # → { "status": "done", "result": { "tags": { "bpm": 122,
    #     "genres": ["deep house", …], "mood": ["uplifting", …], … } } }
    1. 1

      Create an API key

      Studio → API tab → Create key. Copy it right away. It is shown only once.

    2. 2

      POST /v1/analyze

      Send an audio_url. You get a job id back immediately (202).

    3. 3

      Get the result

      Poll GET /v1/jobs/{id}, or pass webhook_url and we push the result to you.

    See also

    Sending audio: URL vs inline

    Two ways to hand us audio: • audio_url (recommended): any public or signed https URL, up to 60 MB. We fetch it server-side. Signed/expiring URLs are perfect: they only need to stay valid for a minute. • audio_b64 + mime: base64-encode the file into the request body, up to 15 MB. Simplest for small MP3s; wasteful for big WAVs. Supported formats: MP3, WAV, FLAC, AIFF, M4A, OGG. Tracks longer than 5 minutes are analyzed from their first section, same as Studio.

    Inline upload (small files)
    curl -X POST https://aaeabanvqnndwgrqsmhg.supabase.co/functions/v1/api-v1/v1/analyze \
      -H "Authorization: Bearer tt_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d "{
        \"audio_b64\": \"$(base64 -i track.mp3)\",
        \"mime\": \"audio/mpeg\",
        \"model\": \"core\"
      }"

    See also

    Authentication

    API keys

    Every request needs a bearer key: Authorization: Bearer tt_live_… Create and manage keys in Studio → API. Keys come in two modes: • tt_live_: real analyses, charges credits. • tt_test_: free, returns a truncated result preview; use it to wire up your integration. The full key is shown exactly once, at creation. We store only a hash. If you lose a key, roll it (a new key is issued and the old one is revoked in the same step). Revoking a key disables it immediately. Keep keys server-side. Never ship them in client-side code or mobile apps.

    See also

    Jobs & Polling

    Async jobs and polling

    POST /v1/analyze responds immediately with 202 and a job object. Analysis usually finishes in 5–20 seconds. Job lifecycle: queued → processing → done | error. On "done", job.result.tags holds the full analysis. On "error", the error field says why, and your credits are automatically refunded. A job that somehow gets stuck is timed out and refunded within minutes. bpm_source and key_source tell you where those two fields came from: "measured" means a real audio measurement was used (deterministic: the same file gives the same answer every time); "engine" means the analysis model's own read, used when a confident measurement wasn't available for that file. Every other field is always model-sourced. GET /v1/jobs lists your recent jobs (newest first, cursor pagination with starting_after and limit up to 100, filter with ?status=done). It includes analyses you ran in Studio as well as API calls. Each job carries an origin field of "api" or "studio" so you can tell them apart.

    Job object
    {
      "id": "9b2e4c1a-…",
      "object": "job",
      "status": "done",
      "model": "core",
      "track_name": "Sunset Drive",
      "metadata": { "your_ref": "catalog-4812" },
      "credits_charged": 1,
      "result": {
        "analyzed_by": "TrackTag.me",
        "bpm_source": "measured",
        "key_source": "measured",
        "tags": { "bpm": 122, "key": "A", "mode": "minor", "genres": ["deep house"], … }
      },
      "error": null,
      "created_at": "2026-07-24T10:12:03Z",
      "finished_at": "2026-07-24T10:12:11Z"
    }

    See also

    Synchronous mode (?wait=true)

    For one-off calls you can skip polling: POST /v1/analyze?wait=true holds the connection open and returns the finished job in a single round-trip (200 on success, 502 if the engine fails, with an automatic refund). Set your HTTP client timeout to at least 90 seconds. For batch work, prefer the async flow: it parallelizes much better.

    One-shot analysis
    curl -X POST "https://aaeabanvqnndwgrqsmhg.supabase.co/functions/v1/api-v1/v1/analyze?wait=true" \
      -H "Authorization: Bearer tt_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{"audio_url": "https://your-cdn.com/track.mp3"}'

    Attaching your own reference (metadata)

    Pass a metadata object (up to 1 KB) with any JSON you need echoed back: your internal track id, catalog number, batch tag. It is returned untouched on the job object and in webhook payloads, so you can match results to your database without keeping a job-id mapping.

    Webhooks

    Receiving results by webhook

    Three ways to receive events: • Per request: add "webhook_url": "https://your-app.com/hooks/tracktag" to any analyze call and we POST the finished job there, signed with that key's webhook secret. • Account endpoints: register a URL once in Studio → Integrations → Webhook endpoints and every job.completed / job.failed event for your account is pushed to it (test-mode endpoints receive test-key events). Each endpoint has its own signing secret and a "Send test event" button. • Studio analyses: every track you analyze in Studio or the web analyzer also fires job.completed to your live endpoints, with origin: "studio" (API and MCP analyses carry origin: "api"). The payload is sent after Precision Mode and Technical Data are applied, so it matches exactly what Studio shows you. Webhook URLs must be https. If your server is down, we retry automatically with backoff (1 minute, 5 minutes, 30 minutes, 2 hours, then 8 hours) before marking the delivery exhausted. Endpoints that keep failing are auto-disabled (re-enable them in the dashboard). Every attempt is visible in the delivery log, and polling GET /v1/jobs/{id} always works as a safety net.

    Webhook payload
    POST https://your-app.com/hooks/tracktag
    tt-signature: t=1753350000,v1=5f8a…
    tt-event-id: 7d1c…
    tt-event-type: job.completed
    
    {
      "id": "7d1c…",
      "type": "job.completed",
      "created": 1753350000,
      "livemode": true,
      "data": { "object": { …job object… } }
    }

    See also

    Verifying signatures

    Every webhook is signed with your key's webhook secret (whsec_…, shown in Studio → API next to each key). The tt-signature header carries a timestamp and an HMAC-SHA256 of "<timestamp>.<raw body>". Always verify before trusting a payload: 1. Parse t and v1 from tt-signature. 2. Reject if |now − t| > 300 seconds (replay protection). 3. Compute HMAC-SHA256(secret, t + "." + rawBody) and compare to v1 with a constant-time comparison.

    Node.js
    const crypto = require("crypto");
    
    function verify(rawBody, sigHeader, secret) {
      const parts = Object.fromEntries(sigHeader.split(",").map(p => p.split("=")));
      if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
      const expected = crypto.createHmac("sha256", secret)
        .update(parts.t + "." + rawBody).digest("hex");
      return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
    }
    Python
    import hmac, hashlib, time
    
    def verify(raw_body: bytes, sig_header: str, secret: str) -> bool:
        parts = dict(p.split("=") for p in sig_header.split(","))
        if abs(time.time() - int(parts["t"])) > 300:
            return False
        expected = hmac.new(secret.encode(),
                            f"{parts['t']}.".encode() + raw_body,
                            hashlib.sha256).hexdigest()
        return hmac.compare_digest(expected, parts["v1"])

    Managing endpoints from code

    Endpoints can be registered and removed with the API instead of the dashboard: this is how platforms like Zapier attach their own listener when a user turns on an automation. • GET /v1/webhooks: list the endpoints visible to this key • POST /v1/webhooks { url, description? }: create one; the response includes the signing secret (shown only here, so store it) • DELETE /v1/webhooks/{id}: remove one A key only sees and touches endpoints in its own mode: a tt_test_ key can never read or delete a live endpoint. Posting a URL that is already registered returns the existing endpoint (and re-enables it if it had been auto-disabled) instead of creating a duplicate, so retries are safe. Limit: 10 endpoints per account, https only.

    Register an endpoint
    curl -X POST https://aaeabanvqnndwgrqsmhg.supabase.co/functions/v1/api-v1/v1/webhooks \
      -H "Authorization: Bearer tt_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{"url":"https://your-app.com/hooks/tracktag","description":"production"}'
    
    {
      "id": "b6f0…",
      "object": "webhook_endpoint",
      "url": "https://your-app.com/hooks/tracktag",
      "description": "production",
      "disabled": false,
      "livemode": true,
      "secret": "whsec_…"
    }

    See also

    Errors

    Error reference

    Errors share one envelope: an error object with code, message and doc_url, plus a request_id you can send to support (also returned on every response as the tt-request-id header). • 400 invalid_request: malformed body, bad model, bad URL. • 401 invalid_api_key: missing/unknown key. • 402 insufficient_credits: top up and retry; response includes top_up_url. • 403 key_disabled: the key was revoked. • 404 not_found: unknown endpoint or job. • 413 audio_too_large: over 60 MB (URL) or 15 MB (inline). • 415 unsupported_media: the file is not audio. • 422 audio_fetch_failed: we could not download audio_url (unreachable, private address, too many redirects). • 429 rate_limited: slow down; honor the Retry-After header. • 502 engine_error: the analysis engine failed. If a job fails after being accepted (fetch failure, engine error, timeout), its credits are refunded automatically. You only ever pay for delivered results.

    Error shape
    {
      "error": {
        "code": "insufficient_credits",
        "message": "Not enough credits.",
        "doc_url": "https://tracktag.me/developers/errors#insufficient_credits",
        "top_up_url": "https://tracktag.me/topup"
      },
      "request_id": "f0a4…"
    }

    Rate Limits

    Rate limits

    Per key, by default: • 10 requests per minute (sliding window, all endpoints) • 2,000 analyses per day Hitting a limit returns 429 rate_limited with a Retry-After header. Tagging a big catalog? Space submissions ~6 seconds apart, or contact us for a raise: higher limits are available on request for platforms and labels.

    Pricing & Credits

    How API billing works

    The API draws from the same credit balance as TrackTag Studio: one pool, one ledger. Both tiers run the same engine and are equally accurate. The tier decides how many fields come back, never how good they are. • Core: 1 credit per track. The 9 fields that file and find a track: bpm, key, mode, timeSignature, genres, mood, energy, instruments and the searchable keyword tag list (additionalTags). • Ultra: 2 credits per track. All 35 fields, adding the written description, subgenres, emotions, atmosphere, themes, vocals, structure, dynamics, production notes, era, influences and sync occasions. Credit packs (one-time, never expire): $20 / 50 · $89 / 250 · $149 / 1,000 · $499 / 5,000. That is as low as $0.10 per track. Failed jobs are refunded automatically, and every charge shows up in the Studio credit ledger tagged "api:" so you can tell app and API usage apart. Check your balance any time with GET /v1/account.

    GET /v1/account
    curl https://aaeabanvqnndwgrqsmhg.supabase.co/functions/v1/api-v1/v1/account -H "Authorization: Bearer tt_live_YOUR_KEY"
    
    # → { "object": "account", "credits_remaining": 412,
    #     "rpm_limit": 10, "daily_limit": 2000, "livemode": true }

    MCP for AI Agents

    TrackTag MCP server

    The TrackTag MCP server lets any MCP-capable AI agent (Claude Desktop, Claude Code, Cursor and friends) analyze music natively: drag an MP3 into your agent, say "analyze this track", and the agent calls TrackTag and reports the full metadata back. It runs on your machine (started automatically by the agent app, alive only while the app is open, nothing to host) and gives the agent four tools: • analyze_track: analyze a local audio file (≤15 MB), 1–2 credits • analyze_url: analyze audio from any https URL (≤60 MB), 1–2 credits • get_credits: remaining balance, free • list_recent_jobs: recent analyses, free Because it runs locally, it also works where cloud agent sandboxes block outbound network access.

    Claude Desktop / Cursor: MCP config
    {
      "mcpServers": {
        "tracktag": {
          "command": "npx",
          "args": ["-y", "tracktag-mcp"],
          "env": { "TRACKTAG_API_KEY": "tt_live_YOUR_KEY" }
        }
      }
    }
    Claude Code: one command
    claude mcp add tracktag -e TRACKTAG_API_KEY=tt_live_YOUR_KEY -- npx -y tracktag-mcp

    See also

    Install in Claude Desktop (step by step)

    Five steps, no coding: 1. Create an API key in Studio → Integrations and copy it (shown only once). 2. In Claude Desktop: Claude menu → Settings → Developer → Edit Config. A file called claude_desktop_config.json opens. 3. Paste the config block (previous article) into the mcpServers section (create it if missing) and replace tt_live_YOUR_KEY with your key. Save. 4. Quit Claude Desktop completely (Cmd+Q on Mac) and reopen it. Closing the window is not enough. 5. Drop an audio file into the chat and say "Analyze this track with TrackTag". Approve the tool when asked. TrackTag locates the file on your disk by name, analyzes it, and can report your remaining credits. Cursor works with the same JSON block in ~/.cursor/mcp.json; Claude Code uses the one-line "claude mcp add" command. You need Node.js installed (nodejs.org).

    Dragged files vs local paths

    Dragging a file into the chat uploads a copy to the AI. The analysis still runs on the original from your disk. Since v0.2.0 the server finds it automatically by name (find_audio_files); if it cannot, give the full path (Finder: right-click the file, hold Option, "Copy … as Pathname").

    See also

    Zapier & No-Code

    TrackTag in Zapier

    The TrackTag Zapier app connects your credits to 6,000+ apps without writing code. It gives you: • Trigger, "Analysis Finished": fires the moment a TrackTag analysis completes, with BPM, key, genres, mood, instruments and the description already split into separate fields you can map into a spreadsheet. • Action, "Analyze Track": send an audio file URL. Returns immediately by default, or can hold the step until the result is ready. • Search, "Get Analysis": look up a result by its Analysis ID later. Two typical Zaps: New file in Dropbox → Analyze Track in TrackTag → Add row in Google Sheets, or simply analyze a batch in Studio and let "Analysis Finished" file every result away. The trigger fires for analyses you run in the app, not just API calls, and each event carries an Origin field (studio or api) you can filter on. Connect the app with an API key from Studio → Integrations; a tt_test_ key lets you build the whole Zap without spending credits. TrackTag is listed in Zapier's public app directory, so you can search for it inside the Zap editor, or use the "Add TrackTag to Zapier" button below (the same button sits in Studio → Integrations). It works on any Zapier plan, including free.

    Use a direct download link

    The Analyze Track action needs a URL that returns the audio itself, not a preview page. In Dropbox triggers pick the "Direct Media Link" field; in Google Drive use a direct download link. Max 60 MB.

    See also

    Credits, limits and failures in a Zap

    Zaps spend the same credits as everything else: 1 credit per track on Core, 2 on Ultra. The Zapier app translates TrackTag errors into messages Zapier understands, so nothing fails silently: • Out of credits: the Zap step errors with a top-up link. Nothing is charged. • Rate limited: Zapier is told how long to wait and retries automatically. • Revoked or wrong key: Zapier asks you to reconnect the account. • Failed analysis (bad file, download failed): credits are refunded automatically; switch on "Also trigger on failed analyses" if you want to be notified. If a long file makes the "Wait for the result" option time out, turn it off and read the result from the "Analysis Finished" trigger instead.

    See also

    Test Mode

    Test mode

    Create a tt_test_ key to build your integration without spending credits. Test keys: • never charge credits (credits_charged is always 0), • never run a real analysis: they return a canned sample result whose field shapes match live responses exactly, so you can validate parsing end-to-end, • produce webhook events with livemode: false, delivered to your test-mode endpoints. When you are ready, swap in your tt_live_ key: the request and response shapes are identical.

    See also

    Ready to tag your catalog?

    Create a free account, grab a test key and run your first analysis in under five minutes.

    Get your API key

    TrackTag API · Open Studio · Help Center