Skip to content
H3 Studio

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

KindFormatsMax sizeMax count
ImageJPG, PNG, WEBP, HEIC, HEIF30 MB1 first frame, 1 last frame, or 9 references
VideoMP4, MOV50 MB3
AudioWAV, MP315 MB3

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.