PutPut vs the Alternatives
Developers waste hours evaluating file upload services. We did the work for you. Each comparison breaks down pricing, setup time, lock-in, and what actually matters when you just need to upload a file and get a URL.
PutPut vs Cloudinary
Cloudinary is a powerful media platform — image transforms, video processing, a full DAM. But if you just need to upload files and get a URL back, it's overkill. PutPut is purpose-built for the upload-and-serve use case.
| Feature | PutPut | Cloudinary |
|---|---|---|
| Signup required | No | Yes (account + API key) |
| Setup time | < 1 minute | 10-20 minutes |
| Free tier storage | 10 GB | 25 credits/mo (~25 GB, shared with transforms) |
| Egress fees | $0 | Credits consumed per delivery |
| Pricing model | Free (no paid tier yet) | From $89/mo |
| Media transformations | Not needed — upload & serve | Full pipeline (resize, crop, effects) |
| SDK required | No (plain HTTP) | Yes (cloudinary SDK) |
| AI-friendly docs | docs.putput.io | No |
| Framework lock-in | None (plain fetch) | None (SDK is framework-agnostic) |
| Direct-to-storage uploads | Yes (R2 presigned) | Yes (unsigned uploads) |
Code comparison
// Get a token (no signup)
const tok = await fetch('/api/v1/auth/guest',
{ method: 'POST' }).then(r => r.json());
// Upload a file
const pre = await fetch('/api/v1/upload/presign',
{ method: 'POST',
headers: { Authorization: `Bearer ${tok.token}` },
body: JSON.stringify({ filename: 'photo.jpg',
content_type: 'image/jpeg' }) }
).then(r => r.json());
await fetch(pre.upload_url,
{ method: 'PUT', body: file });
const result = await fetch('/api/v1/upload/confirm',
{ method: 'POST',
headers: { Authorization: `Bearer ${tok.token}` },
body: JSON.stringify({ upload_id: pre.upload_id }) }
).then(r => r.json());// Install cloudinary SDK, configure
import { v2 as cloudinary } from 'cloudinary';
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
});
// Server-side: generate signature
const timestamp = Math.round(Date.now() / 1000);
const signature = cloudinary.utils
.api_sign_request(
{ timestamp, folder: 'uploads' },
process.env.API_SECRET!
);
// Client-side: upload via form data
const form = new FormData();
form.append('file', file);
form.append('api_key', API_KEY);
form.append('timestamp', String(timestamp));
form.append('signature', signature);
form.append('folder', 'uploads');
const res = await fetch(
`https://api.cloudinary.com/v1_1/${CLOUD}/image/upload`,
{ method: 'POST', body: form }
);Frequently asked questions
Is Cloudinary overkill for simple file uploads?
If you just need to upload files and serve them via a URL, yes. Cloudinary is a full media platform with image/video transformations, a DAM, and delivery optimization. PutPut is purpose-built for the upload-and-serve use case — no extra complexity.
Does PutPut support image transformations?
No, and that's intentional. PutPut stores and serves your files as-is. If you need on-the-fly resizing or format conversion, Cloudinary is the better choice. But most apps handle transforms client-side or at build time.
How does Cloudinary's credit system work?
Cloudinary uses a credit-based system where storage, transformations, and bandwidth all consume credits from a shared pool. The free tier gives 25 credits/month. One credit roughly equals 1 GB of storage or 1 GB of delivery, but transformations cost additional credits.
Can I migrate from Cloudinary to PutPut?
Yes. Since PutPut uses standard HTTP uploads, you can re-upload your files using any script or tool. No SDK changes needed — just point your upload logic at the PutPut API.
No signup required. No credit card.