A SaaS vendor opens its API to integrators. Two weeks later, an ERP partner syncs 200,000 product records in a loop — without pagination — over a weekend. Monday morning: PostgreSQL at 100% CPU, cloud bill tripled, core customers timing out. The post-mortem reveals no quota, no alert, no documented 429.
Launching a public API is not exposing endpoints. It is accepting that strangers will control your load — sometimes maliciously, often through integration mistakes. Limits must exist before the first client, not after the first outage.
Four non-negotiable guardrails
| Guardrail | Without it | Minimum viable |
|---|---|---|
| Authentication | Anonymous abuse | API key or OAuth2 client credentials |
| Rate limiting | Infinite loops | Per key + per-IP fallback |
| Quotas | One client monopolizes | Documented req/day + burst/min |
| Observability | Blind post-mortem | Structured logs, p95 metrics, 429/5xx alerts |
An API without 429 confuses politeness with robustness. Rejecting cleanly protects everyone.
Rate limiting: tier strategy
Example public grid:
| Tier | Burst/min | Quota/day | Typical use |
|---|---|---|---|
| Sandbox | 30 | 1,000 | Dev, tests |
| Standard | 120 | 50,000 | SMB integrator |
| Partner | 600 | 500,000 | ERP, marketplace |
Implementation:
- Nginx
limit_req_zonefor global IP ceiling. - Redis counter per
api_keyfor daily quotas. - Gateway (Traefik, Kong) to centralize without app redeploy.
Respond with 429, Retry-After, and explicit JSON body — do not let clients guess from TCP timeout.
For technical detail, see Rate limiting: protect an API without punishing good clients.
Hosting sizing: the database matters more than API CPU
A "simple" API request can cost:
- 1 indexed SELECT → 2 ms
- 1 unindexed SELECT + JOIN → 800 ms × 500 req/s → death
Infra checklist:
- Mandatory pagination (
limitmax 100, cursor preferred). - Redis cache on idempotent reads (catalog, reference data).
- Connection pool (PgBouncer) — see Connection pooling.
- Separate workers for heavy sync vs interactive API.
- Autoscale on queue depth, not CPU alone.
Shared hosting: no for a serious public API. VPS minimum; cloud with load balancer from several paying clients.
Versioning, deprecation, and communication
Technical limits without product limits = debt:
- Fixed
/v1/prefix; breaking change =/v2/. Sunsetheader and email 90 days before removal.- Status page or incident RSS — integrators do not always read internal Slack.
Observability: metrics that alert before Twitter
- p95/p99 latency per endpoint (not average only).
- 429 rate per key — catches bad integration before saturation.
- Active DB connections vs pool max.
- Top consumers: weekly table of heaviest keys.
Alert if p95 > internal SLA for 5 min or if a key exceeds 80% quota at 2 p.m.
The peak: the worst client signed a contract
Marketing wants "open API." Finance wants predictability. Ops must enforce soft walls: high enough for good use, visible enough to stop errors before the database.
Decide and move forward without blind spots
- Publish limits + 429 examples before the first key.
- Load-test a "client that loops" scenario (not happy path only).
- Redis + PgBouncer before partner launch.
- Top consumers dashboard reviewed weekly the first month.
- Runbook: revoke a key, read-only degraded mode, status comms.
Compare VPS and cloud in our directory and compare tool.
Frequently asked questions
Do you need rate limiting from v1?
Yes — even modest — to prevent accidental loops and frame quotas early.
Where should rate limiting live?
Nginx + API key minimum; gateway for advanced policies; code for business rules.
How do you size hosting for an API?
Peak per client, target p95, DB cost/request; cache and pagination before horizontal scale.
What should you document before launch?
Limits per tier, 429, Retry-After, deprecation, incident status.
A mature public API rejects requests gracefully — before the database rejects everyone without warning.