API Documentation

Generate AI music programmatically. Integrate custom soundtracks into your apps, games, videos, and workflows.

Authentication

All API requests are authenticated using an API key passed in the X-API-Key header. API access is available on Pro and Lifetime plans.

Header

X-API-Key: ft_live_your_key_here

Getting a key: Go to Profile → API and click "Create Key".

Key format: Keys start with ft_live_ followed by 32 hex characters.

Security: Keys are shown only once at creation. Store them securely. Never expose keys in client-side code.

Endpoints

Base URL: https://pub.finetuning.ai

GET/v1/me

Get your account info and remaining credits.

Response

{
  "data": {
    "id": "usr_abc123",
    "email": "you@example.com",
    "name": "Your Name",
    "tier": "pro",
    "limits": {
      "monthlyGenerations": 2000,
      "generationsUsed": 47,
      "monthlyRemaining": 1953,
      "packCredits": 0,
      "totalRemaining": 1953,
      "queueDepth": 5
    }
  }
}
POST/v1/generations

Create a new music generation. Deducts one credit and submits to the generation queue.

Body Parameters

NameTypeRequiredDescription
tagsstringrequiredMusic description (max 500 chars). E.g. "lofi chill piano night"
lyricsstringoptionalOptional lyrics (max 2000 chars)
durationnumberoptionalDuration in seconds (10-180, default 60)
bpmnumberoptionalTempo (60-200, default 120)
languagestringoptionalLanguage code: en, ja, de, fr, es, zh, ko, pt, it, ru
keystringoptionalMusical key: C, C#, D, ... B
scalestringoptionalScale: major or minor
timesignaturestringoptionalTime signature: 2-7 (default 4)
seednumberoptionalReproducibility seed (random if omitted)

Response

{
  "data": {
    "id": "gen_xyz789",
    "status": "processing",
    "prompt": "lofi chill piano night",
    "parameters": {
      "bpm": 85,
      "duration": 120,
      "timesignature": "4",
      "language": "en",
      "keyscale": "C minor",
      "seed": 1234567
    },
    "creditsRemaining": 1952,
    "createdAt": "2025-01-15T10:30:00Z"
  }
}
GET/v1/generations

List your generations with pagination and optional status filtering.

Query Parameters

NameTypeRequiredDescription
limitnumberoptionalResults per page (1-100, default 20)
offsetnumberoptionalPagination offset (default 0)
statusstringoptionalFilter by status: pending, processing, completed, failed

Response

{
  "data": {
    "generations": [
      {
        "id": "gen_xyz789",
        "title": "Lofi Chill Piano",
        "prompt": "lofi chill piano night",
        "status": "completed",
        "audioUrl": "https://media.finetuning.ai/...",
        "duration": 120,
        "isPublic": false,
        "playCount": 5,
        "likeCount": 0,
        "parameters": { "bpm": 85, "duration": 120, ... },
        "createdAt": "2025-01-15T10:30:00Z",
        "completedAt": "2025-01-15T10:30:08Z"
      }
    ],
    "hasMore": true,
    "nextOffset": 20
  }
}
GET/v1/generations/:id

Get detailed info about a single generation including audio URL, parameters, and statistics.

Response

{
  "data": {
    "id": "gen_xyz789",
    "title": "Lofi Chill Piano",
    "prompt": "lofi chill piano night",
    "status": "completed",
    "audioUrl": "https://media.finetuning.ai/...",
    "duration": 120,
    "fileSize": 1920000,
    "isPublic": false,
    "playCount": 5,
    "downloadCount": 2,
    "likeCount": 0,
    "parameters": { ... },
    "generationTime": 7.2,
    "createdAt": "2025-01-15T10:30:00Z",
    "completedAt": "2025-01-15T10:30:08Z"
  }
}

Rate Limits

60

Requests per minute per user

5

Active API keys per account

Rate limit info is included in response headers:

X-RateLimit-Limit: 60

X-RateLimit-Remaining: 57

Retry-After: 42 (only on 429 responses)

If you exceed the limit, you'll receive a 429 Too Many Requests response with a Retry-After header.

Error Codes

All errors follow the format: {"error": {"code": "...", "message": "..."}}

CodeStatusDescription
MISSING_API_KEY401X-API-Key header was not provided
INVALID_API_KEY401API key is malformed, revoked, or does not exist
PRO_PLAN_REQUIRED403Your subscription does not include API access
RATE_LIMITED429Too many requests — wait and retry
VALIDATION_ERROR400Request body is missing or has invalid fields
MONTHLY_LIMIT_REACHED402No generations remaining this month
QUEUE_FULL429Too many generations in progress
GENERATION_FAILED500Generation queue submission failed
NOT_FOUND404Resource not found
INTERNAL_ERROR500Unexpected server error

Code Examples

curl

# Generate a track
curl -X POST https://pub.finetuning.ai/v1/generations \
  -H "X-API-Key: ft_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "tags": "epic orchestral cinematic trailer",
    "duration": 180,
    "bpm": 140,
    "key": "D",
    "scale": "minor"
  }'

# Check generation status
curl https://pub.finetuning.ai/v1/generations/gen_xyz789 \
  -H "X-API-Key: ft_live_your_key_here"

# List your generations
curl "https://pub.finetuning.ai/v1/generations?limit=10&status=completed" \
  -H "X-API-Key: ft_live_your_key_here"

TypeScript / JavaScript

const API_KEY = process.env.FINETUNING_API_KEY;
const BASE = "https://pub.finetuning.ai";

// Generate a track
const res = await fetch(`${BASE}/v1/generations`, {
  method: "POST",
  headers: {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    tags: "lofi chill piano rain",
    duration: 120,
    bpm: 85,
  }),
});

const { data } = await res.json();
console.log("Generation started:", data.id);

// Poll for completion
const poll = async (id: string) => {
  const r = await fetch(`${BASE}/v1/generations/${id}`, {
    headers: { "X-API-Key": API_KEY },
  });
  const { data } = await r.json();
  if (data.status === "completed") {
    console.log("Audio URL:", data.audioUrl);
  } else if (data.status === "failed") {
    console.error("Failed:", data.errorMessage);
  } else {
    setTimeout(() => poll(id), 3000);
  }
};

poll(data.id);

Python

import requests
import time
import os

API_KEY = os.environ["FINETUNING_API_KEY"]
BASE = "https://pub.finetuning.ai"
headers = {"X-API-Key": API_KEY}

# Generate a track
res = requests.post(f"{BASE}/v1/generations", headers=headers, json={
    "tags": "jazz smooth saxophone evening",
    "duration": 90,
    "bpm": 100,
})
gen = res.json()["data"]
print(f"Generation started: {gen['id']}")

# Poll for completion
while True:
    r = requests.get(f"{BASE}/v1/generations/{gen['id']}", headers=headers)
    data = r.json()["data"]
    if data["status"] == "completed":
        print(f"Audio: {data['audioUrl']}")
        break
    elif data["status"] == "failed":
        print(f"Error: {data.get('errorMessage')}")
        break
    time.sleep(3)

Ready to build?

Create your account, upgrade to Pro, and start generating music through the API in minutes.