Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ Suno API currently mainly implements the following APIs:
- `/api/get_aligned_lyrics`: Get list of timestamps for each word in the lyrics
- `/api/clip`: Get clip information based on ID passed as query parameter `id`
- `/api/concat`: Generate the whole song from extensions
- `/api/projects`: Get a list of projects
```

You can also specify the cookies in the `Cookie` header of your request, overriding the default cookies in the `SUNO_COOKIE` environment variable. This comes in handy when, for example, you want to use multiple free accounts at the same time.
Expand Down
54 changes: 54 additions & 0 deletions src/app/api/projects/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { NextResponse, NextRequest } from 'next/server';
import { cookies } from 'next/headers';
import { sunoApi } from '@/lib/SunoApi';
import { corsHeaders } from '@/lib/utils';

export const dynamic = 'force-dynamic';

export async function GET(req: NextRequest) {
if (req.method === 'GET') {
try {
const url = new URL(req.url);
const page = url.searchParams.get('page') || '1';
const cookie = (await cookies()).toString();

const data = await (await sunoApi(cookie)).projects(parseInt(page));

return new NextResponse(JSON.stringify(data), {
status: 200,
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
} catch (error) {
console.error('Error fetching projects:', error);

return new NextResponse(
JSON.stringify({ error: 'Internal server error' }),
{
status: 500,
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
}
);
}
} else {
return new NextResponse('Method Not Allowed', {
headers: {
Allow: 'GET',
...corsHeaders
},
status: 405
});
}
}

export async function OPTIONS(request: Request) {
return new Response(null, {
status: 200,
headers: corsHeaders
});
}
81 changes: 81 additions & 0 deletions src/app/docs/swagger-suno-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,87 @@
}
}
},
"/api/projects": {
"get": {
"summary": "Get user's projects",
"description": "Retrieves the user's projects from Suno API.",
"tags": ["default"],
"parameters": [
{
"in": "query",
"name": "page",
"description": "Page number (defaults to 1)",
"required": false,
"schema": {
"type": "number",
"default": 1
}
}
],
"responses": {
"200": {
"description": "success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"projects": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Project ID"
},
"name": {
"type": "string",
"description": "Project name"
},
"created_at": {
"type": "string",
"description": "Creation date and time"
},
"last_modified": {
"type": "string",
"description": "Last modification date and time"
}
}
}
},
"total_results": {
"type": "integer",
"description": "Total number of projects"
},
"current_page": {
"type": "integer",
"description": "Current page number"
}
}
}
}
}
},
"500": {
"description": "Internal server error",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "string",
"example": "Internal server error"
}
}
}
}
}
}
}
}
},
"/api/get_limit": {
"get": {
"summary": "Get quota information.",
Expand Down
23 changes: 23 additions & 0 deletions src/lib/SunoApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,29 @@ class SunoApi {

return response.data;
}

/**
* Retrieves user's projects from Suno API.
* @param page An optional page number to retrieve projects from (default: 1).
* @returns A promise that resolves to the projects data from Suno.
*/
public async projects(page: number = 1): Promise<any> {
await this.keepAlive(false);

const url = `${SunoApi.BASE_URL}/api/project/me?page=${page}`;

logger.info(`Fetching projects data: ${url}`);

const response = await this.client.get(url, {
timeout: 10000 // 10 seconds timeout
});

if (response.status !== 200) {
throw new Error('Error response: ' + response.statusText);
}

return response.data;
}
}

export const sunoApi = async (cookie?: string) => {
Expand Down