Overview
Many models accept media (images, video, audio) as input. To use your own files, upload them with this endpoint and pass the returned public_url to Create Prediction.
Uploads happen in two steps:
Request a presigned URL
POST /v1/upload/presign with file metadata. The response contains a short-lived URL to upload to and a permanent public_url.
Upload the file
PUT the raw file bytes to presigned_url, applying any required_headers exactly as returned.
Step 1 — Request a presigned URL
Endpoint
POST https://api.eachlabs.ai/v1/upload/presign
Request Body
| Field | Type | Required | Description |
|---|
content_type | string | Yes | MIME type of the file (e.g., image/png, video/mp4, audio/mpeg). |
file_type | string | No | High-level category. One of image, video, audio, other. |
Code Examples
curl -X POST https://api.eachlabs.ai/v1/upload/presign \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"content_type": "image/png",
"file_type": "image"
}'
Response
{
"id": "0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0",
"presigned_url": "https://eachlabs-storage.s3.amazonaws.com/uploads/...?X-Amz-Algorithm=...&X-Amz-Signature=...",
"public_url": "https://cdn-us.eachlabs.ai/uploads/0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0.png",
"expires_at": "2026-04-27T18:15:00Z",
"required_headers": {
"x-amz-meta-file-id": "0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0"
}
}
Response Fields
| Field | Type | Description |
|---|
id | string | Internal upload identifier. |
presigned_url | string | Short-lived URL to PUT the file to. |
public_url | string | Permanent URL to pass as a model input once the upload succeeds. |
expires_at | string | ISO-8601 timestamp when presigned_url stops accepting uploads. |
required_headers | object | Headers that must be sent verbatim on the PUT request. The presigned URL is signed for these exact values — missing or modified headers will fail with a signature mismatch. May be empty. |
Step 2 — Upload the file
PUT the raw file bytes to presigned_url. Include every header from required_headers exactly as returned, plus a Content-Type matching the value sent in step 1.
curl -X PUT "$PRESIGNED_URL" \
-H "Content-Type: image/png" \
-H "x-amz-meta-file-id: 0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0" \
--data-binary @./photo.png
A 200 OK from the PUT means the file is live. Use public_url as the input value when you create a prediction.
{
"model": "flux-1-1-pro",
"version": "1.0.0",
"input": {
"image": "https://cdn-us.eachlabs.ai/uploads/0dce0f44-b8a5-4f30-91d4-5f6b0c221bf0.png",
"prompt": "Make it look cinematic"
}
}
Limits
| Limit | Value | Notes |
|---|
| Max file size | 100 MB per upload | |
| Presigned URL lifetime | 15 minutes | Use the URL before expires_at; request a new one if it lapses. |
Error Responses
| Status | Body | Description |
|---|
400 | {"error": "content_type is required"} | Missing required field. |
401 | {"error": "Invalid or missing API key"} | Authentication failure. |
500 | {"error": "Failed to generate presigned URL"} | Server error. |
The presigned URL expires 15 minutes after it’s issued. Upload soon after requesting — if you wait past expires_at, just request a new one.