> ## Documentation Index
> Fetch the complete documentation index at: https://docs.deapi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Text-to-Music

> Generate music from a text prompt.

Generate music tracks from text with control over lyrics, tempo, key, and style. Returns a `request_id` for status polling.

<Note>
  **Prerequisite:** Consult the [Model Selection](/api/v2/utilities/models) endpoint to identify a valid model `slug` and check its parameter limits. Model slugs look like `AceStep_1_5_Turbo` — always take them from the models endpoint rather than hard-coding a display name.
</Note>

## Field values worth knowing

These constraints are enforced by validation and are easy to trip over:

| Field | Constraint |
| - | - |
| `lyrics` | **Required and must be non-empty**, including for instrumental tracks. Omitting it or sending `""` returns `422 The lyrics field is required.` For an instrumental track, send `"[Instrumental]"`. |
| `timesignature` | **Integer only**, and only `2`, `3`, `4` or `6` are accepted. A notation string such as `"4/4"` returns `422 The timesignature field must be an integer.` Map the numerator of an x/4 signature (4/4 → `4`, 3/4 → `3`, 2/4 → `2`, 6/8 → `6`); 5/4 and 7/8 cannot be expressed. Omit the field or send an empty value to let the model choose. |
| `format` | Use `mp3` — the only format the current models actually produce (`info.limits.output_formats`). `wav` and `flac` are rejected with `422 This output format is not supported by this model. Available formats: mp3.` An unrecognised value such as `ogg` returns `422 The selected format is invalid.` |
| `keyscale` | Free-form string (`"C major"`, `"D minor"`, `"F# minor"`). Not validated against a fixed list — passed to the model as a hint. |
| `vocal_language` | Free-form string. Both ISO codes (`"en"`) and language names (`"English"`) are accepted. Not validated against a fixed list. |

<Warning>
  `timesignature` is validated as an **integer**, not as musical notation. If you populate it from a UI dropdown offering values like `4/4` or `6/8`, convert to the integer before sending — otherwise only the requests that set the field fail, while the rest keep succeeding.
</Warning>


## OpenAPI

````yaml openapi-v2.json POST /api/v2/audio/music
openapi: 3.1.0
info:
  title: deAPI REST API
  description: >-
    Decentralized AI inference API for image generation, video processing, audio
    transcription, and more.


    ## Rate limits


    Two windows can apply to a request: a per-minute one (RPM) and a per-day one
    (RPD). Every

    throttled response — success or 429 — carries the `X-RateLimit-*` headers,
    so you can pace

    traffic without waiting to be rejected. Each header documents itself: see
    `components/headers`,

    or the headers listed on any response. What follows is only what a header
    cannot say about

    itself.


    Windows open on your first request into a bucket and run their full length
    from that second —

    never to a calendar boundary, in any timezone. That is deliberate: a
    boundary every account

    shares is a boundary every account can double up on.


    **The headers describe the pool *this* request landed in, and
    `X-RateLimit-Source` is what

    identifies it — not `X-RateLimit-Limit` alone.** A per-model limit is keyed
    to the model you

    named, so a request that carried no recognised `model` — rejected at
    validation, an unknown

    slug, an empty body — was counted against your tier pool, and it is the tier
    pool the headers

    then describe. The two pools are fully independent: separate minute window,
    separate day,

    separate `X-RateLimit-Daily-Reset`. This is a consequence of rate limiting
    running *before*

    validation, which is what keeps malformed traffic from reaching the rest of
    the API

    uncounted. Compare headers only across responses carrying the same
    `X-RateLimit-Source`.


    **Buckets are shared in ways worth knowing.** API **v1 and v2 count against
    the same bucket**,

    so migrating grants no fresh allowance and mixed traffic behaves as one
    stream. All `*/price`

    and `*/price-calculation` endpoints share a single quote bucket, separate
    from generation.

    Prompt enhancement (`/v2/prompts/enhancements`) has its own bucket as well,
    so heavy prompt

    work no longer eats into your image throughput.


    **Counting is exact in sequence, best-effort in parallel.** Sent one after
    another, your

    requests are counted precisely: the one that crosses the limit is the one
    that gets 429. Sent

    in parallel, a few beyond the limit can slip through, because the counter is
    read and then

    incremented as two steps and requests already in flight are not yet visible
    to one another.

    The excess is bounded by how many of your own requests are in flight at
    once, and it only

    ever runs in your favour — you are never rejected while below your limit. So
    do not treat

    `X-RateLimit-Remaining` as a reservation: it is a snapshot, and a request
    you sent a

    millisecond ago may already have spent what it shows.


    Requests rejected before authentication (HTTP 401) never reach the limiter
    and carry no

    rate-limit headers at all.
  contact:
    name: deAPI Support
    url: https://deapi.ai
    email: support@deapi.ai
  version: 0.0.1
