Upload API
Upload images and videos to Reimage storage.
Endpoint
http
POST /upload/Authentication
Required. Include your API key in the Authorization header.
http
Authorization: Bearer YOUR_API_KEYRequest Format
Content-Type: multipart/form-data
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file | File | Yes | The image or video file to upload |
tags | String | No | Comma-separated list of tags (e.g., "product,hero") |
folder | String | No | Folder/directory to organize the file |
object | String | No | Custom object ID (defaults to random 12-char hex) |
Supported File Types
Images:
- JPEG (.jpg, .jpeg)
- PNG (.png)
- GIF (.gif) - including animated
- WebP (.webp)
- AVIF (.avif)
Videos:
- MP4 (.mp4)
- MOV (.mov)
- AVI (.avi)
- And other common formats
Response
Image Upload Response
json
{
"object_url": "https://files.reimage.dev/abc123/xyz789/",
"original": "https://files.reimage.dev/abc123/xyz789/original",
"optimised": "https://files.reimage.dev/abc123/xyz789/original.webp",
"object_id": "xyz789",
"tags": ["product", "hero"],
"size": 245.52,
"type": "image/jpeg",
"folder": "products",
"original_file_name": "product-photo.jpg",
"height": 1920,
"width": 1080
}Video Upload Response
json
{
"object_url": "https://files.reimage.dev/abc123/vid-xyz789/",
"original": "https://files.reimage.dev/abc123/vid-xyz789/original",
"object_id": "vid-xyz789",
"tags": ["demo", "tutorial"],
"size": 5242.88,
"type": "video/mp4",
"folder": "videos",
"original_file_name": "demo-video.mp4",
"video_url": "https://files.reimage.dev/abc123/vid-xyz789/original.m3u8"
}Response Fields
| Field | Type | Description |
|---|---|---|
object_url | String | Base URL for accessing the file |
original | String | URL to the original file |
optimised | String | URL to optimized WebP version (images only) |
object_id | String | Unique identifier (videos prefixed with "vid-") |
tags | Array | Tags associated with the file |
size | Number | File size in kilobytes |
type | String | MIME type of the uploaded file |
folder | String | Folder location (null if not specified) |
original_file_name | String | Original filename |
height | Number | Image height in pixels (images only) |
width | Number | Image width in pixels (images only) |
video_url | String | HLS streaming URL (videos only) |
Code Examples
javascript
async function uploadImage(file, tags = []) {
const formData = new FormData();
formData.append('file', file);
formData.append('tags', tags.join(','));
formData.append('folder', 'products');
const response = await fetch('https://api.reimage.dev/upload/', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.REIMAGE_API_KEY}`
},
body: formData
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.status}`);
}
return await response.json();
}
// Usage
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const result = await uploadImage(file, ['product', 'hero']);
console.log(`Uploaded: ${result.object_id}`);
console.log(`URL: ${result.object_url}`);javascript
import { useState } from 'react';
function ImageUploader() {
const [uploading, setUploading] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const handleUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
setUploading(true);
setError(null);
const formData = new FormData();
formData.append('file', file);
formData.append('tags', 'user-upload,gallery');
try {
const response = await fetch('https://api.reimage.dev/upload/', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.REACT_APP_REIMAGE_API_KEY}`
},
body: formData
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Upload failed');
}
const data = await response.json();
setResult(data);
} catch (err) {
setError(err.message);
} finally {
setUploading(false);
}
};
return (
<div>
<input
type="file"
accept="image/*,video/*"
onChange={handleUpload}
disabled={uploading}
/>
{uploading && <p>Uploading...</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}
{result && (
<div>
<p>Upload successful!</p>
<p>Object ID: {result.object_id}</p>
{result.height && (
<>
<p>Dimensions: {result.width} x {result.height}</p>
<img
src={`${result.object_url}w-400.webp`}
alt="Uploaded"
width="400"
/>
</>
)}
</div>
)}
</div>
);
}python
import requests
import os
def upload_image(file_path, tags=None, folder=None):
"""Upload an image to Reimage API"""
url = 'https://api.reimage.dev/upload/'
headers = {
'Authorization': f'Bearer {os.environ.get("REIMAGE_API_KEY")}'
}
with open(file_path, 'rb') as f:
files = {'file': (os.path.basename(file_path), f)}
data = {}
if tags:
data['tags'] = ','.join(tags)
if folder:
data['folder'] = folder
response = requests.post(url, files=files, data=data, headers=headers)
response.raise_for_status()
return response.json()
# Usage
result = upload_image(
'product-image.jpg',
tags=['product', 'featured'],
folder='products'
)
print(f"Uploaded: {result['object_id']}")
print(f"Size: {result['size']} KB")
print(f"Dimensions: {result['width']} x {result['height']}")
print(f"Optimized URL: {result['optimised']}")php
<?php
function uploadImage($filePath, $tags = [], $folder = null) {
$apiKey = getenv('REIMAGE_API_KEY');
$url = 'https://api.reimage.dev/upload/';
$cfile = new CURLFile($filePath, mime_content_type($filePath), basename($filePath));
$postData = ['file' => $cfile];
if (!empty($tags)) {
$postData['tags'] = implode(',', $tags);
}
if ($folder) {
$postData['folder'] = $folder;
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("Upload failed with status: $httpCode");
}
return json_decode($response, true);
}
// Usage
$result = uploadImage(
'product-image.jpg',
['product', 'featured'],
'products'
);
echo "Uploaded: " . $result['object_id'] . "\n";
echo "URL: " . $result['object_url'] . "\n";
?>go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strings"
)
type UploadResponse struct {
ObjectURL string `json:"object_url"`
Original string `json:"original"`
Optimised string `json:"optimised"`
ObjectID string `json:"object_id"`
Tags []string `json:"tags"`
Size float64 `json:"size"`
Type string `json:"type"`
Folder string `json:"folder"`
OriginalFileName string `json:"original_file_name"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
}
func uploadImage(apiKey, filePath string, tags []string, folder string) (*UploadResponse, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
if err != nil {
return nil, err
}
io.Copy(part, file)
if len(tags) > 0 {
writer.WriteField("tags", strings.Join(tags, ","))
}
if folder != "" {
writer.WriteField("folder", folder)
}
writer.Close()
req, err := http.NewRequest("POST", "https://api.reimage.dev/upload/", body)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("upload failed with status: %d", resp.StatusCode)
}
var result UploadResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return &result, nil
}
func main() {
apiKey := os.Getenv("REIMAGE_API_KEY")
result, err := uploadImage(
apiKey,
"product-image.jpg",
[]string{"product", "featured"},
"products",
)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Uploaded: %s\n", result.ObjectID)
fmt.Printf("Size: %.2f KB\n", result.Size)
fmt.Printf("Dimensions: %d x %d\n", result.Width, result.Height)
}rust
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
use reqwest::multipart;
use serde::{Deserialize, Serialize};
use std::fs::File;
#[derive(Debug, Deserialize, Serialize)]
struct UploadResponse {
object_url: String,
original: String,
optimised: Option<String>,
object_id: String,
tags: Vec<String>,
size: f64,
#[serde(rename = "type")]
file_type: String,
folder: Option<String>,
original_file_name: String,
height: Option<u32>,
width: Option<u32>,
}
async fn upload_image(
api_key: &str,
file_path: &str,
tags: Vec<&str>,
folder: Option<&str>,
) -> Result<UploadResponse, Box<dyn std::error::Error>> {
let file = File::open(file_path)?;
let file_name = std::path::Path::new(file_path)
.file_name()
.unwrap()
.to_str()
.unwrap();
let file_part = multipart::Part::reader(file)
.file_name(file_name.to_string())
.mime_str("image/jpeg")?;
let mut form = multipart::Form::new().part("file", file_part);
if !tags.is_empty() {
form = form.text("tags", tags.join(","));
}
if let Some(f) = folder {
form = form.text("folder", f.to_string());
}
let mut headers = HeaderMap::new();
headers.insert(
AUTHORIZATION,
HeaderValue::from_str(&format!("Bearer {}", api_key))?,
);
let client = reqwest::Client::new();
let response = client
.post("https://api.reimage.dev/upload/")
.headers(headers)
.multipart(form)
.send()
.await?;
if !response.status().is_success() {
return Err(format!("Upload failed: {}", response.status()).into());
}
let result = response.json::<UploadResponse>().await?;
Ok(result)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = std::env::var("REIMAGE_API_KEY")?;
let result = upload_image(
&api_key,
"product-image.jpg",
vec!["product", "featured"],
Some("products"),
)
.await?;
println!("Uploaded: {}", result.object_id);
println!("Size: {:.2} KB", result.size);
if let (Some(w), Some(h)) = (result.width, result.height) {
println!("Dimensions: {} x {}", w, h);
}
Ok(())
}Storage Limits
Uploads are subject to storage limits based on your subscription tier:
- Free Tier: 100 MB
- Pro Tier: 10 GB
- Enterprise: Custom limits
When you reach 95% of your limit, you'll receive an email notification.
Error Response (Limit Exceeded)
json
{
"error": "Storage limit exceeded"
}Status Code: 403 Forbidden
Video Processing
Videos are processed asynchronously using Mux:
- Upload returns immediately with a
video_url - Video is processed in the background
- HLS stream becomes available at the
video_url - You'll receive a webhook notification when processing completes (if configured)
Video limits are measured in minutes of video, not file size.
Custom Object IDs
You can specify a custom object ID instead of using the auto-generated one:
javascript
formData.append('object', 'my-custom-id');WARNING
Object IDs must be unique. If you use an existing ID, the upload will fail.
Organizing with Folders
Use folders to organize your files:
javascript
formData.append('folder', 'products/electronics');Folders are stored in metadata and can be used for filtering (coming soon).
Best Practices
- Compress before upload: Pre-compress large images to save bandwidth
- Use meaningful tags: Tags make searching and filtering easier
- Organize with folders: Group related assets together
- Handle errors gracefully: Always check response status and handle errors
Next Steps
- Get Images API - Retrieve uploaded images
- Image Transformations - Transform images on-the-fly
- Error Handling - Handle upload errors