Compare
Picking a file upload service means comparing pricing pages, limits and quickstarts that never line up. We did that work for six popular options and put the results side by side: what you pay, how big a file can be, how much code it takes to upload a file and get a URL, and what you would have to build yourself. Where we could not confirm something, we say so.
S3 is the standard for object storage, and it does that job very well: 50 TB objects, bucket versioning and every AWS integration. What it leaves to you is the upload layer: IAM keys, bucket policies, CORS rules, presigned URLs, a database of what you stored, and CloudFront if you want a CDN. MediaLit is that layer, and when you self-host it can run on top of your own S3 buckets.
| Feature | MediaLit | Amazon S3 |
|---|---|---|
| Signup required | Yes, free account | Yes, AWS account |
| Free plan | Basic: 1 GB storage, 50 MB max file | New accounts (since 15 Jul 2025): up to $200 in credits, free plan for 6 months |
| Paid plan price | Pro: $10/mo or $100/yr, 100 GB | Pay as you go: $0.023/GB-month (S3 Standard, us-east-1), plus requests and transfer |
| Max file size | 50 MB (Basic), 2 GB (Pro) | 50 TB per object (5 GB in a single PUT) |
| Setup time (est.) | ~5 min | 15 to 30 min |
| Lines of code (upload a file, get a URL) | 7 lines | 17 lines |
| SDK needed | No. REST API, or the optional medialit Node SDK | Yes in practice (@aws-sdk/client-s3, requests must be SigV4-signed) |
| Presigned / browser uploads | Yes: Upload signatures, React uploader, resumable | Partly: Yes, presigned URLs you generate, plus CORS setup |
| Private files with signed URLs | Yes: Expiring signed URLs on every plan | Yes: Yes, presigned GET URLs |
| CDN included | Yes: Delivery through a CDN | No: No, CloudFront is a separate service |
| Egress fees | Yes: None listed. Plans include unlimited uploads and downloads | No: $0.09/GB out to the internet after 100 GB/mo free |
| Open source / self-host | Yes: AGPL server, MIT clients. Self-host on any S3-compatible storage | No: No |
| AI-agent access (MCP, CLI, Claude connector) | Yes: Hosted MCP server (OAuth), CLI, official Claude connector | Yes: AWS MCP Server (listed in Claude's directory), AWS CLI |
| File versioning | No: No | Yes: Yes, bucket versioning |
import { MediaLit } from "medialit";
const medialit = new MediaLit(); // reads MEDIALIT_API_KEY
const { mediaId } = await medialit.upload("./photo.jpg", {
access: "public",
});
const media = await medialit.seal(mediaId); // keep it
console.log(media.file);Uploads start temporary. seal() keeps the file and returns its permanent URL.
import { readFile } from "node:fs/promises";
import {
S3Client,
PutObjectCommand,
} from "@aws-sdk/client-s3";
// reads AWS credentials from the environment;
// the bucket needs a public-read bucket policy
const s3 = new S3Client({ region: "us-east-1" });
await s3.send(
new PutObjectCommand({
Bucket: "my-bucket",
Key: "photo.jpg",
Body: await readFile("./photo.jpg"),
ContentType: "image/jpeg",
}),
);
console.log(
"https://my-bucket.s3.us-east-1.amazonaws.com/photo.jpg",
);Not counted: creating an IAM user, the public-read bucket policy, and CORS rules if the browser uploads directly.
MediaLit works with any S3-compatible storage. Self-hosted, you point it at your own AWS S3, Cloudflare R2 or MinIO buckets and keep the same API. The hosted service at medialit.cloud handles storage for you.
Yes. Data transfer from S3 to the internet is billed per GB after the first 100 GB a month (about $0.09/GB in the first tier). MediaLit's plans list unlimited uploads and downloads.
When you need versioning, objects larger than 2 GB, or deep AWS integration, and you are happy to build presigned uploads, CORS and cleanup yourself.
UploadThing is a polished upload service for TypeScript apps, with file routes and ready-made upload components. Both services start free and offer a $10/mo plan with 100 GB. The differences are in the details: MediaLit includes private files on its free plan, its server is open source and self-hostable, and agents can use it through a hosted MCP server and a CLI.
Read the full MediaLit vs UploadThing comparison| Feature | MediaLit | UploadThing |
|---|---|---|
| Signup required | Yes, free account | Yes |
| Free plan | Basic: 1 GB storage, 50 MB max file | 2 GB storage |
| Paid plan price | Pro: $10/mo or $100/yr, 100 GB | $10/mo for 100 GB. Usage-based from $25/mo for 250 GB, then $0.08/GB |
| Max file size | 50 MB (Basic), 2 GB (Pro) | UnclearSet per file route (defaults: 4 MB images, 16 MB video). Plan cap: Unclear |
| Setup time (est.) | ~5 min | ~5 to 10 min |
| Lines of code (upload a file, get a URL) | 7 lines | 10 lines |
| SDK needed | No. REST API, or the optional medialit Node SDK | Recommended (uploadthing). A REST API is documented |
| Presigned / browser uploads | Yes: Upload signatures, React uploader, resumable | Yes: Yes, file routes and client uploads |
| Private files with signed URLs | Yes: Expiring signed URLs on every plan | Partly: Paid plans only (from $10/mo), signed URLs up to 7 days |
| CDN included | Yes: Delivery through a CDN | Unclear |
| Egress fees | Yes: None listed. Plans include unlimited uploads and downloads | Yes: None listed. Free plan includes unlimited uploads and downloads |
| Open source / self-host | Yes: AGPL server, MIT clients. Self-host on any S3-compatible storage | Partly: Client SDKs are open source. The service is hosted only |
| AI-agent access (MCP, CLI, Claude connector) | Yes: Hosted MCP server (OAuth), CLI, official Claude connector | Partly: Canary MCP package only. No CLI or Claude connector found |
| File versioning | No: No | No: No |
import { MediaLit } from "medialit";
const medialit = new MediaLit(); // reads MEDIALIT_API_KEY
const { mediaId } = await medialit.upload("./photo.jpg", {
access: "public",
});
const media = await medialit.seal(mediaId); // keep it
console.log(media.file);Uploads start temporary. seal() keeps the file and returns its permanent URL.
import { readFile } from "node:fs/promises";
import { UTApi, UTFile } from "uploadthing/server";
const utapi = new UTApi(); // reads UPLOADTHING_TOKEN
const file = new UTFile(
[await readFile("./photo.jpg")],
"photo.jpg",
);
const { data, error } = await utapi.uploadFiles(file);
if (error) throw new Error(error.message);
console.log(data.ufsUrl);Server-side upload with UTApi. Browser uploads use a file router plus UploadThing's components.
At small sizes they are close. UploadThing's free plan has 2 GB of storage and MediaLit's has 1 GB. Both have a $10/mo plan with 100 GB. MediaLit includes private files on the free plan; UploadThing adds them on paid plans.
MediaLit, yes. The server is AGPL and runs on any S3-compatible storage. UploadThing's client libraries are open source, but the service itself is hosted only.
MediaLit has a hosted MCP server with OAuth, a CLI and an official Claude connector. At the time of writing, UploadThing has only a canary MCP package on npm.
Cloudinary is a full media platform: URL-based transformations, video streaming, AI tagging and a DAM. If you need on-the-fly image or video transforms, it does far more than MediaLit, which only makes thumbnails and optional WebP copies. If you need to store files and serve them, MediaLit is simpler and priced by storage instead of credits.
Read the full MediaLit vs Cloudinary comparison| Feature | MediaLit | Cloudinary |
|---|---|---|
| Signup required | Yes, free account | Yes |
| Free plan | Basic: 1 GB storage, 50 MB max file | 25 credits/mo (1 credit = 1 GB storage, 1 GB bandwidth or 1,000 transformations) |
| Paid plan price | Pro: $10/mo or $100/yr, 100 GB | Plus $89/mo billed yearly ($99 monthly). Advanced $249/mo ($224 yearly) |
| Max file size | 50 MB (Basic), 2 GB (Pro) | Free: 10 MB images, 100 MB video. Plus: 20 MB / 2 GB. Advanced: 40 MB / 4 GB |
| Setup time (est.) | ~5 min | ~5 min |
| Lines of code (upload a file, get a URL) | 7 lines | 4 lines |
| SDK needed | No. REST API, or the optional medialit Node SDK | No. REST API, SDKs available |
| Presigned / browser uploads | Yes: Upload signatures, React uploader, resumable | Yes: Yes, signed or unsigned uploads and an upload widget |
| Private files with signed URLs | Yes: Expiring signed URLs on every plan | Yes: Yes, private and authenticated assets with signed URLs |
| CDN included | Yes: Delivery through a CDN | Yes: Yes |
| Egress fees | Yes: None listed. Plans include unlimited uploads and downloads | Partly: Bandwidth uses credits (1 GB = 1 credit) |
| Open source / self-host | Yes: AGPL server, MIT clients. Self-host on any S3-compatible storage | No: No (SDKs are open source) |
| AI-agent access (MCP, CLI, Claude connector) | Yes: Hosted MCP server (OAuth), CLI, official Claude connector | Yes: MCP servers, CLI, official Claude connector |
| File versioning | No: No | Yes: Yes, backup with revisions (off by default) |
import { MediaLit } from "medialit";
const medialit = new MediaLit(); // reads MEDIALIT_API_KEY
const { mediaId } = await medialit.upload("./photo.jpg", {
access: "public",
});
const media = await medialit.seal(mediaId); // keep it
console.log(media.file);Uploads start temporary. seal() keeps the file and returns its permanent URL.
import { v2 as cloudinary } from "cloudinary";
// reads CLOUDINARY_URL
const result =
await cloudinary.uploader.upload("./photo.jpg");
console.log(result.secure_url);Uses the official Node SDK with CLOUDINARY_URL set in the environment.
No. MediaLit makes thumbnails for images and videos on upload and can convert images to WebP. It has no URL-based transformations. If you need those, Cloudinary is the better fit.
In credits. One credit is 1 GB of storage, 1 GB of bandwidth or 1,000 transformations. The free plan has 25 credits a month; Plus has 225 for $89/mo billed yearly. MediaLit charges by plan: Pro is $10/mo for 100 GB.
Yes. Both have an MCP server and an official connector in Claude's directory.
Filestack pairs uploads with a file picker that pulls from 15+ cloud sources, plus transformations, document conversion and AI add-ons. Paid plans start at $69/mo. MediaLit focuses on storing and serving files, with a $10/mo Pro plan, a React uploader with resumable uploads, and an open-source server.
Read the full MediaLit vs Filestack comparison| Feature | MediaLit | Filestack |
|---|---|---|
| Signup required | Yes, free account | Yes |
| Free plan | Basic: 1 GB storage, 50 MB max file | 1 GB storage, 1 GB bandwidth, 500 uploads/mo, 20 MB max file |
| Paid plan price | Pro: $10/mo or $100/yr, 100 GB | Start $69/mo (50 GB storage, 75 GB bandwidth). Grow $199/mo |
| Max file size | 50 MB (Basic), 2 GB (Pro) | 20 MB (Free), 1 GB (Start), 2 GB (Grow), 5 GB (Scale) |
| Setup time (est.) | ~5 min | ~5 min |
| Lines of code (upload a file, get a URL) | 7 lines | 6 lines |
| SDK needed | No. REST API, or the optional medialit Node SDK | Recommended (filestack-js). A REST API is available |
| Presigned / browser uploads | Yes: Upload signatures, React uploader, resumable | Yes: Yes, the File Picker uploads from the browser |
| Private files with signed URLs | Yes: Expiring signed URLs on every plan | Yes: Yes, with security policies and signatures |
| CDN included | Yes: Delivery through a CDN | Yes: Yes, integrated CDN |
| Egress fees | Yes: None listed. Plans include unlimited uploads and downloads | Partly: Bandwidth quota per plan, then $0.20/GB on Start |
| Open source / self-host | Yes: AGPL server, MIT clients. Self-host on any S3-compatible storage | No: No (SDKs are open source) |
| AI-agent access (MCP, CLI, Claude connector) | Yes: Hosted MCP server (OAuth), CLI, official Claude connector | Partly: Local MCP server via a Claude Code plugin, CLI. No Claude directory connector found |
| File versioning | No: No | Unclear |
import { MediaLit } from "medialit";
const medialit = new MediaLit(); // reads MEDIALIT_API_KEY
const { mediaId } = await medialit.upload("./photo.jpg", {
access: "public",
});
const media = await medialit.seal(mediaId); // keep it
console.log(media.file);Uploads start temporary. seal() keeps the file and returns its permanent URL.
import * as filestack from "filestack-js";
const client = filestack.init(
process.env.FILESTACK_API_KEY,
);
const file = await client.upload("./photo.jpg");
console.log(file.url);filestack-js treats the argument as a file path in Node and as a Blob in the browser.
Filestack's Start plan is $69/mo for 50 GB of storage, 75 GB of bandwidth and files up to 1 GB. MediaLit Pro is $10/mo for 100 GB and files up to 2 GB.
MediaLit has a drop-in React uploader that sends files straight from the browser over resumable uploads. It does not pull files from third-party sources the way Filestack's picker does.
Filestack: 1 GB storage, 1 GB bandwidth, 500 uploads a month and 20 MB files. MediaLit: 1 GB storage and 50 MB files, with unlimited uploads and downloads listed.
Vercel Blob is the natural pick inside a Vercel project: files up to 5 TB, Vercel's CDN, and since June 2026 private stores with signed URLs. It is billed by usage: storage, operations and data transfer. MediaLit is a flat plan that works the same from any host or framework, with a dashboard, temporary uploads that clean themselves up, and agent access over MCP.
| Feature | MediaLit | Vercel Blob |
|---|---|---|
| Signup required | Yes, free account | Yes, Vercel account |
| Free plan | Basic: 1 GB storage, 50 MB max file | Hobby: 1 GB storage, 10 GB transfer, 10k simple and 2k advanced operations |
| Paid plan price | Pro: $10/mo or $100/yr, 100 GB | Pro $20/mo platform fee (includes $20 usage credit), then usage: $0.023/GB-month storage |
| Max file size | 50 MB (Basic), 2 GB (Pro) | 5 TB (multipart recommended over 100 MB) |
| Setup time (est.) | ~5 min | ~5 min (with an existing Vercel project) |
| Lines of code (upload a file, get a URL) | 7 lines | 8 lines |
| SDK needed | No. REST API, or the optional medialit Node SDK | Yes in practice (@vercel/blob) or the Vercel CLI |
| Presigned / browser uploads | Yes: Upload signatures, React uploader, resumable | Yes: Yes, client uploads and signed PUT URLs |
| Private files with signed URLs | Yes: Expiring signed URLs on every plan | Yes: Yes, private stores and signed URLs (up to 7 days) |
| CDN included | Yes: Delivery through a CDN | Yes: Yes |
| Egress fees | Yes: None listed. Plans include unlimited uploads and downloads | Partly: Hobby: 10 GB included. Pro: regional rate, e.g. $0.05/GB in iad1 |
| Open source / self-host | Yes: AGPL server, MIT clients. Self-host on any S3-compatible storage | No: No (SDK is open source) |
| AI-agent access (MCP, CLI, Claude connector) | Yes: Hosted MCP server (OAuth), CLI, official Claude connector | Yes: Vercel MCP, Vercel CLI (vercel blob), official Claude connector |
| File versioning | No: No | No: No |
import { MediaLit } from "medialit";
const medialit = new MediaLit(); // reads MEDIALIT_API_KEY
const { mediaId } = await medialit.upload("./photo.jpg", {
access: "public",
});
const media = await medialit.seal(mediaId); // keep it
console.log(media.file);Uploads start temporary. seal() keeps the file and returns its permanent URL.
import { readFile } from "node:fs/promises";
import { put } from "@vercel/blob";
// reads BLOB_READ_WRITE_TOKEN
const blob = await put(
"photo.jpg",
await readFile("./photo.jpg"),
{ access: "public" },
);
console.log(blob.url);Uses @vercel/blob with BLOB_READ_WRITE_TOKEN. On Vercel, the SDK can use OIDC instead.
You need a Vercel account and a Blob store. Code running outside Vercel authenticates with a BLOB_READ_WRITE_TOKEN. MediaLit works the same from any host, or you can self-host it.
Hobby includes 1 GB of storage and 10 GB of transfer. On Pro, the $20/mo platform fee includes $20 of usage credit, then storage, operations and data transfer are billed per use. MediaLit Pro is a flat $10/mo for 100 GB.
Yes. Vercel Private Blob became generally available in June 2026, with signed URLs that last up to 7 days. MediaLit serves private files through signed URLs that expire, on every plan.
Supabase Storage comes with a Postgres database, auth and row-level security, and like MediaLit it is open source and self-hostable. If you already run your app on Supabase, it is the obvious choice. MediaLit is storage only: no database project to manage, a drop-in React uploader, and temporary uploads that are deleted unless your app keeps them.
| Feature | MediaLit | Supabase Storage |
|---|---|---|
| Signup required | Yes, free account | Yes |
| Free plan | Basic: 1 GB storage, 50 MB max file | 1 GB storage, 5 GB egress, 50 MB max file. Projects pause after 1 week idle |
| Paid plan price | Pro: $10/mo or $100/yr, 100 GB | Pro from $25/mo: 100 GB storage, then $0.0213/GB |
| Max file size | 50 MB (Basic), 2 GB (Pro) | 50 MB (Free), 500 GB (Pro and Team) |
| Setup time (est.) | ~5 min | ~10 min (project, bucket and access policies) |
| Lines of code (upload a file, get a URL) | 7 lines | 16 lines |
| SDK needed | No. REST API, or the optional medialit Node SDK | Recommended (@supabase/supabase-js). A REST API is available |
| Presigned / browser uploads | Yes: Upload signatures, React uploader, resumable | Yes: Yes, signed upload URLs or row-level security |
| Private files with signed URLs | Yes: Expiring signed URLs on every plan | Yes: Yes, private buckets and signed URLs |
| CDN included | Yes: Delivery through a CDN | Yes: Yes, Basic CDN (Free), Smart CDN (Pro) |
| Egress fees | Yes: None listed. Plans include unlimited uploads and downloads | Partly: Free: 5 GB. Pro: 250 GB, then $0.09/GB ($0.03/GB cached) |
| Open source / self-host | Yes: AGPL server, MIT clients. Self-host on any S3-compatible storage | Yes: Yes, open source, self-host with Docker |
| AI-agent access (MCP, CLI, Claude connector) | Yes: Hosted MCP server (OAuth), CLI, official Claude connector | Yes: MCP server, Supabase CLI, official Claude connector |
| File versioning | No: No | Partly: Private alpha only |
import { MediaLit } from "medialit";
const medialit = new MediaLit(); // reads MEDIALIT_API_KEY
const { mediaId } = await medialit.upload("./photo.jpg", {
access: "public",
});
const media = await medialit.seal(mediaId); // keep it
console.log(media.file);Uploads start temporary. seal() keeps the file and returns its permanent URL.
import { readFile } from "node:fs/promises";
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY,
);
const bucket = supabase.storage.from("photos");
const { error } = await bucket.upload(
"photo.jpg",
await readFile("./photo.jpg"),
{ contentType: "image/jpeg" },
);
if (error) throw error;
console.log(
bucket.getPublicUrl("photo.jpg").data.publicUrl,
);Uses the service-role key on the server and a public bucket named photos.
Yes. Both can be self-hosted. Supabase self-hosts as a full backend with Docker; MediaLit needs PostgreSQL and two S3-compatible buckets.
Supabase: 1 GB of file storage, 5 GB of egress and 50 MB files, and free projects pause after a week of inactivity. MediaLit: 1 GB of storage and 50 MB files.
Yes. Both have an MCP server, a CLI and an official connector in Claude's directory.
Prices and limits checked on 19 October 2026 against each provider's pricing page and docs, in USD. Setup times are our conservative estimates for a developer new to the service. Lines of code count the non-blank, non-comment lines in the samples above. Spotted something out of date? Tell us on Discord.
Give your files one home
1 GB free, with the API, MCP server, CLI and dashboard on every plan.