Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

API Design Mistakes I See Over and Over (and How to Fix Each One)

About Post

An API is a promise. The moment a mobile app ships against it, every field name, status code and quirk is frozen in somebody's code, often on phones you can't force to update.

That's what makes API mistakes so expensive. A bad internal function can be refactored on a Tuesday afternoon. A bad API response shape lives as long as the oldest app version still installed. Here are the mistakes I see over and over, in reviews, in third-party APIs I've had to integrate with, and in my own early work. For each one: what it looks like, why it hurts, and the fix.

Mistake 1: Inconsistent naming

What it looks like: one endpoint returns created_at, another createdAt, a third creation_date. Some URLs are /tenants, others /getTenantList.

Why it hurts: every consumer has to look up every field, every time. Mapping code fills up with special cases, and bugs hide in them.

The fix: pick conventions and write them down. Plural nouns for collections (/tenants, /tenants/42), HTTP methods for actions instead of verbs in URLs, one casing style for fields, one format for dates (ISO 8601 with a timezone, like 2025-05-14T09:12:43Z). Then enforce it in code review. Consistency matters more than which convention you pick.

Mistake 2: No versioning plan

What it looks like: /api/contracts, with no version anywhere, and a change that renames amount to total. The web app is fine because it deployed at the same time. The mobile app in the store breaks on the spot.

Why it hurts: you can't update every client at once. Mobile apps especially live on old versions for months.

The fix: version from day one (/api/v1/... is the simplest and most visible), and treat breaking changes as a new version. Most changes don't need one: adding a field is safe, removing or renaming one isn't. Prefer additive changes and you'll rarely need a v2 at all.

Mistake 3: Leaking your database through the API

What it looks like:

public function show(Tenant $tenant)
{
    return $tenant; // every column, every hidden flag, straight out
}

Why it hurts: your API is now your schema. Rename a column and you break clients. Add a sensitive column (an internal note, a verification token) and it's public by default unless someone remembered $hidden. Auto-increment IDs also leak information: anyone can guess that /invoices/1043 exists and roughly how many invoices you've issued.

The fix: return a deliberate shape, not a model. In Laravel that's an API resource, where every field is chosen on purpose. For public-facing identifiers, consider UUIDs or ULIDs (Laravel's HasUuids and HasUlids traits make this easy). And remember that unguessable IDs are not authorisation: every endpoint still needs a policy check.

Mistake 4: Wrong status codes

What it looks like:

HTTP/1.1 200 OK

{ "success": false, "error": "Contract not found" }

Why it hurts: HTTP clients, retry logic, monitoring and caches all read the status code. A 200 that means "failed" fools every one of them. Your error rate dashboard says everything's fine while users stare at errors.

The fix: use the codes that exist for exactly this. The short list covers most APIs:

CodeMeaning
200 / 201 / 204OK / created / done, nothing to return
400The request is malformed
401Not authenticated (who are you?)
403Authenticated, but not allowed
404Doesn't exist (or you're not allowed to know it exists)
409Conflicts with the current state (already paid, already signed)
422Validation failed
429Too many requests, slow down
500 / 503Our fault / temporarily unavailable

The 4xx versus 5xx split matters most: 4xx says "fix your request", 5xx says "try again later". Clients make retry decisions on that difference.

Mistake 5: No pagination

What it looks like: GET /api/payments returns every payment ever recorded. It's fast in development with twenty rows. In production it times out, or worse, quietly loads tens of thousands of models into memory.

Why it hurts: lists only grow. And adding pagination later is a breaking change, because clients assumed they got everything.

The fix: paginate every list endpoint from the first version, with a sensible default and a maximum page size. Laravel's paginate() or cursorPaginate() plus an API resource collection gives you the data, links and meta for free. Cursor pagination is the better fit for long, constantly changing lists such as feeds or activity logs.

Mistake 6: Errors nobody can act on

What it looks like: {"error": "Invalid request"}. Invalid how? Which field? Is it worth retrying?

Why it hurts: the mobile developer can't show a useful message, so the user sees "Something went wrong" and calls support.

The fix: one error format for the whole API, with a human message, a stable machine-readable code, and field-level details for validation:

{
  "message": "The move in date field must be a date after today.",
  "code": "validation_failed",
  "errors": {
    "move_in_date": ["The move in date field must be a date after today."]
  }
}

Laravel already returns message and errors with a 422 for validation, so you're most of the way there. Make your other errors follow the same shape. If you want a formal standard, RFC 9457 ("Problem Details for HTTP APIs") defines one.

The test I use: could a developer who has never seen your codebase build a client from your responses alone, and handle every error sensibly? If they'd need to ask you questions, the API isn't finished yet.

The quick checklist

  • One naming convention, written down and enforced.
  • A version in the URL, and additive changes by default.
  • Deliberate response shapes, never raw models.
  • Honest status codes.
  • Pagination on every list, from day one.
  • One error format, with codes clients can act on.

Which API mistake has cost you the most time, either in your own API or in one you had to integrate with? The "200 with an error inside" one still haunts me.

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close