The TwoShot Platform API is a comprehensive REST API for music production, AI-powered audio generation, and sample library management. Built for developers creating music production tools, AI audio applications, and creative platforms.
Base URLs:
- Production:
https://api.twoshot.app
API Version: v1
Build DAW plugins, web-based music studios, or mobile music creation apps.
Example Flow:
- User creates project →
POST /project
- Import audio samples →
POST /sample/audio
- Build project with tracks/clips →
PUT /project/{id}
- Render final audio →
POST /project/{id}/render
Create applications that generate music using AI models.
Example Flow:
- Browse available models →
GET /model
- Start generation job →
POST /generation
- Poll for completion →
GET /generation/{jobId}
- Download generated audio →
GET /audio/{audioId}/download
Build sample discovery and distribution platforms.
Example Flow:
- Browse public samples →
GET /sample
- Preview samples →
GET /sample/{sampleId}/preview/{audioId}
- Apply licensing →
POST /licence/apply
- Download licensed samples →
GET /sample/{sampleId}/download/{audioId}
Integrate music generation into video editing, podcasting, or streaming tools.
Example Flow:
- Generate background music →
POST /generation
- Create video project →
POST /video
- Sync audio with video →
PUT /video/{id}
- Export final content →
GET /video/{id}/download
Build social platforms for music sharing and collaboration.
Example Flow:
- User registration →
POST /auth/register
- Share public projects →
PUT /project/{id}
(set public: true) - Follow other users →
POST /user/{userId}/follow
- Discover trending content →
GET /tag/explore
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
X-API-Key: your-api-key-here
Create and manage music production projects with full DAW-like functionality.
POST /project
Create a new music project with tracks, clips, and audio sources.
Authentication: Required
Request Body:
{
"name": "My New Track",
"version": "1.0.0",
"tempo": 120.0,
"grid": 0.25,
"snapToGrid": true,
"playHead": {
"start": 0.0,
"end": 16.0
},
"soloTarget": null,
"tracks": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Drums",
"clips": [
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Kick Pattern",
"color": { "hue": 0.8, "saturation": 0.7, "lightness": 0.5 },
"position": 0.0,
"muted": false,
"volume": 0.8,
"source": {
"audioId": "550e8400-e29b-41d4-a716-446655440003",
"sampleId": 12345
},
"pitchOffset": 0.0,
"reversed": false,
"speed": "tempo-sync",
"cutStart": 0.0,
"cutEnd": 2.5,
"pan": 0.0
}
],
"pan": 0.0,
"loopEvery": "auto",
"volume": 0.9,
"muted": false
}
]
}
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}
Example Usage:
const project = await fetch('/project', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: "My Beat",
tempo: 140,
tracks: []
})
});
const { id } = await project.json();
console.log('Created project:', id);
GET /project
Retrieve projects with filtering, pagination, and search capabilities.
Authentication: Optional (returns public projects for anonymous users)
Query Parameters:
owner_id
(integer) - Filter by owner IDpublic
(boolean) - Filter by public projects onlyname
(string) - Search by project namelimit
(integer, max 10, default 10) - Results per pageoffset
(integer, default 0) - Pagination offsetminTempo
(number) - Minimum BPM filtermaxTempo
(number) - Maximum BPM filterminDuration
(number) - Minimum duration in secondsmaxDuration
(number) - Maximum duration in seconds
Response:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"owner": {
"id": 12345,
"userName": "musicmaker2024",
"verified": true,
"featured": false,
"displayPicture": {
"id": 67890,
"url": "https://cdn.twoshot.app/images/profile_67890.jpg"
},
"followed": false
},
"public": true,
"name": "Epic House Track",
"version": "2.1.0",
"created": "2024-01-15T10:30:00Z",
"updated": "2024-01-16T14:45:30Z",
"data": {
"tempo": 128.0,
"grid": 0.25,
"tracks": [...]
}
}
]
Example Usage:
// Get public projects with tempo filter
const projects = await fetch('/project?public=true&minTempo=120&maxTempo=140&limit=10')
.then(r => r.json());
// Get user's own projects
const myProjects = await fetch('/project?owner_id=12345', {
headers: { 'Authorization': `Bearer ${token}` }
}).then(r => r.json());
POST /project/{id}/render
Render project to audio file.
Authentication: Required
Request Body:
{
"format": "wav",
"quality": "high",
"startTime": 0.0,
"endTime": 60.0
}
Response:
{
"jobId": "render-job-uuid",
"status": "processing",
"estimatedCompletion": "2024-01-15T10:35:00Z"
}
POST /sample/audio
Upload audio file to create a new sample.
Authentication: Required
Request: Multipart form data
Content-Type: multipart/form-data
file: [audio file binary]
name: "My Sample"
public: true
scale: "C Major"
Response:
{
"sampleId": 12345,
"audioId": "550e8400-e29b-41d4-a716-446655440000",
"processing": true
}
Example Usage:
const formData = new FormData();
formData.append('file', audioFile);
formData.append('name', 'My Drum Loop');
formData.append('public', 'true');
const response = await fetch('/sample/audio', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: formData
});
GET /sample
Browse and search sample library.
Authentication: Optional
Query Parameters:
q
(string) - Search queryowner_id
(integer) - Filter by ownerpublic
(boolean) - Public samples onlyscale
(string) - Musical scale filterbpm_min
(number) - Minimum BPMbpm_max
(number) - Maximum BPMduration_min
(number) - Minimum durationduration_max
(number) - Maximum durationlimit
(integer) - Results per pageoffset
(integer) - Pagination offset
Response:
[
{
"sample": {
"id": 12345,
"name": "Deep House Bass",
"created": "2024-01-15T10:30:00Z",
"audioId": "550e8400-e29b-41d4-a716-446655440000",
"ownerId": 67890,
"parentFolderId": 555,
"public": true,
"scale": "A Minor",
"imageId": 123
},
"owner": {
"id": 67890,
"userName": "bassmaster",
"verified": true,
"displayPicture": {
"url": "https://cdn.twoshot.app/images/profile_67890.jpg"
}
},
"tags": ["house", "bass", "deep", "electronic"],
"audioInfo": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"duration": 8.0,
"bpm": 125.0,
"free": false,
"licenceId": 789
},
"liked": false,
"likes": 42
}
]
GET /model
Retrieve AI models for audio generation.
Authentication: Optional
Query Parameters:
category
(string) - Filter by model categorysort
(enum) - Sort order:latest
,oldest
,popular
,random
,recently_used
limit
(integer) - Results per page
Response:
[
{
"id": 101,
"name": "MusicGen Large",
"description": "High-quality music generation model",
"category": "music_generation",
"tags": ["music", "ai", "generation"],
"inputTypes": ["text", "audio"],
"outputTypes": ["audio"],
"maxDuration": 30.0,
"pricing": {
"free": false,
"credits": 5
},
"parameters": [
{
"name": "prompt",
"type": "text",
"required": true,
"description": "Describe the music you want to generate"
},
{
"name": "duration",
"type": "number",
"required": false,
"default": 10.0,
"min": 5.0,
"max": 30.0
}
],
"liked": false,
"likes": 1337
}
]
POST /generation
Start AI audio generation job.
Authentication: Required
Request Body:
{
"modelId": 101,
"inputs": [
{
"type": "text",
"value": "Upbeat electronic dance music with heavy bass"
},
{
"type": "number",
"value": 15.0
},
{
"type": "audio",
"audioId": "550e8400-e29b-41d4-a716-446655440000",
"trimStart": 0.0,
"trimEnd": 10.0
}
]
}
Response:
{
"jobId": "550e8400-e29b-41d4-a716-446655440099",
"status": "pending",
"estimatedCompletion": "2024-01-15T10:35:00Z",
"creditsUsed": 5
}
Example Usage:
// Generate music from text prompt
const generation = await fetch('/generation', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
modelId: 101,
inputs: [
{ type: "text", value: "Chill lo-fi hip hop beat" },
{ type: "number", value: 20.0 }
]
})
});
const { jobId } = await generation.json();
// Poll for completion
const checkStatus = async () => {
const status = await fetch(`/generation/${jobId}`)
.then(r => r.json());
if (status.status === 'completed') {
console.log('Generated audio:', status.outputs);
} else if (status.status === 'failed') {
console.error('Generation failed:', status.error);
} else {
setTimeout(checkStatus, 5000); // Check again in 5 seconds
}
};
checkStatus();
GET /generation/{jobId}
Check status of generation job.
Authentication: Optional
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440099",
"status": "completed",
"progress": 100,
"created": "2024-01-15T10:30:00Z",
"completed": "2024-01-15T10:32:15Z",
"model": {
"id": 101,
"name": "MusicGen Large"
},
"inputs": [
{
"type": "text",
"value": "Upbeat electronic dance music"
}
],
"outputs": [
{
"audioId": "550e8400-e29b-41d4-a716-446655440100",
"duration": 15.0,
"format": "wav"
}
],
"error": null,
"creditsUsed": 5
}
GET /user
Authentication: Required
Response:
{
"id": 12345,
"userName": "musicmaker2024",
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"created": "2024-01-01T00:00:00Z",
"displayPicture": {
"id": 67890,
"url": "https://cdn.twoshot.app/images/profile_67890.jpg"
},
"verified": true,
"featured": false,
"instagram": "johnmusic",
"soundcloudUrl": "https://soundcloud.com/johnmusic"
}
POST /user/{followedUserId}/follow
DELETE /user/{followedUserId}/follow
Authentication: Required
Response:
{
"following": true,
"followerCount": 1338
}
GET /licence
Authentication: Optional
Response:
[
{
"id": 1,
"name": "Standard License",
"description": "Use in commercial projects up to 1M streams",
"price": 9.99,
"currency": "USD",
"terms": {
"commercial": true,
"streaming_limit": 1000000,
"attribution_required": false
}
}
]
POST /licence/apply
Apply license to content for usage rights.
Authentication: Required
Request Body:
{
"licenceId": 1,
"contentId": "550e8400-e29b-41d4-a716-446655440000",
"contentType": "sample"
}
Response:
{
"licenseApplicationId": "license-app-uuid",
"status": "approved",
"validUntil": "2025-01-15T10:30:00Z",
"terms": {
"commercial": true,
"streaming_limit": 1000000
}
}
All errors return JSON with consistent structure:
{
"error": "Invalid request parameters",
"code": "VALIDATION_ERROR",
"details": {
"field": "tempo",
"message": "Tempo must be between 60 and 200 BPM"
},
"timestamp": "2024-01-15T10:30:00Z",
"path": "/project"
}
200
- Success201
- Created successfully400
- Bad Request (validation errors)401
- Unauthorized (invalid/missing token)403
- Forbidden (insufficient permissions)404
- Not Found429
- Rate Limited500
- Internal Server Error
API requests are rate-limited per user/IP:
Headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 995
X-RateLimit-Reset: 1642252800
Rate Limits:
- Anonymous: 100 requests/hour
- Authenticated: 1000 requests/hour
- Premium: 5000 requests/hour
class TwoShotAPI {
constructor(token) {
this.token = token;
this.baseURL = 'https://api.twoshot.app';
}
async request(endpoint, options = {}) {
const response = await fetch(`${this.baseURL}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json();
}
// Create project
async createProject(projectData) {
return this.request('/project', {
method: 'POST',
body: JSON.stringify(projectData)
});
}
// Generate audio
async generateAudio(modelId, inputs) {
return this.request('/generation', {
method: 'POST',
body: JSON.stringify({ modelId, inputs })
});
}
// Upload sample
async uploadSample(file, metadata) {
const formData = new FormData();
formData.append('file', file);
Object.entries(metadata).forEach(([key, value]) => {
formData.append(key, value);
});
return this.request('/sample/audio', {
method: 'POST',
body: formData,
headers: {} // Let browser set Content-Type for FormData
});
}
}
// Usage
const api = new TwoShotAPI('your-token-here');
// Create a project
const project = await api.createProject({
name: "My Track",
tempo: 128,
tracks: []
});
// Generate AI audio
const generation = await api.generateAudio(101, [
{ type: "text", value: "Upbeat house music" }
]);
import requests
import json
class TwoShotAPI:
def __init__(self, token):
self.token = token
self.base_url = 'https://api.twoshot.app'
self.headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
def request(self, endpoint, method='GET', data=None, files=None):
url = f'{self.base_url}{endpoint}'
headers = self.headers.copy()
if files:
headers.pop('Content-Type') # Let requests set it for multipart
response = requests.request(
method=method,
url=url,
headers=headers,
json=data if not files else None,
files=files
)
response.raise_for_status()
return response.json()
def create_project(self, project_data):
return self.request('/project', 'POST', project_data)
def generate_audio(self, model_id, inputs):
return self.request('/generation', 'POST', {
'modelId': model_id,
'inputs': inputs
})
def upload_sample(self, file_path, name, public=True):
with open(file_path, 'rb') as f:
files = {'file': f}
data = {'name': name, 'public': str(public).lower()}
return self.request('/sample/audio', 'POST', files=files)
# Usage
api = TwoShotAPI('your-token-here')
# Generate audio
generation = api.generate_audio(101, [
{"type": "text", "value": "Chill lo-fi beat"}
])
print(f"Generation started: {generation['jobId']}")
Click to expand all 61 public endpoints
POST /project
- Create projectGET /project
- Get projects with filtersGET /project/{id}
- Get specific projectPUT /project/{id}
- Update projectDELETE /project/{id}
- Delete projectPOST /project/{id}/clone
- Clone projectPOST /project/{id}/render
- Render projectPOST /project/render
- Render project data
GET /audio
- Get audio filesGET /audio/{audioId}
- Get audio metadataGET /audio/{audioId}/play
- Get playback URLGET /audio/{audioId}/bpm
- Get audio BPMGET /audio/{audioId}/download
- Download audioGET /audio/{audioId}/stem
- Get stem dataGET /audio/download
- Bulk downloadGET /sample
- Browse samplesPOST /sample/audio
- Upload sampleGET /sample/{sampleId}
- Get sample detailsGET /sample/{sampleId}/preview/{audioId}
- Preview samplePOST/DELETE /sample/{sampleId}/like
- Like/unlike sampleGET /sample/{sampleId}/download/{audioId}
- Download samplePATCH /sample/{sampleId}/visibility
- Update visibilityPATCH /sample/{sampleId}/rename
- Rename samplePATCH /sample/{sampleId}/art
- Update artwork
GET /model
- Get available modelsGET /model/preset
- Get model presetsGET /model/tag
- Get model tagsGET /model/{modelId}
- Get model detailsPOST/DELETE /model/{modelId}/like
- Like/unlike modelPOST/DELETE /model/{modelId}/tag/{tag}
- Tag/untag modelPOST /generation
- Create generation jobGET /generation
- Get user generationsGET /generation/{jobId}
- Get generation status
GET /user
- Get current userGET /user/info
- Get user infoGET /user/referred
- Get referred usersPATCH /user/picture
- Update profile picturePATCH /user/instagram
- Update InstagramGET /user/{profileId}
- Get user profilePOST/DELETE /user/{userId}/follow
- Follow/unfollow
- API Status: status.twoshot.app
- Community Discord: twoshot.app/discord
- GitHub Issues: Report bugs and feature requests
- Rate Limits: Check current limits in response headers
Need Help?
- Technical questions: [email protected]
- Business inquiries: [email protected]
- Bug reports: Create an issue on GitHub