Upload API
Upload images to Vydra's storage. Get back a public URL you can use with the Models API.
/api/v1/uploadUpload an image file
Request
Send a multipart/form-data request with the file in a field named file.
Form Fields
| Name | Type | Description |
|---|---|---|
filerequired | File | The image file to upload |
Supported Formats
Example Request
"text">-purple-600">curl "text-blue-600">-X POST https://vydra.app/api/v1/upload \
"text-blue-600">-H "Authorization: Bearer YOUR_API_KEY" \
"text-blue-600">-F "file=@/path/to/image.jpg"Response
{
"url": "https://pub-xxx.r2.dev/uploads/user_123/1706234400000-image.jpg",
"filename": "image.jpg",
"size": 245678,
"type": "image/jpeg",
"key": "uploads/user_123/1706234400000-image.jpg"
}Response Fields
| Name | Type | Description |
|---|---|---|
url | string | Public URL of the uploaded image |
filename | string | Original filename |
size | number | File size in bytes |
type | string | MIME type of the file |
key | string | Storage key (internal reference) |
Error Responses
No File Provided (400)
{
"error": "No file provided"
}Invalid File Type (400)
{
"error": "Invalid file type. Allowed: image/jpeg, image/jpg, image/png, image/webp, image/gif"
}File Too Large (400)
{
"error": "File size exceeds 20MB limit"
}Complete Workflow
Upload an image, then use it with a model:
// 1. Upload the image
"text-purple-600">const formData = "text-purple-600">new FormData();
formData.append("text-green-600">'file', myImageFile);
"text-purple-600">const uploadRes = "text-purple-600">await fetch("text-green-600">'https://vydra.app/api/v1/upload', {
method: "text-green-600">'POST',
headers: { "text-green-600">'Authorization': "text-green-600">'Bearer YOUR_API_KEY' },
body: formData
});
"text-purple-600">const { url: imageUrl } = "text-purple-600">await uploadRes.json();
console.log("text-green-600">'Image uploaded to:', imageUrl);
// 2. Use the URL with a model
"text-purple-600">const editRes = "text-purple-600">await fetch("text-green-600">'https://vydra.app/api/v1/models/gemini/edit', {
method: "text-green-600">'POST',
headers: {
"text-green-600">'Authorization': "text-green-600">'Bearer YOUR_API_KEY',
"text-green-600">'Content-Type': "text-green-600">'application/json'
},
body: JSON.stringify({
imageUrl: imageUrl,
prompt: "text-green-600">'Add a dramatic sunset sky in the background'
})
});
"text-purple-600">const { imageUrl: editedUrl } = "text-purple-600">await editRes.json();
console.log("text-green-600">'Edited image:', editedUrl);Storage Details
Cloudflare R2 Storage
Retention
Uploaded files are retained indefinitely. Generated images are also stored permanently.
URL Format
URLs follow the pattern: https://pub-xxx.r2.dev/uploads/[userId]/[timestamp]-[filename]
No Cost
Uploads don't consume credits. You only pay credits when using models.
Best Practices
Optimize Before Upload
Compress large images before uploading. Most models work fine with images under 4MB. This speeds up both upload and processing.
Use Appropriate Formats
JPEG is best for photos. PNG is best for graphics with transparency. WebP offers good compression for both.
Reuse URLs
Save the returned URL for reuse. You can use the same uploaded image with multiple model requests without re-uploading.