API reference

The canvas, programmable

Everything the app does runs through the same job pipeline this API exposes: submit a generation, poll its state, pull the result from your library. Jobs are durable — once accepted, a job survives disconnects, retries, and our own redeploys.

Private beta API access is rolling out to Ultimate workspaces. Request a key from workspace settings, or write to us.

base URL
https://api.mediamint.app/v1
authentication
# Keys are per-workspace: jobs bill that workspace's
# credit pool and results land in its library.
curl https://api.mediamint.app/v1/models \
  -H "Authorization: Bearer mm_live_9f2ab..."
POST/quote

Price a generation before running it. Costs are computed, not flat — image models bill per megapixel, video per second, speech per thousand characters — so the quote depends on your settings. The same quote is shown in the app before every generation; the API just makes it callable.

Quoting is free and never reserves credits.

POST /quote
curl https://api.mediamint.app/v1/quote \
  -H "Authorization: Bearer $MM_KEY" \
  -d '{
    "model": "veo-3.1",
    "input": { "duration_seconds": 8, "resolution": "1080p" }
  }'

# 200 OK
{
  "model": "veo-3.1",
  "credits": 1600,
  "note": "billed per second: 200 cr x 8s"
}
POST/generations

Submit a job. The API validates the input, places a credit hold for the quoted amount, persists the job, and returns immediately — the work happens on our workers, whether or not you stay connected.

Send an Idempotency-Key header on retries: resubmitting the same key returns the original job instead of charging twice.

If the balance can’t cover the quote you get a 402 and nothing runs.

POST /generations
curl https://api.mediamint.app/v1/generations \
  -H "Authorization: Bearer $MM_KEY" \
  -H "Idempotency-Key: 2b7c-retry-safe" \
  -d '{
    "model": "flux-2-pro",
    "input": {
      "prompt": "product shot: matte ceramic mug on slate",
      "aspect_ratio": "1:1"
    },
    "board_id": "brd_8Yt2..."   # optional: also place on a board
  }'

# 202 Accepted
{
  "id": "job_4Xn9...",
  "state": "queued",
  "credits_held": 15
}
GET/generations/{id}

Poll a job’s state. Jobs walk a persisted state machine, and every output is streamed to Backblaze B2 before the job reports done— the URL you get serves from storage we control, not a model provider’s scratch space.

Result URLs are signed and expire after 7 days. Don’t store them — store the asset id and re-fetch a fresh URL when you need one. On failed, the response carries the reason and the credit hold is already released.

Polling every 2–5 seconds is plenty; video jobs run minutes, not milliseconds. Prefer push? See webhooks below.

GET /generations/job_4Xn9
# state machine:
# queued -> submitted -> running -> uploading -> done
#                                            \-> failed

{
  "id": "job_4Xn9...",
  "state": "done",
  "model": "flux-2-pro",
  "credits_charged": 15,
  "asset": {
    "id": "ast_71Qm...",
    "kind": "image",
    "width": 1024,
    "height": 1024,
    "url": "https://f004.backblazeb2.com/...&expires=...",
    "url_expires_at": "2026-07-30T09:00:00Z"
  }
}
GET/assets

Your workspace’s library — everything generated or uploaded, filterable by kind, board_id, and tag, cursor-paginated. Each item carries a fresh signed URL, so a list call is all a render pipeline needs to pull media.

Single assets are at GET /assets/{id}, which also mints a fresh URL — the endpoint to call when a stored link has expired.

GET /assets?tag=hero&kind=video
{
  "data": [
    {
      "id": "ast_71Qm...",
      "kind": "video",
      "duration_seconds": 8,
      "tags": ["hero", "launch-week"],
      "board_id": "brd_8Yt2...",
      "created_at": "2026-07-22T14:03:11Z",
      "url": "https://f004.backblazeb2.com/...",
      "url_expires_at": "2026-07-30T09:00:00Z"
    }
  ],
  "next_cursor": "cur_9aa3..."
}
POST/webhooks (your endpoint)

Register a webhook URL in workspace settings and we push job.completed and job.failed events instead of making you poll. Deliveries are signed, and retried with backoff for 24 hours until your endpoint returns a 2xx.

Payloads carry ids, not media: fetch the asset by id when you receive the event. That keeps webhook handling fast and means a replayed event can’t leak an old signed URL.

job.completed payload
# Headers
#   X-MediaMint-Signature: t=1690...,v1=5257a8...
# Verify: HMAC-SHA256 of "{t}.{body}" with your
# webhook secret; reject if older than 5 minutes.

{
  "type": "job.completed",
  "created_at": "2026-07-22T14:05:40Z",
  "data": {
    "job_id": "job_4Xn9...",
    "state": "done",
    "asset_id": "ast_71Qm...",
    "credits_charged": 15
  }
}

Errors & rate limits

Errors are JSON with a stable code and a human message. Two principles hold everywhere: an error never costs credits, and a retry with the same idempotency key is always safe.

Keys default to 60 requests/minute and 10 concurrent jobs per workspace. Limits are headers on every response (X-RateLimit-Remaining), and higher ceilings are a support request away.

StatusCodeMeaning
401invalid_keyMissing or revoked API key.
402insufficient_creditsThe workspace balance can't cover the quoted cost. Nothing was run or charged.
404not_foundUnknown job, asset, or board id — or one your key can't see.
409idempotency_conflictAn Idempotency-Key was reused with a different request body.
422invalid_inputThe model rejected the inputs (bad aspect ratio, missing reference image, …). Details are in the message.
429rate_limitedToo many requests. Back off per the Retry-After header.
500provider_errorAn upstream model failed after retries. The credit hold is released automatically.

Prototype in the app first

The canvas and the API share models, credits, and your library — find the prompt and settings interactively, then ship the exact same call.

Open the canvas