mznah
Start a project
HomeServicesAboutPOSBlogContact
← Back to blog

API Design Guide — Building APIs Your Users Will Love

Mznah Engineering Team5 min readSoftware Development

API Design Guide: Building APIs Your Users Will Love

A poorly designed API is a tax on every developer who uses it. A well-designed API feels natural, is self-documenting, and scales without breaking. Here's how to build the latter.

REST API Principles

1. Use Nouns, Not Verbs

Bad:

```

GET /api/getUsers

GET /api/createUser

DELETE /api/deleteUserById?id=123

```

Good:

```

GET /api/users

POST /api/users

DELETE /api/users/123

```

The HTTP verb (GET, POST, DELETE, etc.) is your action. The URL is the resource.

2. HTTP Status Codes Matter

Don't use 200 for everything:

```

200 OK → Request succeeded

201 Created → Resource created

204 No Content → Success, no response body

400 Bad Request → Client error in request

401 Unauthorized → Authentication required

403 Forbidden → Authenticated but not allowed

404 Not Found → Resource doesn't exist

500 Server Error → Something broke on our side

```

Use the right status code. It tells clients what happened.

3. Consistent Response Format

Every response should follow the same structure:

```json

{

"success": true,

"data": {

"id": 123,

"name": "John Doe",

"email": "john@example.com"

},

"error": null

}

```

For errors:

```json

{

"success": false,

"data": null,

"error": {

"code": "INVALID_EMAIL",

"message": "Email is not valid"

}

}

```

Consistency makes client code predictable.

4. Pagination for Lists

Never return millions of records at once:

```

GET /api/users?page=1&limit=50

```

Response:

```json

{

"data": [/ 50 users /],

"pagination": {

"page": 1,

"limit": 50,

"total": 5000,

"pages": 100

}

}

```

Always include total count so clients know how many pages exist.

5. Filtering & Sorting

Let clients specify what data they want:

```

GET /api/users?status=active&sort=created_at:desc&limit=20

```

Key patterns:

  • `?status=active` — Filter by status

  • `?sort=name:asc` — Sort ascending
  • `?fields=name,email` — Only return specific fields
  • `?search=john` — Full-text search
  • Versioning Strategy

    APIs change. Plan for it:

    Option 1: URL Versioning (Simple)

    ```

    /api/v1/users

    /api/v2/users

    ```

    Option 2: Header Versioning (Clean)

    ```

    GET /api/users

    Header: Accept: application/json;version=2

    ```

    Best practice: Maintain two major versions. When you release v3, keep v2 alive for 12 months. Gives clients time to migrate.

    Documentation is Part of the API

    Your API is only as good as its documentation. Use OpenAPI/Swagger:

    ```yaml

    openapi: 3.0.0

    info:

    title: User API

    version: 1.0.0

    paths:

    /users:

    get:

    summary: List users

    parameters:

    - name: status

    in: query

    schema:

    type: string

    description: Filter by user status

    responses:

    '200':

    description: List of users

    ```

    Tools like Swagger UI auto-generate interactive documentation.

    Authentication & Authorization

    Don't Use Basic Auth in Production

    ```

    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

    ```

    Use JWT tokens or OAuth2. Key requirements:

  • HTTPS only — Never send tokens over HTTP
  • Short-lived tokens — 15 minutes to 1 hour
  • Refresh tokens — Long-lived token to get new access tokens
  • Rate limiting — Prevent brute force attacks
  • Revocation — Ability to invalidate tokens
  • Error Handling

    Don't leak internals:

    Bad:

    ```json

    {

    "error": "NullPointerException at line 42 in UserService.java"

    }

    ```

    Good:

    ```json

    {

    "error": {

    "code": "USER_NOT_FOUND",

    "message": "The specified user does not exist"

    }

    }

    ```

    Users don't need stack traces. Give them enough to understand what went wrong.

    Performance Considerations

    Caching Headers

    ```

    Cache-Control: public, max-age=3600

    ```

    Tells clients "you can cache this for 1 hour". Reduces server load.

    Compression

    Use gzip compression for responses:

    ```

    Content-Encoding: gzip

    ```

    Reduces payload size by 70-90%.

    Async Operations

    For long operations, don't make clients wait:

    ```

    POST /api/reports

    → 202 Accepted

    → Returns: { id: "report-123", status_url: "/api/reports/report-123/status" }

    Client polls: GET /api/reports/report-123/status

    → { status: "processing" } or { status: "completed", data: ... }

    ```

    API Security Checklist

    ✅ HTTPS everywhere

    ✅ Input validation (don't trust client input)

    ✅ Rate limiting

    ✅ Authentication required for sensitive endpoints

    ✅ Authorization (authenticated ≠ authorized)

    ✅ Avoid exposing sensitive data (passwords, PII)

    ✅ Log security events

    ✅ Update dependencies regularly

    ✅ Penetration testing

    ✅ Data encryption at rest

    Deprecation & Lifecycle

    Plan for sunset:

    1. Announce deprecation 6 months before

    2. Mark endpoints as deprecated in docs

    3. Return deprecation header

    4. Log which clients use old endpoints

    5. Turn off after 12 months

    ```

    Deprecation: true

    Sunset: Sun, 01 Jan 2025 00:00:00 GMT

    ```

    Testing Your API

    Don't just test success cases:

    ```

    ✅ Valid requests work

    ❌ Invalid requests return 400

    ❌ Missing authentication returns 401

    ❌ Unauthorized access returns 403

    ❌ Missing resources return 404

    ❌ Rate limiting works

    ❌ XSS/SQL injection blocked

    ```

    API Design Checklist

    ✅ Resource-oriented (nouns, not verbs)

    ✅ Correct HTTP status codes

    ✅ Consistent response format

    ✅ Pagination for lists

    ✅ Filtering/sorting support

    ✅ Versioning strategy

    ✅ OpenAPI documentation

    ✅ Authentication/Authorization

    ✅ Error handling with codes

    ✅ Caching headers

    ✅ Compression enabled

    ✅ Rate limiting

    ✅ Security hardened

    ✅ Deprecation plan

    Real-World Example

    At mznah, we built the MZ Listing API following these principles:

  • RESTful with consistent naming
  • Full OpenAPI documentation
  • JWT authentication
  • Pagination on all list endpoints
  • Meaningful error codes
  • 2-year backwards compatibility
  • Result: Developers love using it. Integration partners rarely complain about the API itself.

    Tools We Recommend

  • Design: Swagger/OpenAPI Editor
  • Testing: Postman, REST Client
  • Monitoring: Sentry, DataDog
  • Documentation: Swagger UI, ReadTheDocs
  • Security: OWASP ZAP, Snyk
  • Key Takeaway

    Your API is a product. Treat it like one. Design for your users, not just for your convenience. A good API scales, doesn't break, and feels natural to use.

    Want help designing your API? Let's talk.