I once watched a competitor's API get scraped clean in a weekend because they didn't implement rate limiting. Not by some sophisticated botnet — by a single engineer with a while loop and a free AWS account. Three million user profiles, complete with emails and phone numbers, sitting in a CSV by Monday morning. The API returned 200 OK for every request, never throttled, never flagged the pattern. When I told their CISO, he asked if we could help them "add some encryption." Encryption wouldn't have stopped that exfiltration. Rate limiting would have.
APIs are the connective tissue of modern software, and they're also the attack surface most teams underestimate. I've tested APIs that returned full database schemas in error messages, accepted SQL in query parameters, and authenticated users based on a header that said is_admin: false (guess what happened when you changed it to true). The OWASP API Security Top 10 exists for a reason. Let's talk about how to stay off it.
1. Rate Limiting Is About Economics, Not Politeness
Rate limiting isn't just preventing abuse — it's changing the attacker's cost equation. A credential stuffing attack that takes an hour with no limits becomes infeasible when you drop it to ten requests per minute per IP. I implement tiered rate limiting: stricter for unauthenticated endpoints, adaptive based on behavior patterns, and per-user limits for authenticated APIs. Tools like Redis with sliding window counters, or cloud-native solutions like AWS WAF and Cloudflare, make this straightforward. But the key is what happens when limits hit. Return 429 Too Many Requests with a Retry-After header. Log the event. Alert on sustained violations. One pattern I use: if an IP hits 50% of its limit consistently, that's not a power user—that's automation probing your defenses. Treat it accordingly.
2. Input Validation Is Your First and Last Line of Defense
APIs accept data from the internet. The internet is hostile. Every parameter, header, and body field is an attack vector until proven otherwise. I enforce validation at the edge: type checking, length limits, format validation, and allowlisting for enumerated values. A parameter that should be an integer between 1 and 100 gets validated as exactly that — not coerced, not guessed. For structured data like JSON, I use strict schema validation with tools like JSON Schema or OpenAPI spec enforcement. One vulnerability I found: an API accepted a limit parameter for pagination with no upper bound. Set it to 100,000, and the database choked for thirty seconds while dumping records. That's not a DoS—it's a design flaw that validation prevents.
3. Authentication and Authorization Are Separate, and Both Must Be Right
Broken authentication tops the OWASP API Top 10 for good reason. I see APIs that check for a valid JWT but never verify the signature. APIs that accept API keys in query parameters where they get logged by proxies. APIs with long-lived tokens that never rotate. The fix isn't complex: short-lived access tokens (15 minutes), refresh token rotation, proper signature verification with algorithm whitelisting, and never sending credentials in URLs.
But authentication without authorization is just as dangerous. I test for IDOR — Insecure Direct Object Reference — on every API I review. Can User A access User B's data by changing a URL parameter? Can a standard user hit admin endpoints by guessing the path? I enforce authorization at every layer: route-level middleware for coarse checks, service-level logic for fine-grained permissions, and database queries that include user context so even a bypassed middleware layer can't leak cross-tenant data. Defense in depth isn't paranoia when the alternative is a GDPR fine.
4. Error Messages Leak Information — Control Them
A 500 Internal Server Error with a full stack trace is a gift to an attacker. It reveals framework versions, database schemas, internal paths, and sometimes raw SQL. I configure APIs to return generic error messages to clients and log detailed diagnostics server-side. "Invalid request" to the user. Full context to the SIEM. Same for validation errors — don't echo back the invalid input in the response if it's crafted to test for injection vulnerabilities. One API I tested returned "Column 'password' does not exist in table 'users'" when I sent a malformed query. That's not debugging. That's reconnaissance assistance.
5. Inventory Your APIs or Someone Else Will
You can't secure what you don't know exists. Shadow APIs — endpoints deployed but not documented, deprecated but not decommissioned, internal but exposed to the internet — are a growing problem. I implement API discovery through traffic analysis, OpenAPI spec generation from code, and regular external reconnaissance. One engagement found a /v1/admin endpoint that had been superseded by /v2/admin but never removed. It lacked the authentication middleware added in v2. Anyone who knew the old path had admin access. API gateways help here: centralize routing, enforce policies consistently, and provide a chokepoint for logging and monitoring. But gateways aren't a substitute for knowing your own surface area.
The Takeaway
API security isn't a checklist you complete once. It's a posture you maintain as your surface evolves. Rate limit aggressively to raise attacker costs. Validate every input like it's malicious. Separate and strengthen authentication and authorization. Control error verbosity. Know what APIs you actually have exposed. The OWASP API Top 10 isn't theoretical — it's a catalog of real breaches, and most of them start with basic oversights. Build APIs that assume they're under attack, because they are. The teams that get this right don't have fewer vulnerabilities because they're smarter. They have fewer vulnerabilities because they designed for abuse from day one.