Upload API

Upload images to Vydra's storage. Get back a public URL you can use with the Models API.

POST
/api/v1/upload

Upload an image file

Storage
Cloudflare R2
Max Size
20 MB
Cost
Free

Request

Send a multipart/form-data request with the file in a field named file.

Form Fields

NameTypeDescription
filerequiredFileThe image file to upload

Supported Formats

image/jpegimage/jpgimage/pngimage/webpimage/gif

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

200 OKjson
{
  "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

NameTypeDescription
urlstringPublic URL of the uploaded image
filenamestringOriginal filename
sizenumberFile size in bytes
typestringMIME type of the file
keystringStorage 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:

Upload → Edit Workflowjavascript
// 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

Uploaded images are stored on Cloudflare R2 with global CDN distribution. This ensures fast access from anywhere in the world with zero egress fees.

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.