api-design

REST API design patterns including resource naming, status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.

INSTALLATION
npx skills add https://github.com/affaan-m/ecc --skill api-design
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

API Design Patterns

Conventions and best practices for designing consistent, developer-friendly REST APIs.

When to Activate

  • Designing new API endpoints
  • Reviewing existing API contracts
  • Adding pagination, filtering, or sorting
  • Implementing error handling for APIs
  • Planning API versioning strategy
  • Building public or partner-facing APIs

Resource Design

URL Structure

# Resources are nouns, plural, lowercase, kebab-case

GET    /api/v1/users

GET    /api/v1/users/:id

POST   /api/v1/users

PUT    /api/v1/users/:id

PATCH  /api/v1/users/:id

DELETE /api/v1/users/:id

# Sub-resources for relationships

GET    /api/v1/users/:id/orders

POST   /api/v1/users/:id/orders

# Actions that don't map to CRUD (use verbs sparingly)

POST   /api/v1/orders/:id/cancel

POST   /api/v1/auth/login

POST   /api/v1/auth/refresh

Naming Rules

# GOOD

/api/v1/team-members          # kebab-case for multi-word resources

/api/v1/orders?status=active  # query params for filtering

/api/v1/users/123/orders      # nested resources for ownership

# BAD

/api/v1/getUsers              # verb in URL

/api/v1/user                  # singular (use plural)

/api/v1/team_members          # snake_case in URLs

/api/v1/users/123/getOrders   # verb in nested resource

HTTP Methods and Status Codes

Method Semantics

MethodIdempotentSafeUse For
GETYesYesRetrieve resources
POSTNoNoCreate resources, trigger actions
PUTYesNoFull replacement of a resource
PATCHNo*NoPartial update of a resource
DELETEYesNoRemove a resource

*PATCH can be made idempotent with proper implementation

Status Code Reference

# Success

200 OK                    — GET, PUT, PATCH (with response body)

201 Created               — POST (include Location header)

204 No Content            — DELETE, PUT (no response body)

# Client Errors

400 Bad Request           — Validation failure, malformed JSON

401 Unauthorized          — Missing or invalid authentication

403 Forbidden             — Authenticated but not authorized

404 Not Found             — Resource doesn't exist

409 Conflict              — Duplicate entry, state conflict

422 Unprocessable Entity  — Semantically invalid (valid JSON, bad data)

429 Too Many Requests     — Rate limit exceeded

# Server Errors

500 Internal Server Error — Unexpected failure (never expose details)

502 Bad Gateway           — Upstream service failed

503 Service Unavailable   — Temporary overload, include Retry-After

Common Mistakes

# BAD: 200 for everything

{ "status": 200, "success": false, "error": "Not found" }

# GOOD: Use HTTP status codes semantically

HTTP/1.1 404 Not Found

{ "error": { "code": "not_found", "message": "User not found" } }

# BAD: 500 for validation errors

# GOOD: 400 or 422 with field-level details

# BAD: 200 for created resources

# GOOD: 201 with Location header

HTTP/1.1 201 Created

Location: /api/v1/users/abc-123

Response Format

Success Response

{

  "data": {

    "id": "abc-123",

    "email": "alice@example.com",

    "name": "Alice",

    "created_at": "2025-01-15T10:30:00Z"

  }

}

Collection Response (with Pagination)

{

  "data": [

    { "id": "abc-123", "name": "Alice" },

    { "id": "def-456", "name": "Bob" }

  ],

  "meta": {

    "total": 142,

    "page": 1,

    "per_page": 20,

    "total_pages": 8

  },

  "links": {

    "self": "/api/v1/users?page=1&per_page=20",

    "next": "/api/v1/users?page=2&per_page=20",

    "last": "/api/v1/users?page=8&per_page=20"

  }

}

Error Response

{

  "error": {

    "code": "validation_error",

    "message": "Request validation failed",

    "details": [

      {

        "field": "email",

        "message": "Must be a valid email address",

        "code": "invalid_format"

      },

      {

        "field": "age",

        "message": "Must be between 0 and 150",

        "code": "out_of_range"

      }

    ]

  }

}

Response Envelope Variants

// Option A: Envelope with data wrapper (recommended for public APIs)

interface ApiResponse<T> {

  data: T;

  meta?: PaginationMeta;

  links?: PaginationLinks;

}

interface ApiError {

  error: {

    code: string;

    message: string;

    details?: FieldError[];

  };

}

// Option B: Flat response (simpler, common for internal APIs)

// Success: just return the resource directly

// Error: return error object

// Distinguish by HTTP status code

Pagination

Offset-Based (Simple)

GET /api/v1/users?page=2&per_page=20

# Implementation

SELECT * FROM users

ORDER BY created_at DESC

LIMIT 20 OFFSET 20;

Pros: Easy to implement, supports "jump to page N"

Cons: Slow on large offsets (OFFSET 100000), inconsistent with concurrent inserts

Cursor-Based (Scalable)

GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20

# Implementation

SELECT * FROM users

WHERE id > :cursor_id

ORDER BY id ASC

LIMIT 21;  -- fetch one extra to determine has_next
{

  "data": [...],

  "meta": {

    "has_next": true,

    "next_cursor": "eyJpZCI6MTQzfQ"

  }

}

Pros: Consistent performance regardless of position, stable with concurrent inserts

Cons: Cannot jump to arbitrary page, cursor is opaque

When to Use Which

Use CasePagination Type

| Admin dashboards, small datasets ( Resource URL follows naming conventions (plural, kebab-case, no verbs)

  • Correct HTTP method used (GET for reads, POST for creates, etc.)
  • Appropriate status codes returned (not 200 for everything)
  • Input validated with schema (Zod, Pydantic, Bean Validation)
  • Error responses follow standard format with codes and messages
  • Pagination implemented for list endpoints (cursor or offset)
  • Authentication required (or explicitly marked as public)
  • Authorization checked (user can only access their own resources)
  • Rate limiting configured
  • Response does not leak internal details (stack traces, SQL errors)
  • Consistent naming with existing endpoints (camelCase vs snake_case)
  • Documented (OpenAPI/Swagger spec updated)
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card