I've reviewed 50+ REST APIs this year (for market research tools, scraping services, and internal tools). The same mistakes show up in almost every one.
Here are the 7 worst offenders.
1. Returning 200 for Errors
// BAD: Status 200
{"success": false, "error": "User not found"}
// GOOD: Status 404
{"error": "User not found", "code": "USER_NOT_FOUND"}
If you return 200 for errors, every client has to parse the body to know if the request worked. HTTP status codes exist for a reason.
2. No Pagination (or Bad Pagination)
// BAD: Returns all 50,000 records
GET /api/users
// GOOD: Cursor-based pagination
GET /api/users?limit=50&cursor=abc123
Offset pagination breaks at scale (try OFFSET 1000000 on a large table). Use cursor-based pagination from day one.
3. Nested URLs 4 Levels Deep
// BAD
GET /api/companies/123/departments/456/employees/789/reviews
// GOOD
GET /api/reviews?employee_id=789
Deep nesting makes URLs fragile and hard to cache. Flat is better.
4. No Rate Limiting
If your API has no rate limiting, it's not a matter of IF someone will DDoS you, but WHEN. Even a simple token bucket with 100 req/min is better than nothing.
# Redis rate limiter in 5 lines
import redis
r = redis.Redis()
def is_rate_limited(user_id, limit=100, window=60):
key = f"rate:{user_id}"
current = r.incr(key)
if current == 1:
r.expire(key, window)
return current > limit
5. Inconsistent Response Formats
// Endpoint 1 returns:
{"data": [{...}], "total": 100}
// Endpoint 2 returns:
{"results": [{...}], "count": 100}
// Endpoint 3 returns:
[{...}] // Just a raw array
Pick ONE format and use it everywhere:
{"data": [...], "meta": {"total": 100, "page": 1, "limit": 50}}
6. No Versioning
The day you need to change a response format, you'll break every client. Version from day one:
GET /api/v1/users ← current
GET /api/v2/users ← new format
Or use headers: Accept: application/vnd.myapi.v2+json
7. Exposing Internal IDs
// BAD: Sequential IDs leak information
{"id": 12847}
// Competitor can guess: you have ~12,847 users
// GOOD: UUIDs or hashids
{"id": "usr_a1b2c3d4e5"}
Sequential IDs tell competitors your user count, enable enumeration attacks, and leak business metrics.
Bonus: No CORS Headers
If your API is meant to be called from browsers and you forgot CORS headers, nothing works. Save yourself the debugging:
# Flask
from flask_cors import CORS
CORS(app)
# Express
app.use(cors())
The Quick Checklist
- [ ] Correct HTTP status codes
- [ ] Cursor-based pagination
- [ ] Flat URL structure
- [ ] Rate limiting
- [ ] Consistent response format
- [ ] API versioning
- [ ] No sequential IDs exposed
- [ ] CORS headers
What API mistakes would you add to this list?
I build API wrappers and data tools. Check my latest: Alpha Vantage Client | CoinGecko Client | All repos
Top comments (0)