TrackID.dev

API Reference

Complete technical reference for all TrackID.dev API endpoints

Quick Reference

Base URL

https://trackid.dev/api

Authentication

None Required

Rate Limit

10 requests / 15 minutes

Response Format

JSON

📋 Endpoints Overview

POST/api/analyze
Submit URL for analysis
GET/api/job/[id]
Get job status & results
GET/api/health
Service health check

Advanced Integration Patterns

🔔 Real-time Updates (Polling Pattern)

Since webhooks aren't available, implement efficient polling:

// Smart polling with exponential backoff
async function pollWithBackoff(jobId, maxAttempts = 60) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const response = await fetch(`/api/job/${jobId}`);
    const job = await response.json();
    
    if (job.status === 'completed' || job.status === 'failed') {
      return job;
    }
    
    // Progressive delay: 3s, 5s, 8s, 10s, 10s...
    const delay = Math.min(3000 + (attempt * 1000), 10000);
    await new Promise(resolve => setTimeout(resolve, delay));
  }
  
  throw new Error('Polling timeout');
}

🔄 Batch Processing

Process multiple videos efficiently while respecting rate limits:

async function processBatch(urls) {
  const results = [];
  const BATCH_SIZE = 5; // Stay within rate limits
  
  for (let i = 0; i < urls.length; i += BATCH_SIZE) {
    const batch = urls.slice(i, i + BATCH_SIZE);
    
    // Start all jobs in batch
    const jobIds = await Promise.all(
      batch.map(async (url) => {
        const response = await fetch('/api/analyze', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ url })
        });
        const { jobId } = await response.json();
        return { url, jobId };
      })
    );
    
    // Wait for all to complete
    const batchResults = await Promise.all(
      jobIds.map(({ url, jobId }) => pollWithBackoff(jobId))
    );
    
    results.push(...batchResults);
    
    // Rate limit pause between batches
    if (i + BATCH_SIZE < urls.length) {
      await new Promise(resolve => setTimeout(resolve, 15 * 60 * 1000)); // 15 min
    }
  }
  
  return results;
}

Error Recovery & Retries

async function robustAnalyze(url, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('/api/analyze', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ url })
      });
      
      if (response.status === 429) {
        // Rate limited - wait and retry
        const retryAfter = 15 * 60 * 1000; // 15 minutes
        console.log(`Rate limited, waiting ${retryAfter/1000}s...`);
        await new Promise(resolve => setTimeout(resolve, retryAfter));
        continue;
      }
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      
      const { jobId } = await response.json();
      return await pollWithBackoff(jobId);
      
    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error.message);
      
      if (attempt === maxRetries) {
        throw error;
      }
      
      // Exponential backoff between retries
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

Complete Response Schemas

JobResponse Schema

interface JobResponse {
  id: string;                    // Unique job identifier
  status: JobStatus;             // Current processing status
  progress: number;              // Completion percentage (0-100)
  currentStep: string;           // Human-readable current step
  estimatedTimeRemaining?: number; // Seconds remaining (if available)
  youtubeUrl: string;            // Original YouTube URL
  createdAt: string;             // ISO 8601 timestamp
  updatedAt: string;             // ISO 8601 timestamp
  tracks?: Track[];              // Available when status === 'completed'
  error?: string;                // Error message if status === 'failed'
}

type JobStatus = 
  | 'queued' 
  | 'downloading' 
  | 'segmenting' 
  | 'fingerprinting' 
  | 'matching' 
  | 'completed' 
  | 'failed';

Track Schema

interface Track {
  id: string;              // Unique track identifier
  timestamp: number;       // Start time in seconds from beginning
  duration: number;        // Track length in seconds
  artist: string;          // Artist name (empty string if unknown)
  title: string;           // Track title (empty string if unknown)
  confidence: number;      // Confidence score 0.0-1.0
  acoustidId?: string;     // AcoustID database identifier
  youtubeUrl?: string;     // Individual track YouTube URL
  isUnknown?: boolean;     // True if track couldn't be identified
}

// Confidence Score Guide:
// 0.9-1.0: Very High (almost certainly correct)
// 0.8-0.9: High (likely correct)
// 0.6-0.8: Medium (possibly correct, manual verification recommended)
// 0.3-0.6: Low (uncertain, likely incorrect)
// 0.0-0.3: Very Low (probably incorrect or unknown)

Error Response Schema

interface ErrorResponse {
  error: string;           // Human-readable error message
  code?: string;           // Machine-readable error code
  details?: any;           // Additional error context
}

// Common Error Codes:
// INVALID_URL: YouTube URL format is invalid
// RATE_LIMITED: Too many requests from this IP
// PROCESSING_FAILED: Audio processing encountered an error
// VIDEO_UNAVAILABLE: YouTube video is private/deleted/restricted
// INTERNAL_ERROR: Server-side error occurred

Community SDKs & Libraries

Community-built libraries to make integration even easier:

JavaScript/TypeScript

• trackid-js - Full-featured client

• trackid-react - React hooks

• trackid-node - Node.js wrapper

Python

• trackid-python - Python client

• trackid-async - Asyncio support

Other Languages

• trackid-go - Go client

• trackid-php - PHP wrapper

• trackid-ruby - Ruby gem

Framework Integrations

• trackid-nextjs - Next.js plugin

• trackid-express - Express middleware

• trackid-fastapi - FastAPI integration

🚀 Want to contribute?

Build an SDK for your favorite language! We'll feature it here and in our docs.