DEV Community

Cover image for 7 API Design Mistakes That Make Your Users Hate You (and How to Fix Them)
MaxxMini
MaxxMini

Posted on • Edited on

7 API Design Mistakes That Make Your Users Hate You (and How to Fix Them)

Every developer has used a terrible API. Inconsistent naming, cryptic errors, pagination that makes no sense.

After reviewing 50+ production APIs (Stripe, GitHub, Twitch, and plenty of bad ones), I compiled the 7 most common design mistakes — and the exact patterns to fix them.


1. RPC-Style URLs Instead of Resources

❌ Bad:

POST /getUsers
POST /createUser
POST /deleteUser
Enter fullscreen mode Exit fullscreen mode

✅ Good:

GET    /users          → List
POST   /users          → Create
GET    /users/123      → Read
PATCH  /users/123      → Update
DELETE /users/123      → Delete
Enter fullscreen mode Exit fullscreen mode

HTTP methods are your verbs. URLs should be nouns. This is REST 101, but 40% of APIs I reviewed still get it wrong.

Exception: Actions that don not map to CRUD use a verb sub-resource:

POST /articles/42/publish
POST /payments/abc/refund
Enter fullscreen mode Exit fullscreen mode

2. Mixing Naming Conventions

{
  "firstName": "John",
  "last_name": "Doe",
  "Email-Address": "john@example.com"
}
Enter fullscreen mode Exit fullscreen mode

Pick ONE convention:

{
  "first_name": "John",
  "last_name": "Doe",
  "email_address": "john@example.com"
}
Enter fullscreen mode Exit fullscreen mode
Where Convention Example
URL paths kebab-case, plural /user-profiles
Query params snake_case ?sort_by=created_at
JSON fields snake_case first_name
Headers Title-Case X-Request-Id

Stripe, GitHub, and Slack all use snake_case for JSON. Google uses camelCase. Both are fine — mixing is not.


3. Useless Error Messages

{ "error": "Bad request" }
Enter fullscreen mode Exit fullscreen mode

vs.

{
  "error": {
    "code": "validation_error",
    "message": "Email format is invalid",
    "field": "email",
    "docs": "https://api.example.com/docs/errors#validation_error"
  }
}
Enter fullscreen mode Exit fullscreen mode

Great APIs include: what failed, where it failed, a machine-readable code, and a link to docs. Stripe error responses are the gold standard here.


4. No Pagination (or Bad Pagination)

GET /users?page=500&limit=20
Enter fullscreen mode Exit fullscreen mode

Offset pagination breaks when records are added/deleted between pages.

GET /users?limit=20&after=cursor_abc123

{
  "data": [...],
  "pagination": {
    "has_more": true,
    "next_cursor": "cursor_def456"
  }
}
Enter fullscreen mode Exit fullscreen mode

Cursor-based pagination is consistent and scales to millions of records.


5. No Versioning Strategy

Your API will change. Without versioning, every change risks breaking clients.

Strategy Example Pros Cons
URL path /v1/users Simple, explicit URL changes
Header Accept: application/vnd.api.v1+json Clean URLs Hidden
Query param /users?version=1 Easy to test Messy

Stripe uses URL versioning + date-based pinning. Each API key is pinned to a version. Most developer-friendly approach.


6. Authentication Afterthoughts

GET /users?api_key=sk_live_xxx
Enter fullscreen mode Exit fullscreen mode

API keys in URLs end up in logs, browser history, and referrer headers.

GET /users
Authorization: Bearer sk_live_xxx
Enter fullscreen mode Exit fullscreen mode

Auth checklist:

  • Never pass secrets in URLs
  • Use short-lived tokens + refresh flow
  • Return 401 for missing/invalid auth, 403 for insufficient permissions
  • Rate limit by API key, not by IP

7. Ignoring Idempotency

POST /payments { amount: 100 }
// network timeout... retry?
POST /payments { amount: 100 }  // DOUBLE CHARGE
Enter fullscreen mode Exit fullscreen mode

With idempotency keys:

POST /payments
Idempotency-Key: req_abc123
{ amount: 100 }
Enter fullscreen mode Exit fullscreen mode
Method Idempotent Safe to retry
GET Yes Yes
PUT Yes Yes
DELETE Yes Yes
POST No Need idempotency key
PATCH Yes Yes

Stripe, Shopify, and Square all support idempotency keys on POST requests. If your API handles money or creates resources, you need this.


The Full Picture

These 7 mistakes cover the surface. A production API also needs:

  • Rate limiting with clear headers (X-RateLimit-Remaining)
  • Consistent filtering/sorting (?status=active&sort=-created_at)
  • Bulk operations for performance
  • Webhook design for async events
  • GraphQL schema patterns
  • Real-world examples from Stripe, GitHub, Twitch

I build tools, games, and resources for developers — all free or pay-what-you-want:

🛠️ 27+ Free Developer Tools — JSON formatter, UUID generator, password analyzer, and more. All browser-based, no signup.

🎮 27 Browser Games — Built with vanilla JS. Play instantly, no install.

📚 Developer Resources on Gumroad — AI prompt packs, automation playbooks, and productivity guides.

7 patterns free above. The guide covers 40+ patterns across REST, GraphQL, auth, versioning, pagination, error handling, rate limiting, and production deployment.


More Developer Resources

If you found this useful:

Also check out DonFlow — a free budget drift detector that shows where your money plan diverges from reality.


What is the worst API you have ever used? Drop a comment — I want to hear the horror stories.


📘 Free Resource

If you are building with a $0 budget, I wrote a playbook about what works, what doesn't, and how to think about the $0 phase.

📥 The $0 Developer Playbook — Free (PWYW)

Want the deep dive? The Extended Edition ($7) includes a 30-day launch calendar, 5 copy templates, platform comparison matrix, and revenue math calculator.

Top comments (0)