api-best-practices

This skill provides comprehensive guidance for designing, implementing, and documenting RESTful APIs following industry best practices.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "api-best-practices" with this command: npx skills add webdevtodayjason/titanium-plugins/webdevtodayjason-titanium-plugins-api-best-practices

API Best Practices

This skill provides comprehensive guidance for designing, implementing, and documenting RESTful APIs following industry best practices.

RESTful Design Principles

Resource-Oriented Design

APIs should be designed around resources (nouns), not actions (verbs):

Good:

GET /api/v1/users POST /api/v1/users GET /api/v1/users/{id} PUT /api/v1/users/{id} DELETE /api/v1/users/{id}

Bad:

GET /api/v1/getUsers POST /api/v1/createUser POST /api/v1/updateUser POST /api/v1/deleteUser

HTTP Methods and Their Meanings

  • GET: Retrieve a resource (safe, idempotent, cacheable)

  • POST: Create a new resource (not idempotent)

  • PUT: Replace entire resource (idempotent)

  • PATCH: Partial update (not necessarily idempotent)

  • DELETE: Remove a resource (idempotent)

HTTP Status Codes

Success (2xx):

  • 200 OK : Successful GET, PUT, PATCH, DELETE

  • 201 Created : Successful POST with resource creation

  • 202 Accepted : Request accepted for async processing

  • 204 No Content : Successful DELETE or update with no response body

Client Errors (4xx):

  • 400 Bad Request : Malformed request, validation error

  • 401 Unauthorized : Authentication required

  • 403 Forbidden : Authenticated but not authorized

  • 404 Not Found : Resource doesn't exist

  • 409 Conflict : Resource conflict (duplicate, version mismatch)

  • 422 Unprocessable Entity : Valid syntax but semantic errors

  • 429 Too Many Requests : Rate limit exceeded

Server Errors (5xx):

  • 500 Internal Server Error : Unexpected server error

  • 502 Bad Gateway : Upstream service failure

  • 503 Service Unavailable : Temporary overload or maintenance

  • 504 Gateway Timeout : Upstream timeout

API Versioning

URL Versioning (Recommended)

GET /api/v1/users GET /api/v2/users

Pros: Clear, easy to route, visible in logs Cons: Duplicate code across versions

Header Versioning

GET /api/users Accept: application/vnd.myapi.v1+json

Pros: Clean URLs Cons: Harder to test, less visible

Version Management Rules

  • Never break backwards compatibility in same version

  • Deprecate old versions with advance notice (6-12 months)

  • Document migration guides between versions

  • Support at least 2 major versions simultaneously

Request/Response Patterns

Standard Request Format

JSON Request Body:

{ "email": "user@example.com", "name": "John Doe", "preferences": { "newsletter": true, "notifications": false } }

Query Parameters (for filtering, pagination, sorting):

GET /api/v1/users?role=admin&status=active&page=2&limit=20&sort=-created_at

Standard Response Format

Success Response:

{ "data": { "id": "user_123", "email": "user@example.com", "name": "John Doe", "createdAt": "2025-10-16T10:30:00Z" } }

Error Response:

{ "error": { "code": "INVALID_EMAIL", "message": "Email address is invalid", "field": "email", "details": "Email must contain @ symbol" } }

Collection Response with Pagination:

{ "data": [ { "id": 1, "name": "User 1" }, { "id": 2, "name": "User 2" } ], "pagination": { "page": 2, "limit": 20, "total": 156, "totalPages": 8, "hasNext": true, "hasPrev": true }, "links": { "self": "/api/v1/users?page=2", "next": "/api/v1/users?page=3", "prev": "/api/v1/users?page=1", "first": "/api/v1/users?page=1", "last": "/api/v1/users?page=8" } }

Authentication Patterns

JWT (JSON Web Tokens)

Login Flow:

POST /api/v1/auth/login { "email": "user@example.com", "password": "SecurePassword123" }

Response (200): { "accessToken": "eyJhbGc...", "refreshToken": "eyJhbGc...", "expiresIn": 900 }

Using Access Token:

GET /api/v1/users/me Authorization: Bearer eyJhbGc...

Token Refresh:

POST /api/v1/auth/refresh { "refreshToken": "eyJhbGc..." }

Response (200): { "accessToken": "eyJhbGc...", "expiresIn": 900 }

API Keys

Header-based (recommended):

GET /api/v1/data X-API-Key: sk_live_abc123xyz

Query parameter (less secure, use only for public data):

GET /api/v1/public-data?api_key=sk_live_abc123xyz

OAuth 2.0 Flows

Authorization Code Flow (for web apps):

  • Redirect to /oauth/authorize

  • User grants permission

  • Receive authorization code

  • Exchange code for access token at /oauth/token

  • Use access token for API requests