servers:
  - url: https://api.deapi.ai
    description: Production API Server base URL
security:
  - bearerAuth: []
tags:
  - name: Client API v2
    description: Current client endpoints (OpenAI-aligned noun-based paths)
paths:
  /api/v2/audio/music:
    post:
      tags:
        - Client API v2
      summary: Generate music from a text prompt
      description: Generate music from a text prompt.
      operationId: generateMusic
      parameters:
        - $ref: '#/components/parameters/AcceptHeader'
      requestBody:
        description: Music generation parameters
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Txt2MusicRequestBody'
      responses:
        '200':
          description: ID of the inference request.
          headers:
            X-RateLimit-Policy:
              $ref: '#/components/headers/X-RateLimit-Policy'
            X-RateLimit-Source:
              $ref: '#/components/headers/X-RateLimit-Source'
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
            X-RateLimit-Daily-Limit:
              $ref: '#/components/headers/X-RateLimit-Daily-Limit'
            X-RateLimit-Daily-Remaining:
              $ref: '#/components/headers/X-RateLimit-Daily-Remaining'
            X-RateLimit-Daily-Reset:
              $ref: '#/components/headers/X-RateLimit-Daily-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobRequestResponseResource'
        '401':
          description: Unauthorized user.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/response_error_default'
        '403':
          $ref: '#/components/responses/AccountSuspended'
        '404':
          description: Model not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/response_error_default'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
      security:
        - bearerAuth: []
