Limits
301.so costs nothing at rest, and that is a design constraint, not an accident.
| Resource | Free ceiling | Actual use |
|---|---|---|
| Workers | 100 000 requests/day | tens |
| KV | 1 GB · 100k reads/day · 1 000 writes/day | written only when a link is created or changed |
| D1 | 5 GB · 5M rows read/day · 100k written/day | trivial |
| Analytics Engine | 100 000 data points/day | 1 per click |
Requests that match a file of the marketing build — the landing page, its CSS,
/docs/** — are served by Cloudflare’s asset layer before the worker is
invoked, and don’t count as requests at all.
The one that matters
Section titled “The one that matters”So the split is: KV is a read cache for resolution (l:{hostname}:{slug}),
written only on create or update; D1 is the source of truth and carries the
click counters; Analytics Engine carries the dimensions.
Application-level limits
Section titled “Application-level limits”GET /api/links |
200 links, newest first, no pagination |
GET /api/stats |
days clamped to 1–365 |
| Slug | ≤ 64 chars, [a-z0-9._-]+, lowercased, not reserved |
| Redirect status | 301, 302, 307, 308 — default 302 |
| Destination | http: and https: only |
| Waitlist email | ≤ 254 chars |
Default 302 is deliberate on a domain called 301.so: a 301 gets burned into
browser caches, and a destination has to stay changeable.
What the free tier costs in capability
Section titled “What the free tier costs in capability”D1 is SQLite, not Postgres: no jsonb, no extensions, one write region (EEUR),
migrations through wrangler. For a shortener — heavy reads, rare writes, a
trivial schema — none of that has bitten yet. It gets reopened the day 301.so
becomes a product with billing and real tenants.