API Reference
Complete technical reference for all TrackID.dev API endpoints
Quick Reference
Base URL
https://trackid.dev/apiAuthentication
None RequiredRate Limit
10 requests / 15 minutesResponse Format
JSON📋 Endpoints Overview
POST
Submit URL for analysis/api/analyzeGET
Get job status & results/api/job/[id]GET
Service health check/api/healthAdvanced 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 occurredCommunity 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.