Skip to content

Instantly share code, notes, and snippets.

@tobq
Last active June 12, 2025 04:00
Show Gist options
  • Save tobq/02c904192e1a76fca5dc6df445fa7726 to your computer and use it in GitHub Desktop.
Save tobq/02c904192e1a76fca5dc6df445fa7726 to your computer and use it in GitHub Desktop.

TwoShot Platform API Documentation

Overview

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

Use Cases & Integration Scenarios

🎵 Music Production Platform Integration

Build DAW plugins, web-based music studios, or mobile music creation apps.

Example Flow:

  1. User creates project → POST /project
  2. Import audio samples → POST /sample/audio
  3. Build project with tracks/clips → PUT /project/{id}
  4. Render final audio → POST /project/{id}/render

🤖 AI Music Generation App

Create applications that generate music using AI models.

Example Flow:

  1. Browse available models → GET /model
  2. Start generation job → POST /generation
  3. Poll for completion → GET /generation/{jobId}
  4. Download generated audio → GET /audio/{audioId}/download

📚 Sample Library Platform

Build sample discovery and distribution platforms.

Example Flow:

  1. Browse public samples → GET /sample
  2. Preview samples → GET /sample/{sampleId}/preview/{audioId}
  3. Apply licensing → POST /licence/apply
  4. Download licensed samples → GET /sample/{sampleId}/download/{audioId}

🎬 Content Creator Tools

Integrate music generation into video editing, podcasting, or streaming tools.

Example Flow:

  1. Generate background music → POST /generation
  2. Create video project → POST /video
  3. Sync audio with video → PUT /video/{id}
  4. Export final content → GET /video/{id}/download

🌐 Social Music Platform

Build social platforms for music sharing and collaboration.

Example Flow:

  1. User registration → POST /auth/register
  2. Share public projects → PUT /project/{id} (set public: true)
  3. Follow other users → POST /user/{userId}/follow
  4. Discover trending content → GET /tag/explore

Authentication

Authentication Methods

1. Bearer Token (JWT) - Primary Method

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

2. API Key

X-API-Key: your-api-key-here

Project Management

Create and manage music production projects with full DAW-like functionality.

Create Project

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 Projects

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 ID
  • public (boolean) - Filter by public projects only
  • name (string) - Search by project name
  • limit (integer, max 10, default 10) - Results per page
  • offset (integer, default 0) - Pagination offset
  • minTempo (number) - Minimum BPM filter
  • maxTempo (number) - Maximum BPM filter
  • minDuration (number) - Minimum duration in seconds
  • maxDuration (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());

Render Project

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"
}

Audio & Sample Management

Upload Sample Audio

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 Samples

GET /sample

Browse and search sample library.

Authentication: Optional

Query Parameters:

  • q (string) - Search query
  • owner_id (integer) - Filter by owner
  • public (boolean) - Public samples only
  • scale (string) - Musical scale filter
  • bpm_min (number) - Minimum BPM
  • bpm_max (number) - Maximum BPM
  • duration_min (number) - Minimum duration
  • duration_max (number) - Maximum duration
  • limit (integer) - Results per page
  • offset (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
  }
]

AI Generation & Models

Get Available Models

GET /model

Retrieve AI models for audio generation.

Authentication: Optional

Query Parameters:

  • category (string) - Filter by model category
  • sort (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
  }
]

Create Generation Job

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 Status

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
}

User Management

Get Current User

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"
}

Follow/Unfollow User

POST /user/{followedUserId}/follow
DELETE /user/{followedUserId}/follow

Authentication: Required

Response:

{
  "following": true,
  "followerCount": 1338
}

Licensing & Subscriptions

Get Available Licenses

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
    }
  }
]

Apply License

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
  }
}

Error Handling

Error Response Format

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"
}

HTTP Status Codes

  • 200 - Success
  • 201 - Created successfully
  • 400 - Bad Request (validation errors)
  • 401 - Unauthorized (invalid/missing token)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found
  • 429 - Rate Limited
  • 500 - Internal Server Error

Rate Limiting

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

SDK Examples

JavaScript/Node.js

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" }
]);

Python

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']}")

Complete Endpoint Reference

Click to expand all 61 public endpoints

Project Management (8 endpoints)

  • POST /project - Create project
  • GET /project - Get projects with filters
  • GET /project/{id} - Get specific project
  • PUT /project/{id} - Update project
  • DELETE /project/{id} - Delete project
  • POST /project/{id}/clone - Clone project
  • POST /project/{id}/render - Render project
  • POST /project/render - Render project data

Audio & Sample Management (17 endpoints)

  • GET /audio - Get audio files
  • GET /audio/{audioId} - Get audio metadata
  • GET /audio/{audioId}/play - Get playback URL
  • GET /audio/{audioId}/bpm - Get audio BPM
  • GET /audio/{audioId}/download - Download audio
  • GET /audio/{audioId}/stem - Get stem data
  • GET /audio/download - Bulk download
  • GET /sample - Browse samples
  • POST /sample/audio - Upload sample
  • GET /sample/{sampleId} - Get sample details
  • GET /sample/{sampleId}/preview/{audioId} - Preview sample
  • POST/DELETE /sample/{sampleId}/like - Like/unlike sample
  • GET /sample/{sampleId}/download/{audioId} - Download sample
  • PATCH /sample/{sampleId}/visibility - Update visibility
  • PATCH /sample/{sampleId}/rename - Rename sample
  • PATCH /sample/{sampleId}/art - Update artwork

AI Generation & Models (9 endpoints)

  • GET /model - Get available models
  • GET /model/preset - Get model presets
  • GET /model/tag - Get model tags
  • GET /model/{modelId} - Get model details
  • POST/DELETE /model/{modelId}/like - Like/unlike model
  • POST/DELETE /model/{modelId}/tag/{tag} - Tag/untag model
  • POST /generation - Create generation job
  • GET /generation - Get user generations
  • GET /generation/{jobId} - Get generation status

User Management (7 endpoints)

  • GET /user - Get current user
  • GET /user/info - Get user info
  • GET /user/referred - Get referred users
  • PATCH /user/picture - Update profile picture
  • PATCH /user/instagram - Update Instagram
  • GET /user/{profileId} - Get user profile
  • POST/DELETE /user/{userId}/follow - Follow/unfollow

And 20 more endpoint categories...


Support & Resources

Need Help?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment