How to Upload Files
Upload images, videos, and audio to Get3W storage, then pass the returned download_url to any model input. The client makes one small authenticated request, then uploads the bytes directly to storage with a short-lived URL. File bytes never pass through the API gateway.
Endpoint
POST https://api.get3w.com/v1/media/uploads
Authorization: Bearer YOUR_API_KEY
Content-Type: application/jsonRequest Body
{
"filename": "image.png",
"size": 1024000,
"content_type": "image/png"
}| Field | Type | Required | Description |
|---|---|---|---|
filename | string | Yes | Original filename, including its extension |
size | integer | Yes | Exact file size in bytes; maximum 200 MB (209,715,200 bytes) |
content_type | string | No | MIME type; inferred from the extension when omitted |
Supported formats:
| Type | Extensions |
|---|---|
| image | jpg jpeg png gif webp bmp tiff tif |
| video | mp4 webm mov |
| audio | mp3 wav m4a aac flac |
Response
{
"type": "image",
"download_url": "https://get3w-media-1252164560.cos.ap-guangzhou.myqcloud.com/tmp/uploads/2026/10/17/3f2a....png",
"filename": "image.png",
"size": 1024000,
"upload": {
"method": "PUT",
"url": "https://...signed...",
"headers": {
"Content-Type": "image/png",
"x-cos-acl": "public-read",
"x-cos-forbid-overwrite": "true"
},
"expires_at": "2026-10-17T02:15:00Z"
}
}| Field | Description |
|---|---|
type | image, video, or audio |
download_url | Public URL to use as a model input once the upload succeeds |
upload.method | Always PUT |
upload.url | Short-lived signed upload URL (valid for 15 minutes) |
upload.headers | Headers you must send with the PUT, exactly as returned |
upload.expires_at | UTC time after which upload.url stops working |
Upload the exact file bytes with the returned method, URL, and headers. A successful PUT returns any 2xx status, and download_url is ready to use immediately.
Examples
FILE="/path/to/image.png"
FILE_SIZE=$(wc -c < "$FILE" | tr -d ' ')
TICKET=$(curl --fail-with-body --silent --show-error \
-X POST "https://api.get3w.com/v1/media/uploads" \
-H "Authorization: Bearer $GET3W_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg filename "$(basename "$FILE")" --argjson size "$FILE_SIZE" \
'{filename: $filename, size: $size}')")
UPLOAD_URL=$(printf '%s' "$TICKET" | jq -r '.upload.url')
UPLOAD_HEADERS=()
while IFS= read -r header; do
UPLOAD_HEADERS+=(-H "$header")
done < <(printf '%s' "$TICKET" | jq -r \
'.upload.headers | to_entries[] | "\(.key): \(.value)"')
curl --fail-with-body --request PUT "$UPLOAD_URL" \
"${UPLOAD_HEADERS[@]}" \
--upload-file "$FILE"
printf '%s' "$TICKET" | jq -r '.download_url'import mimetypes
import os
from pathlib import Path
import requests
path = Path("/path/to/image.png")
payload = {"filename": path.name, "size": path.stat().st_size}
content_type = mimetypes.guess_type(path.name)[0]
if content_type:
payload["content_type"] = content_type
ticket_response = requests.post(
"https://api.get3w.com/v1/media/uploads",
headers={"Authorization": f"Bearer {os.environ['GET3W_API_KEY']}"},
json=payload,
timeout=30,
)
ticket_response.raise_for_status()
ticket = ticket_response.json()
with path.open("rb") as file:
upload_response = requests.put(
ticket["upload"]["url"],
headers=ticket["upload"]["headers"],
data=file,
timeout=300,
)
upload_response.raise_for_status()
print(ticket["download_url"])import { readFile, stat } from 'node:fs/promises';
import { basename } from 'node:path';
const filePath = '/path/to/image.png';
const ticketResponse = await fetch('https://api.get3w.com/v1/media/uploads', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GET3W_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
filename: basename(filePath),
size: (await stat(filePath)).size,
}),
});
if (!ticketResponse.ok) throw new Error(await ticketResponse.text());
const ticket = await ticketResponse.json();
const uploadResponse = await fetch(ticket.upload.url, {
method: ticket.upload.method,
headers: ticket.upload.headers,
body: await readFile(filePath),
});
if (!uploadResponse.ok) throw new Error(await uploadResponse.text());
console.log(ticket.download_url);Errors
| Code | Description |
|---|---|
| 400 | Missing or invalid fields (Invalid parameters), invalid filename, unsupported format, or content type mismatch |
| 401 | Missing or invalid API key |
| 413 | File exceeds 200 MB |
| 429 | Upload rate limit exceeded (60 tickets per minute); retry later |
The PUT to upload.url can also fail with 403 if the ticket has expired, the headers differ from the returned ones, or the uploaded byte count does not match the declared size. Request a new ticket in that case.
Reusing a ticket after a successful PUT returns 409, because existing objects are never overwritten. Request a new ticket for every file.
Important Notes
- The upload URL is a temporary credential. Do not log, persist, or share it
- Send your API key only to
api.get3w.com. Never add anAuthorizationheader to thePUTrequest - Treat
upload.urlas opaque. Its hostname and storage provider may change. Always use the full URL returned for that upload; do not construct or rewrite it - One ticket, one file. Tickets cannot be reused, and an existing object is never overwritten
- Retention: uploaded files are kept for 7 days, then deleted automatically. They are intended only as inference inputs. See Data Retention Policy
Next Steps
- How to Submit Task — Pass
download_urlas a model input - Error Codes — Full error reference