tunova-ai Generation API

Create songs (async)

Operations 2

POST /api/generate Generate a song from a prompt #
POST /api/custom_generate Generate a song from your own lyrics + style #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/tunova-ai:tunova-ai-generation-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

tunova-ai-generation-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Tunova Generation API
  version: 1.0.0
  description: 'Suno-AI music generation API. Generation is **always async**: submit returns `202 {job_id}`; poll `GET /api/jobs/{id}` or pass `callback_url` for an HMAC-signed webhook. Billed in **tokens, on success only** (a failed render is refunded). Every response carries `X-Request-Id`. An **MCP server** for AI agents (Claude, Cursor, …) is also available at `POST /mcp` (stateless Streamable HTTP; tools `generate_song` / `wait_for_song` / `check_song`) — same `X-API-Key`, same token billing.'
servers:
- url: https://api.tunova.ai
  description: production
security:
- ApiKeyAuth: []
tags:
- name: generation
  description: Create songs (async)
paths:
  /api/generate:
    post:
      tags:
      - generation
      operationId: generateSong
      summary: Generate a song from a prompt
      description: 'Simple mode: a text description. When generation capacity is available, returns `202` + a durable `job_id`. Keep polling the returned job; after admission, an uncertain transport outcome is reconciled to completion or failure/refund. When neither the selected session route nor a legacy BOX/account pair is dispatch-ready, returns `503` before creating a job or reserving tokens. Costs 10 tokens, settled on success.'
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GenerateRequest'
      responses:
        '202':
          $ref: '#/components/responses/JobAccepted'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '402':
          $ref: '#/components/responses/InsufficientTokens'
        '403':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/GenerationUnavailable'
  /api/custom_generate:
    post:
      tags:
      - generation
      operationId: customGenerate
      summary: Generate a song from your own lyrics + style
      description: 'Custom mode: supply `prompt` (your lyrics) plus `tags` (style) and `title`. When generation capacity is available, returns `202` + a durable `job_id`. Keep polling the returned job; after admission, an uncertain transport outcome is reconciled to completion or failure/refund. When neither the selected session route nor a legacy BOX/account pair is dispatch-ready, returns `503` before creating a job or reserving tokens. Costs 10 tokens, settled on success.'
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomGenerateRequest'
      responses:
        '202':
          $ref: '#/components/responses/JobAccepted'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '402':
          $ref: '#/components/responses/InsufficientTokens'
        '403':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/GenerationUnavailable'
