Files and references
Upload media once, reference it by id. Bytes go straight to object storage — they never pass through the API server, so a 50 MB video does not have to be buffered anywhere on the way.
Upload
Ask for a target:
curl https://api.h3.studio/v1/files \
-H "Authorization: Bearer $H3_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "content_type": "image/png", "size_bytes": 204800, "filename": "first.png" }'
{
"file_id": "718293847561029",
"uri": "mm_file://718293847561029",
"upload_url": "https://storage.googleapis.com/…",
"upload_method": "PUT",
"upload_headers": { "Content-Type": "image/png" },
"expires_at": 1785126429
}
Then PUT the bytes to upload_url with exactly the headers given:
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/png" \
--data-binary @first.png
The signature covers the content type, so sending a different one fails.
Reference it
{
"type": "image_url",
"image_url": { "url": "mm_file://718293847561029" },
"role": "first_frame"
}
Check on a file
curl "https://api.h3.studio/v1/files/retrieve?file_id=$FILE_ID" \
-H "Authorization: Bearer $H3_API_KEY"
{
"file_id": "718293847561029",
"uri": "mm_file://718293847561029",
"content_type": "image/png",
"size_bytes": 204800,
"uploaded": true,
"created_at": 1785125529,
"download_url": "https://cdn.h3.studio/…?Expires=…&Signature=…"
}
Because the upload goes directly to storage, this call is the moment the API
learns it happened — uploaded flips to true here, not during the PUT.
Accepted formats
| Kind | Formats | Max size | Max count |
|---|---|---|---|
| Image | JPG, PNG, WEBP, HEIC, HEIF | 30 MB | 1 first frame, 1 last frame, or 9 references |
| Video | MP4, MOV | 50 MB | 3 |
| Audio | WAV, MP3 | 15 MB | 3 |
Reference video and audio must each be 2–15 seconds, and no more than 12 media files total.
Retention
Uploaded inputs are deleted after 7 days, generated outputs after 30. Download anything you want to keep.
Uploading from a browser
The signed URL is scoped to one object, one method and one content type, and it
expires in fifteen minutes — which makes it safe to hand to a browser, unlike
an API key. Mint it on your server, return just the upload_url to the client,
and let the client PUT directly.