Skip to content

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.

FeaturePutPutCloudinary
Signup required No Yes (account + API key)
Setup time< 1 minute10-20 minutes
Free tier storage10 GB25 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

PutPut
// 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());
Cloudinary
// 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.

Get started for free

No signup required. No credit card.

v0.4.77