components:
  responses:
    InsufficientTokens:
      description: 402 — token balance too low (billed on success only)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    IdempotencyConflict:
      description: 409 — Idempotency-Key is already bound to a different canonical generation request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Error:
      description: Error envelope
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    GenerationUnavailable:
      description: 503 — no dispatch-ready generation route. No job was created and no tokens were reserved; retry with the same Idempotency-Key after the indicated delay.
      headers:
        Retry-After:
          description: Seconds to wait before retrying
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: 429 — per-key rate limit (60 requests/min, sliding 60s window). When limiter state is available, `RateLimit-Limit`/`RateLimit-Remaining`/`RateLimit-Reset` are returned on rate-checked generation/lyrics accepts and 429 responses. They are omitted during a fail-open limiter outage and are not promised on auth/validation errors, status reads, or idempotent replay. `RateLimit-Reset` is a delay in seconds (0–60), not a Unix timestamp. On 429 also honor `Retry-After`.
      headers:
        Retry-After:
          description: Seconds to wait before retrying
          schema:
            type: integer
            minimum: 1
            maximum: 60
        RateLimit-Limit:
          description: Requests allowed in the sliding window
          schema:
            type: integer
        RateLimit-Remaining:
          description: Requests remaining in the current sliding window
          schema:
            type: integer
        RateLimit-Reset:
          description: Seconds until quota resets (delay, not Unix time)
          schema:
            type: integer
            minimum: 0
            maximum: 60
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    JobAccepted:
      description: Accepted — generation is async. Keep polling the returned job; a queued job may proceed normally or, if transport delivery was truly lost, fail and refund automatically (normally within about 30–45 minutes).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/JobAccepted'
  schemas:
    Error:
      type: object
      required:
      - code
      - detail
      properties:
        code:
          type: string
          example: VALIDATION_ERROR
        detail:
          type: string
        request_id:
          type: string
          description: Echoed in the X-Request-Id header; quote it in support requests
        balance:
          type: integer
          description: 'On 402 (INSUFFICIENT_TOKENS / DAILY_CAP_REACHED): your current token balance'
        cost:
          type: integer
          description: 'On 402: tokens this call needs'
        job_id:
          type: string
          description: 'On 409 IDEMPOTENCY_CONFLICT: the retained job already bound to this key'
    GenerateRequest:
      type: object
      required:
      - prompt
      example:
        prompt: calm rainy-night lofi
        model: v6
      properties:
        prompt:
          type: string
          description: Free-text description of the song
        make_instrumental:
          type: boolean
          default: false
        model:
          type: string
          enum:
          - v6
          - v5.5
          description: 'Suno model: v6, or v5.5 as a compatibility alias for v6. Both generate v6 at the same token cost. Omitted model keeps the existing account default.'
        callback_url:
          type: string
          format: uri
          description: Public http(s) URL for the completion webhook
        streaming_callback:
          type: boolean
          default: false
          description: 'Opt in to an additional signed `status: streaming` preview webhook. Requires callback_url; it may also be enabled as an account default. Terminal complete/failed webhooks are unchanged.'
    JobAccepted:
      type: object
      description: 'The job was recorded; for billed customer keys, its token cost was reserved. `queued` is also returned when generation-transport delivery is uncertain: poll `status_url` rather than submitting a duplicate. The job may still proceed normally; if it was truly not delivered, it is marked `failed` and refunded, normally within about 30–45 minutes.'
      example:
        job_id: 8f3a1c2e-7b9d-4c2a-9e5f-1a2b3c4d5e6f
        status: queued
        status_url: /api/jobs/8f3a1c2e-7b9d-4c2a-9e5f-1a2b3c4d5e6f
      properties:
        job_id:
          type: string
        status:
          type: string
          example: queued
        status_url:
          type: string
          example: /api/jobs/…
        idempotent_replay:
          type: boolean
          description: Present when an Idempotency-Key matched an existing job
    CustomGenerateRequest:
      type: object
      required:
      - prompt
      example:
        prompt: '[Verse]

          Rain taps the window, slow and low

          [Chorus]

          Stay a while, let the night go'
        tags: lofi hip-hop, rainy, mellow
        title: Rain On Glass
        model: v6
      properties:
        prompt:
          type: string
          description: Your lyrics. CRLF/CR line endings are normalized to LF and outer whitespace is trimmed; interior whitespace is preserved.
        tags:
          type: string
          description: Style / genre tags
        title:
          type: string
        make_instrumental:
          type: boolean
          default: false
        model:
          type: string
          enum:
          - v6
          - v5.5
          description: 'Suno model: v6, or v5.5 as a compatibility alias for v6. Both generate v6 at the same token cost. Omitted model keeps the existing account default.'
        callback_url:
          type: string
          format: uri
          description: Public http(s) URL for the completion webhook
        streaming_callback:
          type: boolean
          default: false
          description: 'Opt in to an additional signed `status: streaming` preview webhook. Requires callback_url; it may also be enabled as an account default. Preview audio is transient and not yet quality-verified.'
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        maxLength: 128
      description: Reuse for retries of one logical submission to get the same job and avoid duplicate renders/charges. Scoped to your account and bound to the accepted canonical request. Reusing a key with a different prompt, mode, model, generation options, or callback URL returns `409 IDEMPOTENCY_CONFLICT`; use a new key for new work. Maximum 128 UTF-8 bytes. The equivalent v6 and v5.5 model aliases replay the same job when all other fields match. While the job is retained (normally about 7 days), the key replays it even after terminal state; to start a new generation after `failed`, send a NEW Idempotency-Key. After retention expires, the old key may create a new job.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Per-customer key, e.g. sk_live_…