Skip to content

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.

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.

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.

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.