Client Credentials Flow (for server-to-server):

POST /oauth/token Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=abc&client_secret=xyz

Error Handling

Validation Errors

{ "error": { "code": "VALIDATION_ERROR", "message": "Request validation failed", "errors": [ { "field": "email", "message": "Email is required" }, { "field": "age", "message": "Age must be at least 18" } ] } }

Business Logic Errors

{ "error": { "code": "INSUFFICIENT_FUNDS", "message": "Account balance too low for this transaction", "details": { "balance": 50.00, "required": 100.00, "currency": "USD" } } }

Rate Limiting Errors

HTTP/1.1 429 Too Many Requests X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1634400000 Retry-After: 3600

{ "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "API rate limit exceeded", "retryAfter": 3600 } }

Pagination Strategies

Offset Pagination (Simple)

GET /api/v1/users?offset=40&limit=20

Pros: Simple, allows jumping to any page Cons: Performance degrades with large offsets, inconsistent if data changes

Cursor Pagination (Recommended for large datasets)

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

Response: { "data": [...], "pagination": { "nextCursor": "eyJpZCI6MTQzfQ", "hasMore": true } }

Pros: Consistent results, performant at any scale Cons: Can't jump to specific page

Page-Number Pagination (User-friendly)

GET /api/v1/users?page=3&limit=20

Pros: User-friendly, easy to understand Cons: Same issues as offset pagination

Rate Limiting

Implementation Pattern

Headers to include:

X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 999 X-RateLimit-Reset: 1634400000

Tiered Limits:

  • Anonymous: 100 requests/hour

  • Basic tier: 1,000 requests/hour

  • Pro tier: 10,000 requests/hour

  • Enterprise: Custom limits

Rate Limiting Algorithms

Token Bucket (recommended):

  • Allows bursts

  • Smooth long-term rate

  • Most flexible

Fixed Window:

  • Simple to implement

  • Can allow double limit at window boundaries

  • Less flexible

Sliding Window:

  • More accurate than fixed window

  • More complex to implement

  • Better user experience

API Security Best Practices

  1. Always Use HTTPS

Never send sensitive data over HTTP. Enforce HTTPS at the load balancer level.

  1. Validate All Inputs

from pydantic import BaseModel, EmailStr, constr

class UserCreate(BaseModel): email: EmailStr password: constr(min_length=8, max_length=100) name: constr(min_length=1, max_length=100)

  1. Sanitize Outputs

Prevent injection attacks by escaping output:

import html safe_output = html.escape(user_input)

  1. Use Parameterized Queries

✅ SAFE - Parameterized

cursor.execute("SELECT * FROM users WHERE email = ?", (email,))

❌ UNSAFE - String concatenation

cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")

  1. Implement CORS Properly

Be specific with origins

CORS(app, origins=["https://myapp.com", "https://app.myapp.com"])

❌ NEVER use wildcard in production

CORS(app, origins=["*"]) # DANGEROUS

  1. Authenticate Before Authorization

1. Verify JWT token (authentication)

2. Check user permissions (authorization)

3. Process request

  1. Log Security Events

logger.warning(f"Failed login attempt for {email} from {ip_address}") logger.critical(f"Privilege escalation attempt by user {user_id}")

  1. Rate Limit Authentication Endpoints

Prevent brute force attacks:

  • /auth/login : 5 attempts per 15 minutes per IP

  • /auth/register : 3 attempts per hour per IP

  • /auth/reset-password : 3 attempts per hour per email

OpenAPI/Swagger Documentation

OpenAPI 3.0 Example

openapi: 3.0.0 info: title: My API version: 1.0.0 description: API for managing users and posts

servers:

paths: /users: get: summary: List users operationId: listUsers tags: - Users parameters: - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 20 maximum: 100 responses: '200': description: Successful response content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/User' pagination: $ref: '#/components/schemas/Pagination' post: summary: Create user operationId: createUser tags: - Users requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserCreate' responses: '201': description: User created content: application/json: schema: $ref: '#/components/schemas/User' '400': description: Validation error content: application/json: schema: $ref: '#/components/schemas/Error'

components: schemas: User: type: object required: - id - email - name properties: id: type: string example: user_123 email: type: string format: email example: user@example.com name: type: string example: John Doe createdAt: type: string format: date-time example: 2025-10-16T10:30:00Z

UserCreate:
  type: object
  required:
    - email
    - password
    - name
  properties:
    email:
      type: string
      format: email
    password:
      type: string
      minLength: 8
      maxLength: 100
    name:
      type: string
      minLength: 1
      maxLength: 100

Pagination:
  type: object
  properties:
    page:
      type: integer
    limit:
      type: integer
    total:
      type: integer
    totalPages:
      type: integer
    hasNext:
      type: boolean
    hasPrev:
      type: boolean

Error:
  type: object
  properties:
    error:
      type: object
      properties:
        code:
          type: string
        message:
          type: string
        details:
          type: object

securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT

security:

  • BearerAuth: []

API Endpoint Design Patterns

Collection and Resource Endpoints

Collection operations

GET /api/v1/posts # List posts POST /api/v1/posts # Create post GET /api/v1/posts/{id} # Get specific post PUT /api/v1/posts/{id} # Replace post PATCH /api/v1/posts/{id} # Update post DELETE /api/v1/posts/{id} # Delete post

Nested resources

GET /api/v1/posts/{id}/comments # List comments for post POST /api/v1/posts/{id}/comments # Create comment on post GET /api/v1/comments/{id} # Get specific comment DELETE /api/v1/comments/{id} # Delete comment

Action Endpoints (When REST Isn't Enough)

Sometimes you need RPC-style endpoints for actions:

POST /api/v1/users/{id}/verify-email POST /api/v1/orders/{id}/cancel POST /api/v1/posts/{id}/publish POST /api/v1/invoices/{id}/send

Pattern: POST /{resource}/{id}/{action}

Use when:

  • Action doesn't fit CRUD model

  • State transitions need to be explicit

  • Business logic requires specific endpoint

Request Validation

Input Validation Pattern

from pydantic import BaseModel, EmailStr, Field, validator

class UserCreate(BaseModel): email: EmailStr password: str = Field(min_length=8, max_length=100) name: str = Field(min_length=1, max_length=100) age: int = Field(ge=18, le=120)

@validator('password')
def password_strength(cls, v):
    if not any(c.isupper() for c in v):
        raise ValueError('Password must contain uppercase letter')
    if not any(c.isdigit() for c in v):
        raise ValueError('Password must contain digit')
    return v

Validation Error Response

{ "error": { "code": "VALIDATION_ERROR", "message": "Request validation failed", "errors": [ { "field": "email", "message": "Email is required", "code": "REQUIRED_FIELD" }, { "field": "password", "message": "Password must contain uppercase letter", "code": "INVALID_FORMAT" } ] } }

Filtering, Sorting, Searching

Filtering

Single filter

GET /api/v1/posts?status=published

Multiple filters (AND)

GET /api/v1/posts?status=published&author=john

Multiple values (OR)

GET /api/v1/posts?tags=tech,ai,ml

Range filters

GET /api/v1/posts?created_after=2025-01-01&created_before=2025-12-31

Sorting

Single field ascending

GET /api/v1/posts?sort=created_at

Single field descending

GET /api/v1/posts?sort=-created_at

Multiple fields

GET /api/v1/posts?sort=-priority,created_at

Searching

Full-text search

GET /api/v1/posts?q=machine+learning

Field-specific search

GET /api/v1/posts?title=contains:machine&author=starts_with:john

Idempotency

Idempotent Operations (Safe to Retry)

  • GET, PUT, DELETE: Always idempotent

  • POST: Not idempotent by default

Idempotency Keys for POST

POST /api/v1/payments Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

{ "amount": 100.00, "currency": "USD", "description": "Payment for order #123" }

Server stores idempotency key:

  • First request: Process and return 201

  • Duplicate requests with same key: Return cached 201 response

  • Different request with same key: Return 409 Conflict

Async Operations

Long-Running Tasks

POST /api/v1/reports/generate { "type": "annual_summary", "year": 2025 }

Response (202 Accepted): { "id": "job_abc123", "status": "processing", "statusUrl": "/api/v1/jobs/job_abc123" }

Check Status

GET /api/v1/jobs/job_abc123

Response: { "id": "job_abc123", "status": "completed", "result": { "reportUrl": "/api/v1/reports/annual_summary_2025.pdf" }, "createdAt": "2025-10-16T10:00:00Z", "completedAt": "2025-10-16T10:05:00Z" }

Status values: queued , processing , completed , failed

Webhooks

Webhook Payload

{ "event": "user.created", "timestamp": "2025-10-16T10:30:00Z", "id": "evt_abc123", "data": { "id": "user_123", "email": "user@example.com", "name": "John Doe" } }

Webhook Security

HMAC Signature:

POST https://customer.com/webhooks X-Webhook-Signature: sha256=abc123...

Verify signature

import hmac import hashlib

def verify_webhook(payload, signature, secret): expected = hmac.new( secret.encode(), payload.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(f"sha256={expected}", signature)

API Performance Best Practices

  1. Use ETags for Caching

GET /api/v1/users/123 ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

Client sends If-None-Match on subsequent requests

GET /api/v1/users/123 If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

Response: 304 Not Modified (if unchanged)

  1. Implement Field Selection

Get only specific fields

GET /api/v1/users/123?fields=id,email,name

Response: { "id": "user_123", "email": "user@example.com", "name": "John Doe" }

  1. Use Compression

Accept-Encoding: gzip, deflate

Server should compress responses >1KB.

  1. Batch Operations

Instead of N individual requests

GET /api/v1/users/1 GET /api/v1/users/2 GET /api/v1/users/3

Use batch endpoint

GET /api/v1/users?ids=1,2,3

  1. Database Query Optimization
  • Use database indexes on filter fields

  • Limit result set size (max 100 items per page)

  • Use connection pooling

  • Implement query caching for expensive queries

HATEOAS (Hypermedia)

Including Links in Responses

{ "data": { "id": "user_123", "email": "user@example.com", "name": "John Doe" }, "links": { "self": "/api/v1/users/123", "posts": "/api/v1/users/123/posts", "comments": "/api/v1/users/123/comments", "avatar": "/api/v1/users/123/avatar" } }

Benefits:

  • Self-documenting API

  • Clients discover available actions

  • API evolution easier

Content Negotiation

Request Format

Content-Type: application/json Accept: application/json

Support Multiple Formats (Optional)

Request JSON

Accept: application/json

Request XML

Accept: application/xml

Request CSV

Accept: text/csv

Deprecation Strategy

Announce Deprecation

GET /api/v1/old-endpoint Sunset: Sat, 31 Dec 2025 23:59:59 GMT Deprecation: Tue, 1 Oct 2025 00:00:00 GMT Link: </api/v2/new-endpoint>; rel="alternate"

Migration Guide

Provide clear migration path:

  • Announce deprecation 6-12 months in advance

  • Provide migration guide with code examples

  • Support old and new versions simultaneously

  • Monitor usage of deprecated endpoints

  • Send email notifications to API consumers

  • Finally remove deprecated endpoint

API Health and Status

Health Check Endpoint

GET /health

Response (200): { "status": "healthy", "version": "1.2.3", "timestamp": "2025-10-16T10:30:00Z" }

Readiness Check (Dependencies)

GET /health/ready

Response (200): { "status": "ready", "checks": { "database": "ok", "cache": "ok", "messageQueue": "ok", "externalAPI": "ok" } }

Response (503) if any dependency fails: { "status": "not_ready", "checks": { "database": "ok", "cache": "degraded", "messageQueue": "failed" } }

Testing APIs

Unit Testing Controllers

def test_create_user(): response = client.post("/api/v1/users", json={ "email": "test@example.com", "password": "SecurePass123", "name": "Test User" })

assert response.status_code == 201
assert response.json()["email"] == "test@example.com"
assert "password" not in response.json()  # Never return passwords

Integration Testing

def test_user_flow(): # Create user response = client.post("/api/v1/users", json=user_data) user_id = response.json()["id"]

# Login
response = client.post("/api/v1/auth/login", json={
    "email": user_data["email"],
    "password": user_data["password"]
})
token = response.json()["accessToken"]

# Access protected resource
response = client.get(
    f"/api/v1/users/{user_id}",
    headers={"Authorization": f"Bearer {token}"}
)
assert response.status_code == 200

Common API Mistakes to Avoid

  • Using GET for state changes: GET should be safe and idempotent

  • Returning sensitive data: Never return passwords, tokens, secrets

  • Inconsistent naming: Stick to camelCase or snake_case, not both

  • Missing error details: Provide helpful error messages

  • No rate limiting: Always implement rate limits

  • Exposing internal IDs: Use UUIDs or slugs for public APIs

  • No versioning: Always version from day one

  • Ignoring CORS: Configure properly for web clients

  • Poor pagination: Implement cursor-based for large datasets

  • No documentation: Always provide OpenAPI docs

When to Use This Skill

Use this skill when:

  • Designing new API endpoints

  • Implementing REST APIs

  • Reviewing API code

  • Creating API documentation

  • Troubleshooting API issues

  • Discussing authentication/authorization

  • Planning API versioning strategy

  • Implementing rate limiting

  • Handling errors in APIs

Remember: A well-designed API is intuitive, secure, performant, and well-documented. Follow these patterns to create APIs that developers love to use.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

frontend-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

clawhub-install

Download and install skills from ClawHub directly via curl, bypassing official CLI rate limits. Use when the user wants to install one or more ClawHub skills...

Registry SourceRecently Updated
0199
upupc
Coding

Homebrew Bridge

Expose Mac Homebrew tools like brew, gh, and other /opt/homebrew/bin CLIs on a Linux OpenClaw gateway by installing explicit same-LAN SSH wrappers with optio...

Registry SourceRecently Updated