Skip to main content
POST /upload accepts one or more files and an optional list of transformations. When transformations are provided, Openinary generates and caches each variant immediately — so the first user request is served from cache instead of triggering on-demand processing.

Endpoint

POST /upload
Authorization: Bearer <API_KEY>
Content-Type: multipart/form-data

Upload with prewarmed variants

curl -X POST "http://localhost:3000/upload" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "files=@./photo.jpg;type=image/jpeg" \
  -F 'transformations=["w_800,h_600,f_webp,q_80","w_400,h_400,c_fill,f_avif,q_70"]'
The transformations field accepts:
  • A JSON array of transformation strings
  • Multiple transformations form fields (one string per field)
  • A newline- or semicolon-separated string

Upload video with queued transformations

Video transformations run in a background queue — they don’t block the upload response.
curl -X POST "http://localhost:3000/upload" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "files=@./video.mp4;type=video/mp4" \
  -F 'transformations=["w_1280,h_720,q_75","w_640,h_360,q_60"]'
Even without a transformations field, Openinary automatically queues a default thumbnail for every video upload.

TypeScript examples

import { readFile } from "node:fs/promises";

const apiKey = process.env.OPENINARY_API_KEY;
if (!apiKey) throw new Error("Missing OPENINARY_API_KEY");

const bytes = await readFile("./photo.jpg");
const file = new File([bytes], "photo.jpg", { type: "image/jpeg" });

const form = new FormData();
form.append("files", file);
form.append(
  "transformations",
  JSON.stringify(["w_800,h_600,f_webp,q_80", "w_400,h_400,c_fill,f_avif,q_70"]),
);

const res = await fetch("http://localhost:3000/upload", {
  method: "POST",
  headers: { Authorization: `Bearer ${apiKey}` },
  body: form,
});

if (!res.ok) throw new Error(`Upload failed: ${res.status} ${await res.text()}`);

const data = await res.json();
console.log(data);

Response

{
  "success": true,
  "files": [
    {
      "filename": "photo.jpg",
      "path": "uploads/photo.jpg",
      "size": 248731,
      "url": "/t/uploads/photo.jpg",
      "prewarmedUrls": ["/t/w_800,h_600,f_webp,q_80/uploads/photo.jpg"]
    }
  ]
}
FieldTypeDescription
filenamestringOriginal filename
pathstringStorage path
sizenumberFile size in bytes
urlstringBase transformation URL
prewarmedUrlsstring[]Cached variants (images)
prewarmErrorsobject[]Prewarm failures per variant (images)
queuedTransformationUrlsstring[]Queued variants (videos)
queueErrorsobject[]Queue failures per variant (videos)

Limits

  • Max 20 transformations per upload request.
  • Max file size defaults to 50 MB. Configurable via MAX_FILE_SIZE_MB — useful for large video uploads in self-hosted setups.

Tips

  • Prewarm only the variants you actually serve — don’t prewarm speculatively.
  • Use consistent transformation strings across your frontend and backend to maximize cache hits.
  • For video, monitor the queue via GET /queue/stats during high-volume ingestion.

Next steps

Image Transformations

Full parameter reference for resizing, cropping, and format conversion.

Video Transformations

Resize, trim, and extract thumbnails from video files.