Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Distribea Catalog API
version: 1.0.0
summary: Royalty-free media catalog and AI image, video, music and voice generation for agents.
description: 'Royalty-free media catalog (music, sound effects, stock footage, images) and AI generation (images, video, music, voice) for AI agents and scripts.
## Quick start (steps 1 and 2 need no key)
1. Search the catalog:
`GET https://distribea.com/api/v1/catalog/search?q=epic+cinematic+drums&type=music`
2. Get the exact price of a generation. Nothing is generated or charged:
`POST https://distribea.com/api/v1/generate/image` with `{"prompt":"a red fox in fresh snow","dry_run":true}`
3. Get a key at https://distribea.com/account/mcp. Every new account includes 250 test credits, even without a plan.
4. Send the same body without `dry_run`, with the key, to generate. The response holds the file URL.
## Authentication
Send the key in `Authorization: Bearer ` (or `X-Api-Key: `). Catalog search, item details, the model list and every `dry_run` price quote are public. The account and real generation need a key. The same key works with the Distribea MCP server at https://distribea.com/api/mcp/remote.
## Responses
Success bodies are plain JSON objects, snake_case, with no envelope. One name per idea, in requests and responses alike: `duration_seconds` for any length, `aspect_ratio` for the picture shape (`16:9`), `file_type` for the file format (`png`, `mp4`, `mp3`). Lists are JSON arrays (`genre`, `mood`, `tags`). A setting left to the model''s default is `null`.
Every generation answer is a `GenerationResult`: read `status` first. `quoted` (`QuotedResult`: dry run, price and settings, `charged: false`), `succeeded` (`SucceededResult`: the file, `charged: true`) or `pending` (`PendingResult`: send again later).
## Retries
For video, music and voice, a job is identified by the request itself: the same key and the same JSON body within 3 minutes is the same job (the body is compared after parsing, so key order and spacing do not matter), so sending it again collects the result and never charges twice. No idempotency header is needed. An image request sent twice makes two images. Only errors with `retryable: true` are worth sending again unchanged.
Every error, on every endpoint, has one shape:
`{"error":{"code":"missing_parameter","message":"...","param":"q","fix":"...","retryable":false,"docs_url":"https://distribea.com/openapi.json"}}`
`code` is stable and machine-readable, `param` names the field to change, `fix` says what to send instead, `valid_values` lists the accepted values when there is a fixed list, `retryable` says whether the same request can simply be sent again, and `retry_after_seconds` (also in the `Retry-After` header) says when.
## Credits and limits
Generation is paid in credits at the same price as the website. `GET /api/v1/models` shows each model''s starting price, and `dry_run: true` returns the exact price of a request. A failed generation is refunded automatically. Each account can run 12 generations per minute and 200 per day (UTC). Public endpoints are cached for 10 minutes.
## Long jobs
Video can take a few minutes. If it is not ready within the request, the API answers `202` with `status: "pending"` and a `Retry-After` header: send the exact same request again to collect the result. Video, music and voice retries within 3 minutes are never charged twice.'
contact:
name: Distribea
email: hello@distribea.com
url: https://distribea.com
termsOfService: https://distribea.com/legal/licence-terms
servers:
- url: https://distribea.com
description: Production
security: []
tags:
- name: Catalog
description: Royalty-free music, sound effects, footage and images. Public.
paths:
/api/v1:
get:
operationId: getApiIndex
tags:
- Catalog
summary: List the endpoints
description: 'Machine-readable index: base URL, auth, links to this spec and every endpoint.'
responses:
'200':
headers:
X-Request-Id:
$ref: '#/components/headers/XRequestId'
Cache-Control:
$ref: '#/components/headers/CacheControl'
description: The API index.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiIndex'
example:
name: Distribea API
version: 1.0.0
base_url: https://distribea.com/api/v1
openapi: https://distribea.com/openapi.json
docs: https://distribea.com/llms.txt
auth:
type: api_key
header: 'Authorization: Bearer <key>'
get_key: https://distribea.com/account/mcp
note: Every new Distribea account, even without a plan, includes 250 test credits. Catalog search, item details, the model list and price quotes (dry_run) need no key.
mcp_server: https://distribea.com/api/mcp/remote
endpoints:
- method: GET
path: /api/v1/catalog/search?q={words}&type={music|sfx|footage|images}&limit={1-20}
auth: none
does: Search the royalty-free catalog.
- method: GET
path: /api/v1/catalog/items/{slug}
auth: none
does: Details and preview link of one catalog item.
- method: GET
path: /api/v1/models?type={image|video|music|voice}
auth: none
does: Generation models, their settings and price in credits.
- method: GET
path: /api/v1/account
auth: api_key
does: Plan and credit balance of the key's account.
- method: POST
path: /api/v1/generate/{image|video|music|voice}
auth: api_key, or none with dry_run
does: 'Generate a file. Add "dry_run": true to get the exact price without a key: nothing is generated or charged.'
/api/v1/catalog/search:
get:
operationId: searchCatalog
tags:
- Catalog
summary: Search the catalog
description: Full-text search over titles, tags, genres and moods. All words must match. Results are ranked by relevance. No key needed.
parameters:
- name: q
in: query
required: true
description: 'Words to search for: mood, genre, instrument, subject.'
schema:
type: string
minLength: 1
example: epic cinematic drums
- name: type
in: query
required: false
description: Which catalog to search.
schema:
type: string
enum:
- music
- sfx
- footage
- images
default: music
- name: limit
in: query
required: false
description: Maximum number of results.
schema:
type: integer
minimum: 1
maximum: 20
default: 10
responses:
'200':
headers:
X-Request-Id:
$ref: '#/components/headers/XRequestId'
Cache-Control:
$ref: '#/components/headers/CacheControl'
description: Matching items, best first. An empty `results` array means nothing matched.
content:
application/json:
schema:
$ref: '#/components/schemas/SearchResponse'
example:
type: music
query: epic
total: 357
count: 2
results:
- slug: epic-pirate-adventure
title: Epic Pirate Adventure
genre:
- Movie Score
- Thriller
- Trailer
- War
- Rock
mood:
- Performance
- Dreamy
- Epic
- Heroic
duration_seconds: 179
bpm: 105
- slug: asian-historical-epic
title: Asian Historical Epic
genre:
- Electro
- Movie Score
- Peplum
- Trailer
- War
mood:
- Epic
- Heroic
- Performance
- Massive
- Energetic
duration_seconds: 186
bpm: 145
'400':
$ref: '#/components/responses/BadRequest'
/api/v1/catalog/items/{slug}:
get:
operationId: getCatalogItem
tags:
- Catalog
summary: Get one catalog item
description: Details of one item and a preview link to listen or look. Downloading the licensed file needs a subscription and goes through the MCP tool download_asset. No key needed.
parameters:
- name: slug
in: path
required: true
description: The `slug` from a search result.
schema:
type: string
example: epic-pirate-adventure
responses:
'200':
headers:
X-Request-Id:
$ref: '#/components/headers/XRequestId'
Cache-Control:
$ref: '#/components/headers/CacheControl'
description: The item.
content:
application/json:
schema:
$ref: '#/components/schemas/CatalogItem'
example:
slug: epic-pirate-adventure
type: music
title: Epic Pirate Adventure
genre:
- Movie Score
- Thriller
- Trailer
- War
- Rock
mood:
- Performance
- Dreamy
- Epic
- Heroic
duration_seconds: 179
bpm: 105
category: Pirates
tags:
- epic
- pirate
- adventure
preview_url: https://distribea.com/uploads-proxy/music/music-1776643944780-y6u09m/audio.mp3?exp=1792609200&sig=Y2bTYdOnLF4b63wUsJEL1RY7PzQg8gtP17P96Dm_jgc
license: Royalty-free. Downloading the licensed file needs a Distribea subscription (MCP tool download_asset).
'404':
$ref: '#/components/responses/NotFound'
components:
responses:
NotFound:
description: Nothing at this address or with this slug. `fix` says where to find a valid one.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
slug:
value:
error:
code: not_found
message: No published asset has the slug "ghost".
param: slug
fix: Take the slug from GET /api/v1/catalog/search results.
retryable: false
docs_url: https://distribea.com/openapi.json
endpoint:
value:
error:
code: not_found
message: No endpoint at GET /api/v1/nope.
fix: Use one of the endpoints listed in valid_endpoints.
valid_endpoints:
- GET /api/v1/catalog/search?q={words}&type={music|sfx|footage|images}&limit={1-20}
- GET /api/v1/catalog/items/{slug}
- GET /api/v1/models?type={image|video|music|voice}
- GET /api/v1/account
- POST /api/v1/generate/{image|video|music|voice}
retryable: false
docs_url: https://distribea.com/openapi.json
BadRequest:
description: A query parameter is missing or not accepted. `param` names it, `fix` says what to send and `valid_values` lists the accepted values.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missing_q:
value:
error:
code: missing_parameter
message: The "q" parameter is required.
param: q
fix: Add what to search for, e.g. /api/v1/catalog/search?q=epic+cinematic+drums&type=music
retryable: false
docs_url: https://distribea.com/openapi.json
bad_type:
value:
error:
code: invalid_parameter
message: '"type" must be one of: music, sfx, footage, images.'
param: type
fix: Use type=music, type=sfx, type=footage or type=images.
valid_values:
- music
- sfx
- footage
- images
retryable: false
docs_url: https://distribea.com/openapi.json
bad_limit:
value:
error:
code: invalid_parameter
message: '"limit" must be an integer from 1 to 20.'
param: limit
fix: Use limit=10, or remove it.
retryable: false
docs_url: https://distribea.com/openapi.json
headers:
XRequestId:
description: Unique id of this response. Quote it when reporting a problem.
schema:
type: string
format: uuid
CacheControl:
description: public, s-maxage=600 on catalog and model lists (CDN cached 10 minutes); no-store on the account and on generation.
schema:
type: string
schemas:
SearchResponse:
type: object
required:
- type
- query
- total
- count
- results
properties:
type:
type: string
enum:
- music
- sfx
- footage
- images
query:
type: string
total:
type: integer
description: All matches, before `limit`.
count:
type: integer
description: Items in `results`.
results:
type: array
items:
$ref: '#/components/schemas/SearchHit'
CatalogItem:
type: object
required:
- slug
- type
- title
- preview_url
- license
properties:
slug:
type: string
type:
type: string
enum:
- music
- sfx
- footage
- images
title:
type: string
genre:
type: array
items:
type: string
mood:
type: array
items:
type: string
duration_seconds:
type:
- integer
- 'null'
bpm:
type: integer
category:
type: string
tags:
type: array
items:
type: string
description:
type: string
preview_url:
type: string
format: uri
description: Stream or view the item. Not the licensed file.
license:
type: string
SearchHit:
type: object
required:
- slug
- title
properties:
slug:
type: string
description: Pass it to GET /api/v1/catalog/items/{slug}.
title:
type: string
genre:
type: array
items:
type: string
mood:
type: array
items:
type: string
duration_seconds:
type:
- integer
- 'null'
description: Music, sound effects and footage.
bpm:
type: integer
ApiIndex:
type: object
required:
- name
- version
- base_url
- openapi
- auth
- endpoints
properties:
name:
type: string
version:
type: string
base_url:
type: string
format: uri
openapi:
type: string
format: uri
docs:
type: string
format: uri
auth:
type: object
required:
- type
- header
- get_key
properties:
type:
type: string
const: api_key
header:
type: string
get_key:
type: string
format: uri
note:
type: string
mcp_server:
type: string
format: uri
endpoints:
type: array
items:
type: object
required:
- method
- path
- auth
- does
properties:
method:
type: string
enum:
- GET
- POST
path:
type: string
auth:
type: string
does:
type: string
Error:
type: object
required:
- error
properties:
error:
type: object
required:
- code
- message
- retryable
- docs_url
properties:
code:
type: string
description: Stable, machine-readable error code.
enum:
- missing_parameter
- invalid_parameter
- invalid_json
- not_found
- method_not_allowed
- missing_api_key
- invalid_api_key
- insufficient_credits
- test_credits_used_up
- rate_limited
- daily_limit_reached
- request_in_progress
- generation_failed
- internal_error
message:
type: string
description: What went wrong, in one sentence.
param:
type: string
description: The parameter or field to change.
fix:
type: string
description: What to send instead.
valid_values:
type: array
items:
type:
- string
- integer
description: Accepted values, when there is a fixed list.
credits_required:
type: integer
description: Price of the refused request, in credits.
valid_endpoints:
type: array
items:
type: string
description: 'Unknown address only: every endpoint of the API.'
daily_limit:
type: integer
description: Generations allowed per day (UTC).
retryable:
type: boolean
description: 'true: send the same request again (after `retry_after_seconds` when present). false: change the request first.'
retry_after_seconds:
type: integer
description: Seconds to wait, same as the Retry-After header.
docs_url:
type: string
format: uri
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: Your Distribea key, from https://distribea.com/account/mcp.
apiKeyHeader:
type: apiKey
in: header
name: X-Api-Key
description: The same key, for clients that cannot set Authorization.
externalDocs:
description: llms.txt
url: https://distribea.com/llms.txt