Shadow Lancers
RESTful API Design: Best Practices for Scalable Web Services
Home/Blog/Web Development
Web Development

RESTful API Design: Best Practices for Scalable Web Services

Design APIs that developers love - covering URL structure, versioning, error handling, pagination, and security from years of real-world experience.

Shadow Lancers Team

Shadow Lancers Team

Oct 15, 202415 min read

Your API Is Your Product's Handshake

A well-designed API is a pleasure to integrate with. A poorly designed one creates support tickets, frustrated developers, and lost partnerships. We've built and consumed hundreds of APIs - here's what separates the good from the painful.

Core Principles

1. Resource-Based URL Design

URLs should represent resources (nouns), not actions (verbs):

❌ Bad✅ Good
GET /getUsersGET /users
POST /createOrderPOST /orders
PUT /updateUser/123PUT /users/123
DELETE /removeProduct/456DELETE /products/456

Nesting resources logically:

  • GET /users/123/orders - Orders belonging to user 123
  • GET /orders/456/items - Items in order 456
  • But avoid nesting deeper than 2 levels - it gets unwieldy

2. HTTP Methods - Use Them Correctly

MethodPurposeIdempotent?Response
GETRetrieve resource(s)Yes200 with data
POSTCreate a new resourceNo201 with created resource
PUTReplace entire resourceYes200 with updated resource
PATCHUpdate specific fieldsYes200 with updated resource
DELETERemove a resourceYes204 (no content)

3. Status Codes - Be Specific

Don't return 200 for everything. Status codes exist to communicate what happened:

CodeMeaningWhen to Use
200OKSuccessful GET, PUT, PATCH
201CreatedSuccessful POST
204No ContentSuccessful DELETE
400Bad RequestInvalid input data
401UnauthorizedMissing or invalid authentication
403ForbiddenAuthenticated but lacks permission
404Not FoundResource doesn't exist
409ConflictDuplicate resource or state conflict
422Unprocessable EntityValidation errors
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server failure

Pagination - Don't Return Everything

For any endpoint that returns a list, implement pagination:

{
  "data": [...],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 1543,
    "total_pages": 78
  },
  "links": {
    "self": "/api/v1/products?page=1",
    "next": "/api/v1/products?page=2",
    "last": "/api/v1/products?page=78"
  }
}

Cursor-based pagination is better for large datasets:

GET /api/v1/events?after=eyJpZCI6MTAwfQ&limit=20

Error Handling - Be Helpful

When something goes wrong, tell the developer exactly what happened and how to fix it:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The request contains invalid fields",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address",
        "received": "not-an-email"
      },
      {
        "field": "age",
        "message": "Must be a positive integer",
        "received": -5
      }
    ],
    "documentation": "https://api.example.com/docs/errors/validation"
  }
}

Versioning Strategy

APIs evolve. Breaking changes happen. How you version determines how painful upgrades are for your consumers.

ApproachExampleProsCons
URL path/api/v1/usersClear, easy to implementURLs change between versions
HeaderAccept: application/vnd.api.v2+jsonClean URLsHarder to test, discover
Query param/api/users?version=2Easy to implementCan be accidentally omitted

Our recommendation: URL path versioning. It's explicit, discoverable, and makes it obvious which version documentation you should be reading.

Security - Non-Negotiable

  • Always use HTTPS - no exceptions, even for internal APIs
  • Implement rate limiting - protect against abuse and accidental overload
  • Use OAuth 2.0 for authentication - don't build your own auth
  • Validate all inputs server-side - never trust the client
  • Don't expose internal errors - return generic messages to clients, log details internally
  • Implement API keys for third-party access - with scoping and rotation

Advanced Patterns

Filtering, Sorting, and Field Selection

GET /api/v1/products?category=electronics&price_min=100&sort=-created_at&fields=id,name,price

Bulk Operations

For APIs that need to process multiple items:

POST /api/v1/products/bulk
{
  "operations": [
    { "action": "create", "data": { "name": "Widget A" } },
    { "action": "update", "id": "123", "data": { "price": 29.99 } }
  ]
}

Webhooks for Real-Time Updates

Instead of clients polling your API, push events to them:

POST https://client-webhook-url.com/events
{
  "event": "order.completed",
  "data": { "order_id": "456", "total": 99.99 },
  "timestamp": "2025-01-15T14:30:00Z"
}

Conclusion

API design is product design. Invest time upfront in consistent naming, clear error messages, comprehensive documentation, and thoughtful versioning. The developers integrating with your API will thank you - and your support team will have fewer tickets.

Building APIs for your application? Our web development team designs and builds APIs that scale. Let's discuss your project.

API
REST
Backend
Web Development
Architecture

Enjoyed this article?

Share it with your network

Shadow Lancers Team

Written by

Shadow Lancers Team

Software & Digital Transformation Experts

Shadow Lancers is a software development and digital transformation company helping businesses build scalable, secure, and high-performance solutions since 2023.

Let's Build Something Great

Have a Project in Mind?

Let's discuss how we can help bring your ideas to life.