components:
  parameters:
    AcceptHeader:
      name: Accept
      in: header
      required: true
      schema:
        type: string
        default: application/json
        enum:
          - application/json
  schemas:
    Txt2MusicRequestBody:
      description: >-
        Every example in this schema is a working set for the `model` example
        shown — they are not defaults and not portable. Sizes, step counts,
        frame counts and frame rates are all per-model: read `info.limits` and
        `info.defaults` for the model you actually intend to use from `GET
        /api/v2/models`. Substituting a different `model` while keeping these
        values is the most common source of a 422.
      required:
        - caption
        - model
        - lyrics
        - duration
        - inference_steps
        - guidance_scale
        - seed
        - format
      properties:
        caption:
          description: Text description of the music to generate
          type: string
          example: upbeat electronic dance music with energetic synths
        model:
          description: >-
            The model to use for music generation. Available models can be
            retrieved via the GET /api/v2/models endpoint.
          type: string
          example: AceStep_1_5_Turbo
        lyrics:
          description: >-
            Lyrics for the music. Use "[Instrumental]" for instrumental tracks
            without vocals.
          type: string
          example: '[Instrumental]'
        duration:
          description: >-
            Duration in seconds (10-600). The accepted range is model-specific —
            read `info.limits.min_duration`/`max_duration` for your `model` from
            `GET /api/v2/models`. Some models additionally cap duration per
            output format via `info.limits.max_duration_by_format`.
          type: number
          example: 30
        bpm:
          description: Beats per minute (30-300)
          type:
            - integer
            - 'null'
          example: 120
        keyscale:
          description: Musical key/scale (e.g. "C major", "F# minor")
          type:
            - string
            - 'null'
          example: C major
        timesignature:
          description: Time signature. Must be 2, 3, 4, or 6.
          type:
            - integer
            - 'null'
          example: 4
        vocal_language:
          description: Language code for vocals (e.g. "en", "es", "fr")
          type:
            - string
            - 'null'
          example: en
        inference_steps:
          description: >-
            Number of diffusion inference steps (1-100). Use 8 for turbo models,
            32+ for base models. The accepted range is model-specific — read
            `info.limits.min_steps`/`max_steps` for your `model` from `GET
            /api/v2/models`; several models pin it to a single value.
          type: integer
          example: 8
        guidance_scale:
          description: >-
            Classifier-free guidance scale. The accepted range is model-specific
            — read `info.limits.min_guidance` / `max_guidance` for your `model`
            from `GET /api/v2/models`. Several models pin it to a single value
            (AceStep_1_5_Turbo accepts only 1), so a value outside that range is
            rejected with 422.
          type: number
          example: 1
        seed:
          description: Random seed. Use -1 for random.
          type: integer
          example: -1
        format:
          description: >-
            Audio output format. Read `info.limits.output_formats` for your
            `model` from `GET /api/v2/models` and send one of the listed values;
            anything else is rejected with 422.
          type: string
          example: mp3
        reference_audio:
          description: >-
            Optional reference audio file for style transfer. Supported formats:
            mp3, wav, flac, ogg, m4a. Max size configurable (default 10MB).
            Duration must be within model-specific limits.
          type:
            - string
            - 'null'
          format: binary
        webhook_url:
          description: >-
            Optional HTTPS URL to receive webhook notifications for job status
            changes (processing, completed, failed). Must be HTTPS. Max 2048
            characters. A per-request URL is self-contained: it is delivered
            even when the account has no webhook configuration, or the account
            webhook is disabled. See the `webhooks` block at the top level of
            this document for the event payloads and the headers each delivery
            carries.
          type:
            - string
            - 'null'
          format: uri
          example: https://your-server.com/webhooks/deapi
          maxLength: 2048
        webhook_secret:
          description: >-
            Optional per-request HMAC secret (min. 32 chars) used to sign the
            webhook callback. Overrides the account-default secret for this job
            only. If omitted, the account-default secret signs the callback; if
            no secret exists at all, the callback is still delivered but
            UNSIGNED (empty X-DeAPI-Signature header). Requires `webhook_url` to
            also be set.
          type:
            - string
            - 'null'
          example: a1b2c3d4e5f60708091a2b3c4d5e6f7081920a1b2c3d4e5f60708091a2b3c4d5
          maxLength: 255
          minLength: 32
      type: object
    JobRequestResponseResource:
      required:
        - data
      properties:
        data:
          description: Information from success endpoint
          required:
            - request_id
          properties:
            request_id:
              description: >-
                UUID identifying the created inference request. Pass it to `GET
                /api/v2/jobs/{job_request}` to poll status and collect the
                result.
              type: string
              format: uuid
              example: c08a339c-73e5-4d67-a4d5-231302fbff9a
          type: object
      type: object
    response_error_default:
      properties:
        data:
          description: Information from success endpoint
          type: object
        message:
          description: Error general message
          type: string
        errors:
          description: Information about errors
          type: array
          items: {}
        statusCode:
          description: Status code
          type: integer
        code:
          description: >-
            A stable, machine-readable reason, on the responses that publish
            one. Refused credentials (401): `missing_key` — no Bearer
            credentials arrived at all; `invalid_key` — a key was presented but
            is unknown, carries the wrong secret, or is malformed; `key_expired`
            — past its expiry date; `key_revoked` — deactivated. Refused
            requests: `insufficient_permissions` (403), `ip_not_allowed` (403),
            `active_key_limit_reached` (422), `insufficient_balance` (422),
            `key_limit_exceeded` (402), `rate_limited` (429). Absent wherever a
            response publishes no code, so treat an unknown value as the status
            code alone — the list grows.
          type: string
          enum:
            - missing_key
            - invalid_key
            - key_expired
            - key_revoked
            - insufficient_permissions
            - ip_not_allowed
            - active_key_limit_reached
            - insufficient_balance
            - key_limit_exceeded
            - rate_limited
      type: object
    response_error_rate_limit:
      description: Rate limit exceeded response
      properties:
        message:
          description: Error message
          type: string
          example: Too Many Attempts.
      type: object
  headers:
    X-RateLimit-Policy:
      description: >-
        Every window that applies, as `<limit>;w=<seconds>` elements. Always
        present on a throttled response, so a single element means there is no
        daily limit — do not infer that from the absence of the Daily-* headers.
        `300;w=60` is minute-only; `3;w=60, 100;w=86400` is both.
      schema:
        type: string
        example: 3;w=60, 100;w=86400
    X-RateLimit-Source:
      description: >-
        Where the limit came from, so you know what would change it:

        `tier` — your account tier's rate for this endpoint;

        `fallback` — no rate configured for this tier and endpoint, degraded
        default in force;

        `model` — a per-model override for the model in the request, unaffected
        by your tier

        (only when the request carried a recognised `model`, otherwise the tier
        pool applies);

        `static` — a flat limit outside the tier system, identical for every
        account.


        If you ever see `fallback`, tell us: it means no rate is configured for
        that tier and endpoint.
      schema:
        type: string
        example: tier
        enum:
          - tier
          - fallback
          - model
          - static
    X-RateLimit-Limit:
      description: Maximum requests allowed per minute (RPM)
      schema:
        type: integer
        example: 3
    X-RateLimit-Remaining:
      description: Remaining requests in current minute window
      schema:
        type: integer
        example: 2
    X-RateLimit-Reset:
      description: >-
        Epoch second at which the minute (RPM) window refills. Fixed window —
        the value does not drift as you spend the quota. Describes the same
        window as X-RateLimit-Limit, including on a daily rejection.
      schema:
        type: integer
        example: 1755800460
    X-RateLimit-Daily-Limit:
      description: Maximum requests allowed per day (RPD)
      schema:
        type: integer
        example: 100
    X-RateLimit-Daily-Remaining:
      description: Remaining requests in current day window
      schema:
        type: integer
        example: 95
    X-RateLimit-Daily-Reset:
      description: >-
        Epoch second at which the daily (RPD) window refills — 86400 s after the
        first request that opened this bucket's window, not a calendar midnight.
        Present only when a daily limit applies.
      schema:
        type: integer
        example: 1755846000
    X-RateLimit-Type:
      description: >-
        Which limit rejected the request. Sent only on 429. Read it before
        anything else on the response: it decides what the other `X-RateLimit-*`
        headers are counting.


        `minute` — the per-request RPM window. `daily` — the per-request RPD
        window; the Daily-* pair is the one at 0.


        `key-creation` — the hourly cap on keys created over the API (`POST
        /api/v2/keys`), per account and shared by its ADMIN keys. The whole
        `X-RateLimit-*` set describes that bucket and there are no Daily-*
        headers.


        `ip-not-allowed` — too many calls from an address your key's IP
        whitelist refuses. Counted against that address, never against your
        account.
      schema:
        type: string
        example: minute
        enum:
          - minute
          - daily
          - key-creation
          - ip-not-allowed
    Retry-After:
      description: >-
        Seconds until the window that rejected you frees up (60 for minute, up
        to 86400 for daily). Counted from when that window opened, not to a
        calendar boundary. Sent only on 429.
      schema:
        type: integer
        example: 60
  responses:
    AccountSuspended:
      description: >-
        The account is suspended. Applies to every endpoint that spends credits
        — generation, transcription and prompt enhancement — and is returned
        before any validation runs, so a suspended account sees this rather than
        a 422 about the body. Price-estimate endpoints are not gated and keep
        working.
      content:
        application/json:
          schema:
            properties:
              message:
                description: >-
                  Human-readable explanation. No `errors` object accompanies
                  this response.
                type: string
                example: >-
                  Client account is suspended. If you believe this is a mistake,
                  contact us.
            type: object
    ValidationError:
      description: >-
        Validation failed. Common errors include: model does not exist, model
        does not support the inference type for this endpoint, the model is not
        available on your plan (it requires a higher tier than your account has
        — upgrade to use it), or invalid request parameters.
      content:
        application/json:
          schema:
            properties:
              message:
                description: General error message
                type: string
                example: The selected model does not support Text To Image.
              errors:
                description: >-
                  Detailed validation errors by field. A model your plan does
                  not cover is reported on the "model" field with the message
                  "This model is not available on your plan. Please upgrade to
                  access it." Such a model is also omitted from GET /models.
                type: object
                example:
                  model:
                    - >-
                      This model is not available on your plan. Please upgrade
                      to access it.
            type: object
    RateLimitExceeded:
      description: >-
        Rate limit exceeded. Check X-RateLimit-Type header to determine if
        minute (RPM) or daily (RPD) limit was hit.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Daily-Limit:
          $ref: '#/components/headers/X-RateLimit-Daily-Limit'
        X-RateLimit-Daily-Remaining:
          $ref: '#/components/headers/X-RateLimit-Daily-Remaining'
        X-RateLimit-Type:
          $ref: '#/components/headers/X-RateLimit-Type'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Daily-Reset:
          $ref: '#/components/headers/X-RateLimit-Daily-Reset'
        X-RateLimit-Source:
          $ref: '#/components/headers/X-RateLimit-Source'
        X-RateLimit-Policy:
          $ref: '#/components/headers/X-RateLimit-Policy'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/response_error_rate_limit'
  securitySchemes:
    bearerAuth:
      type: http
      description: >-
        Sanctum personal access token, sent as `Authorization: Bearer <token>`.
        The token is opaque — it carries no claims and no embedded expiry, so do
        not attempt to decode it. Issue and revoke tokens from your account
        dashboard.
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.