@motosan-dev/sunoapi
v0.2.1
Published
TypeScript SDK for [sunoapi.org](https://sunoapi.org) — a third-party API for Suno AI music generation.
Readme
@motosan-dev/sunoapi
TypeScript SDK for sunoapi.org — a third-party API for Suno AI music generation.
Zero external dependencies, uses native fetch.
Installation
npm install @motosan-dev/sunoapiQuick Start
import { SunoApiClient } from "@motosan-dev/sunoapi";
const client = new SunoApiClient({
apiKey: "your-api-key",
});
// Generate music and wait for completion
const result = await client.generateAndWait({
prompt: "A chill lo-fi hip hop beat for studying",
customMode: false,
instrumental: true,
});
console.log(result.response?.sunoData);Initialization
const client = new SunoApiClient({
apiKey: "your-api-key", // Required. Get from sunoapi.org
baseUrl: "https://api.sunoapi.org", // Optional. Default value
callbackUrl: "https://your-webhook.com/callback", // Optional. Global default callback URL
pollingInterval: 5000, // Optional. Polling interval (ms). Default: 5000
pollingTimeout: 120000, // Optional. Polling timeout (ms). Default: 120000
});callBackUrl Priority
- Method parameter — each request can specify its own
- Config global setting —
new SunoApiClient({ callbackUrl: "..." }) - Built-in placeholder —
https://example.com/callbackwhen none is provided
API
Music Generation
generate(params) → GenerateTaskResult
Submit a music generation task. Returns a taskId. Each request produces 2 tracks. Non-blocking.
const { taskId } = await client.generate({
prompt: "Japanese city pop with funky bass",
customMode: false,
instrumental: false,
model: "V4", // Optional. Default: "V4"
style: "City Pop", // Optional
title: "Tokyo Nights", // Optional
negativeTags: "heavy metal", // Optional
vocalGender: "f", // Optional. "m" | "f"
styleWeight: 0.8, // Optional. 0.0-1.0
callBackUrl: "https://...", // Optional
});generateAndWait(params) → MusicGenerationRecord
Generate music and automatically poll until completion. Blocking — may take several minutes.
const record = await client.generateAndWait({
prompt: "Upbeat electronic dance music",
customMode: false,
instrumental: true,
});
for (const audio of record.response?.sunoData ?? []) {
console.log(audio.audioUrl, audio.duration);
}extend(params) → GenerateTaskResult
Extend an existing music track. Non-blocking.
const { taskId } = await client.extend({
audioId: "audio-id-from-generation",
defaultParamFlag: true,
model: "V4",
prompt: "Continue with a guitar solo",
continueAt: 30.0, // Start extension at 30 seconds
});extendAndWait(params) → MusicGenerationRecord
Extend music and automatically poll until completion. Blocking.
const record = await client.extendAndWait({
audioId: "audio-id",
defaultParamFlag: false, // Reuse original settings
model: "V4",
});getMusicStatus(taskId) → MusicGenerationRecord
Query music generation task status. Non-blocking.
const record = await client.getMusicStatus("task-id");
console.log(record.status);
// "PENDING" | "TEXT_SUCCESS" | "FIRST_SUCCESS" | "SUCCESS"
// | "CREATE_TASK_FAILED" | "GENERATE_AUDIO_FAILED" | "SENSITIVE_WORD_ERROR"Lyrics Generation
generateLyrics(params) → GenerateTaskResult
Submit a lyrics generation task (text only, no audio). Non-blocking.
const { taskId } = await client.generateLyrics({
prompt: "A love song about missing someone in the rain",
});generateLyricsAndWait(params) → LyricsGenerationRecord
Generate lyrics and automatically poll until completion. Blocking.
const record = await client.generateLyricsAndWait({
prompt: "A love song about missing someone in the rain",
});
console.log(record.response?.title);
console.log(record.response?.lyrics);getLyricsStatus(taskId) → LyricsGenerationRecord
Query lyrics generation task status. Non-blocking.
const record = await client.getLyricsStatus("task-id");WAV Conversion
convertToWav(params) → GenerateTaskResult
Submit a WAV conversion task for an existing track. Non-blocking.
const { taskId } = await client.convertToWav({
taskId: "original-task-id",
audioId: "audio-id",
});convertToWavAndWait(params) → WavConversionRecord
Convert to WAV and automatically poll until completion. Blocking.
const record = await client.convertToWavAndWait({
taskId: "original-task-id",
audioId: "audio-id",
});
console.log(record.response?.audioWavUrl); // WAV download URLgetWavStatus(taskId) → WavConversionRecord
Query WAV conversion task status. Non-blocking.
const record = await client.getWavStatus("wav-task-id");
// record.successFlag: "PENDING" | "SUCCESS" | "CREATE_TASK_FAILED" | "GENERATE_WAV_FAILED" | "CALLBACK_EXCEPTION"Credits
getCredits() → number
Get the remaining API credit balance. Non-blocking.
const credits = await client.getCredits();
console.log(`Remaining credits: ${credits}`);Error Handling
import {
SunoApiError,
SunoApiTimeoutError,
SunoApiGenerationError,
SunoApiValidationError,
} from "@motosan-dev/sunoapi";
try {
await client.generateAndWait({ ... });
} catch (error) {
if (error instanceof SunoApiValidationError) {
// Invalid parameters — check error.message for details
} else if (error instanceof SunoApiTimeoutError) {
// Polling timed out — use error.taskId to check status later
} else if (error instanceof SunoApiGenerationError) {
// Generation failed (e.g. GENERATE_AUDIO_FAILED)
console.log(error.status);
} else if (error instanceof SunoApiError) {
// API error (HTTP error or response code !== 200)
console.log(error.code);
}
}| Error Class | Trigger |
|-------------|---------|
| SunoApiValidationError | Invalid request parameters |
| SunoApiError | API response code !== 200, or HTTP non-2xx |
| SunoApiTimeoutError | Polling exceeded pollingTimeout. Includes taskId for retry |
| SunoApiGenerationError | Terminal failure status (e.g. GENERATE_AUDIO_FAILED) |
Types
All types are exported directly from the package:
import type {
SunoModel, // "V4" | "V4_5" | "V4_5PLUS" | "V4_5ALL" | "V5"
GenerateMusicParams,
ExtendMusicParams,
GenerateLyricsParams,
ConvertToWavParams,
GenerateTaskResult,
MusicGenerationRecord,
MusicGenerationStatus,
LyricsGenerationRecord,
WavConversionRecord,
WavConversionStatus,
CreditsInfo,
SunoAudioData,
SunoApiClientConfig,
} from "@motosan-dev/sunoapi";