API Documentation Best Practices

Philip Rehberger Feb 20, 2026 7 min read

Write documentation developers love. Create clear examples, interactive docs, and maintain accuracy over time.

API Documentation Best Practices

API documentation determines how quickly developers can integrate with your service. Poor documentation creates support burden, slows adoption, and frustrates users. Good documentation enables self-service, reduces integration time, and builds developer trust. The investment in documentation pays dividends through reduced support costs and increased adoption.

Documentation serves multiple audiences with different needs. New developers need getting started guides and tutorials. Experienced integrators need comprehensive reference documentation. Troubleshooters need error explanations and debugging guides. Designing for these different use cases improves documentation effectiveness.

Documentation Structure

Organize documentation into layers that serve different needs. The Divio documentation system provides a useful framework: tutorials, how-to guides, explanation, and reference.

Tutorials walk beginners through their first successful API call. They're learning-oriented and focus on doing rather than understanding. Keep tutorials simple and achievable in minutes.

# Quick Start: Your First API Call

This tutorial helps you make your first API call in under 5 minutes.

## Prerequisites
- An API key (get one at dashboard.example.com)
- cURL or any HTTP client

## Step 1: Set Up Authentication

Export your API key as an environment variable:
```bash
export API_KEY="your-api-key-here"

Step 2: Make Your First Request

Fetch your account details:

curl -H "Authorization: Bearer $API_KEY" \
  https://api.example.com/v1/account

You should see a response like:

{
  "id": "acct_123",
  "email": "developer@example.com",
  "plan": "developer"
}

Congratulations! You've made your first API call.


How-to guides solve specific problems. They're goal-oriented and assume basic familiarity. Focus on the task, not background explanation.

Reference documentation provides complete, accurate technical details. Every endpoint, parameter, and response field should be documented. Reference is meant for lookup, not reading.

Explanation (conceptual guides) provides background understanding. Authentication concepts, rate limiting philosophy, versioning strategy—these help developers make good decisions.

## OpenAPI Specification

OpenAPI (formerly Swagger) provides a standard format for API documentation. It enables automatic generation of interactive documentation, client libraries, and testing tools.

```yaml
openapi: 3.1.0
info:
  title: Orders API
  version: 1.0.0
  description: |
    The Orders API allows you to create, retrieve, and manage orders.

    ## Authentication
    All requests require a Bearer token in the Authorization header.

servers:
  - url: https://api.example.com/v1
    description: Production

paths:
  /orders:
    post:
      summary: Create an order
      description: |
        Creates a new order for the authenticated customer.

        Orders are created in `pending` status and must be confirmed
        within 30 minutes or they will be automatically cancelled.
      operationId: createOrder
      tags:
        - Orders
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
            examples:
              simple:
                summary: Simple order
                value:
                  items:
                    - product_id: "prod_123"
                      quantity: 2
              withShipping:
                summary: Order with shipping address
                value:
                  items:
                    - product_id: "prod_123"
                      quantity: 2
                  shipping_address:
                    line1: "123 Main St"
                    city: "San Francisco"
                    state: "CA"
                    postal_code: "94102"
      responses:
        '201':
          description: Order created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

components:
  schemas:
    CreateOrderRequest:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          description: Line items for the order
          minItems: 1
          items:
            $ref: '#/components/schemas/OrderItem'
        shipping_address:
          $ref: '#/components/schemas/Address'
          description: Required for physical products

    Order:
      type: object
      properties:
        id:
          type: string
          description: Unique order identifier
          example: "ord_abc123"
        status:
          type: string
          enum: [pending, confirmed, shipped, delivered, cancelled]
          description: Current order status
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'
        total:
          type: integer
          description: Total in cents
          example: 2499
        created_at:
          type: string
          format: date-time
          description: When the order was created

Code Examples

Code examples are the most-used part of documentation. Provide examples in multiple languages and make them copy-pasteable.

# Creating an Order

<tabs>
<tab title="cURL">
```bash
curl -X POST https://api.example.com/v1/orders \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {"product_id": "prod_123", "quantity": 2}
    ]
  }'

$response = $client->post('orders', [ 'json' => [ 'items' => [ ['product_id' => 'prod_123', 'quantity' => 2], ], ], ]);

$order = json_decode($response->getBody(), true);

</tab>

<tab title="Python">
```python
import requests

