API

File uploads

Host your own images, videos and audio on AITOPIA to use them as model or edit input.

Models and edits take files by URL (for example an image_url model field, or asset_url for POST /v1/edit). If your file is not already at a public URL, upload it first. Uploads are not charged.

MethodSizeUse when
POST /v1/uploads with content_base64about 19 MB of fileSmall files you have in memory
POST /v1/uploads with source_urlup to 200 MBThe file is already at a public URL
POST /v1/uploads/link, then PUTup to 95 MBLarger local files

#Base64 upload

Send the file as base64 together with its type:

Shell
curl -X POST "https://api-live.aitopia.ai/v1/uploads" \
  -H "Authorization: Key $AITOPIA_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"content_base64\": \"$(base64 < photo.png | tr -d '\n')\", \"content_type\": \"image/png\", \"file_name\": \"photo.png\"}"
JSON
{ "url": "https://cdn.aitopia.ai/...", "content_type": "image/png", "size": 482113 }
FieldRequiredDescription
content_base64one of content_base64 / source_urlThe file, base64-encoded.
content_typewith content_base64MIME type, for example image/png.
file_namenoFile name to keep.

The whole request body is limited to 25 MB, which leaves room for about 19 MB of file once base64-encoded. A larger body answers 413 FILE_TOO_LARGE; use an upload link or source_url instead.

#Import from a URL

Shell
curl -X POST "https://api-live.aitopia.ai/v1/uploads" \
  -H "Authorization: Key $AITOPIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source_url": "https://example.com/clip.mp4", "file_name": "clip.mp4"}'

AITOPIA downloads the file on its side (up to 200 MB) and answers with the same { url, content_type, size }. The URL must be a direct link to the file on a public host; private or internal addresses answer 400 URL_NOT_ALLOWED. Pass a file_name with an extension (as above) or a content_type; with neither, the upload is refused with 400 INVALID_INPUT.

Send exactly one of content_base64 or source_url; both or neither is 400 INVALID_INPUT.

For larger local files, ask for a one-time upload URL and PUT the raw bytes to it.

1. Create the link

Shell
curl -X POST "https://api-live.aitopia.ai/v1/uploads/link" \
  -H "Authorization: Key $AITOPIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_name": "clip.mp4", "content_type": "video/mp4"}'
JSON
{
  "upload_url": "https://.../uploads/...",
  "file_url": null,
  "expires_at": "2026-10-23T10:10:00.000Z",
  "max_bytes": 99614720
}

Both body fields are optional; the body may be empty ({}). file_url is always null here: the file's URL is known only after the upload.

2. PUT the file

Shell
curl -T clip.mp4 "$UPLOAD_URL"

The upload_url is on mcp.aitopia.ai, not on the API host: if your servers only reach allow-listed hosts, allow mcp.aitopia.ai (and cdn.aitopia.ai, where the hosted file lives). No Authorization header is needed: the link itself is the credential. It is valid once, until expires_at (10 minutes), and only for the account that created it. If you did not pass content_type when creating the link, the type is taken from the file and the Content-Type header of the PUT.

The PUT answers with the hosted file. This answer comes from the upload host, not the API, so it differs from the rest of the API: field names are camelCase, and an error is { "error": "<message>" } (a plain string, no code; branch on the HTTP status in the table below):

JSON
{
  "status": "completed",
  "assetUrl": "https://cdn.aitopia.ai/...",
  "assetName": "clip.mp4",
  "sizeBytes": 73400320,
  "contentType": "video/mp4",
  "billing": "not_charged",
  "billed": false
}

Use assetUrl as input to models and edits.

PUT statusMeaning
200Uploaded; see assetUrl.
400The upload was empty.
410The link is invalid, already used, or expired. Create a new one.
413The file is larger than max_bytes.
415The file type is not allowed. Web pages, scripts, SVG, XML and other active content are refused; photos, video, audio, PDF and common documents are fine.
429Uploads are busy; retry in a few seconds with the same link.
502The upload failed on our side; retry with the same link.

Errors from the PUT have the form { "error": "<message>" }.

#Upload errors (POST /v1/uploads)

StatusCodeMeaning
400INVALID_INPUTMissing or conflicting fields, missing content_type, invalid source_url, or a body that is not valid JSON.
400URL_NOT_ALLOWEDsource_url points to a private or internal address.
413FILE_TOO_LARGEOver the size limit.
429RATE_LIMITUploads are busy; retry after retry_after_seconds.
502UPSTREAM_5XXThe source_url could not be downloaded (retryable).
AITOPIA REST API · Live https://api-live.aitopia.ai/v1 · Sandbox https://api-sandbox.aitopia.ai/v1