response = requests.post(
    'https://api.example.com/v1/orders',
    headers={'Authorization': f'Bearer {api_key}'},
    json={
        'items': [
            {'product_id': 'prod_123', 'quantity': 2}
        ]
    }
)

order = response.json()

Test your code examples. Broken examples destroy trust. Automate example testing in CI to catch drift.

// Test that documentation examples work
class DocumentationExampleTest extends TestCase
{
    /** @test */
    public function create_order_example_works(): void
    {
        $response = $this->postJson('/v1/orders', [
            'items' => [
                ['product_id' => 'prod_123', 'quantity' => 2],
            ],
        ]);

        $response->assertStatus(201)
            ->assertJsonStructure([
                'id',
                'status',
                'items',
                'total',
                'created_at',
            ]);
    }
}

Error Documentation

Document errors thoroughly. Developers spend significant time debugging errors; good error documentation reduces this time.

# Error Handling

The API uses standard HTTP status codes and returns detailed error objects.

## Error Response Format

```json
{
  "error": {
    "code": "invalid_parameter",
    "message": "The 'quantity' parameter must be a positive integer",
    "param": "items[0].quantity",
    "doc_url": "https://docs.example.com/errors/invalid_parameter"
  }
}

Common Errors

400 Bad Request

Code Description Resolution
invalid_parameter A parameter has an invalid value Check the param field and correct the value
missing_parameter A required parameter is missing Add the required parameter
invalid_json Request body is not valid JSON Verify JSON syntax

401 Unauthorized

Code Description Resolution
invalid_api_key The API key is invalid or revoked Generate a new API key in the dashboard
expired_token The access token has expired Refresh the token

429 Too Many Requests

Code Description Resolution
rate_limit_exceeded You've exceeded the rate limit Wait and retry with exponential backoff

The Retry-After header indicates how long to wait before retrying.


## Interactive Documentation

Interactive documentation lets developers try endpoints without writing code. Tools like Swagger UI, Redoc, or Stoplight render OpenAPI specs into interactive interfaces.

```php
// Serve OpenAPI spec for documentation tools
Route::get('/openapi.json', function () {
    return response()->file(base_path('docs/openapi.json'));
});

Route::get('/docs', function () {
    return view('api-docs', [
        'specUrl' => url('/openapi.json'),
    ]);
});

Provide a sandbox environment for safe experimentation:

# Sandbox Environment

Use the sandbox to test your integration without affecting real data.

**Sandbox URL:** `https://sandbox.api.example.com/v1/`

**Test Credentials:**
- API Key: `sk_test_sandbox123`
- Test credit card: `4242 4242 4242 4242`

The sandbox resets nightly. Don't store important data there.

Versioning Documentation

Document API versioning strategy clearly. Developers need to know how changes are communicated and how long old versions are supported.

# API Versioning

The API version is specified in the URL path (`/v1/`, `/v2/`).

## Version Lifecycle

| Version | Status | End of Life |
|---------|--------|-------------|
| v2 | Current | - |
| v1 | Deprecated | 2025-06-01 |

## Changelog

### v2.1.0 (2024-02-15)
- Added `metadata` field to orders
- `shipping_address` now accepts `country_code`

### v2.0.0 (2024-01-01)
**Breaking changes:**
- Removed `legacy_id` field from all resources
- Renamed `customer` to `customer_id`
- Changed error response format

See [migration guide](/docs/v1-to-v2) for upgrade instructions.

Maintaining Documentation

Documentation requires ongoing maintenance. Outdated documentation is worse than no documentation—it actively misleads.

Generate documentation from code where possible:

// Generate OpenAPI from route definitions
class OpenApiGenerator
{
    public function generate(): array
    {
        $routes = Route::getRoutes();

        foreach ($routes as $route) {
            if ($this->isApiRoute($route)) {
                $this->documentRoute($route);
            }
        }

        return $this->spec;
    }
}

Review documentation with code changes. Include documentation updates in pull request checklists.

Conclusion

API documentation is a product, not an afterthought. Structure documentation for different audiences and use cases. Use OpenAPI for standardized, tooling-friendly specs. Provide tested code examples in multiple languages. Document errors thoroughly. Make documentation interactive and provide sandboxes.

Good documentation reduces support costs, accelerates adoption, and builds developer trust. The effort invested in documentation returns value throughout the API's lifetime.

Share this article

Related Articles

Need help with your project?

Let's discuss how we can help you build reliable software.