Incorporating Code Samples in API Documentation

API7.ai

July 21, 2026

API 101

Key Takeaways

  • Design samples around developer tasks. A small, runnable path from authentication to a meaningful result is more useful than one disconnected snippet for every method.
  • Show raw HTTP and selected idiomatic languages. Raw requests reveal the contract; language examples teach safe use of the ecosystem developers actually work in.
  • Make prerequisites and placeholders explicit. State runtime and SDK versions, environment setup, permissions, test data, expected output, and cleanup.
  • Treat copied code as production-bound. Use secure credential handling, timeouts, status checks, bounded retries, and idempotency where the scenario requires them.
  • Test samples continuously. Syntax checks are the minimum; high-value examples should run against a controlled environment and validate meaningful responses.

Code samples are often the shortest path between understanding an API and trusting it. A developer can infer authentication, URL structure, serialization, and response handling from a good example in seconds. A bad sample does more damage just as quickly: it normalizes insecure credentials, ignores failures, uses obsolete SDKs, or cannot run without undocumented setup.

The goal is not to maximize the number of language tabs. It is to give each target audience the smallest complete example that teaches the right behavior and remains correct as the API evolves.

Plan a Code Sample Portfolio

Start with tasks, not endpoints. Endpoint-level snippets are valuable reference material, but a developer journey crosses endpoints: obtain credentials, create a resource, store its ID, retrieve status, handle a failure, and clean up test data.

Organize samples in three layers:

  1. Request snippets show the exact wire-level call for one operation.
  2. Task examples combine a few calls into a useful outcome, such as creating and confirming an order.
  3. Sample applications demonstrate architecture, configuration, callbacks, persistence, observability, and deployment for an end-to-end use case.
flowchart TB
    R["Request snippets"] --> T["Task-focused examples"]
    T --> A["End-to-end sample applications"]
    R -. "Precise contract" .-> V["API reference"]
    T -. "Fast learning" .-> Q["Quick starts and guides"]
    A -. "Production patterns" .-> C["Architecture tutorials"]

    classDef small fill:#e8f3ff,stroke:#1677ff,color:#102a43;
    classDef medium fill:#fff7e6,stroke:#d48806,color:#5c3b00;
    classDef large fill:#f6ffed,stroke:#389e0d,color:#173b0b;
    class R,V small;
    class T,Q medium;
    class A,C large;

Use audience and support data to decide language coverage. Raw HTTP with curl should usually be available because it exposes headers and payloads without an SDK abstraction. Add languages that represent real users, not every language a generator can emit. A small set of reviewed, idiomatic examples is better than twelve mechanically translated snippets that drift.

For each sample, record an owner, API version, language and dependency versions, tested environment, last verification time, and retirement trigger. That inventory makes maintenance measurable.

Make Every Sample Self-Contained

A copyable sample needs context immediately around the code. Before the block, state:

  • what the sample accomplishes;
  • required account, permission, and test resource;
  • runtime and dependency versions;
  • environment variables or configuration to set;
  • whether the target is sandbox or production;
  • what the caller may create, change, or delete.

After the block, show expected output and explain the few lines that matter. If output contains variable IDs or timestamps, label them as examples rather than promising exact values.

Google's developer documentation style guide recommends introducing samples, following language-specific style, keeping formatting readable, and marking omitted code with a language-appropriate comment rather than an ambiguous ellipsis. These practices make the boundary between runnable code and illustrative fragments clear.

Use Descriptive Placeholders

Placeholders should tell developers what to replace. ACCESS_TOKEN, ORDER_ID, and API_BASE_URL are more useful than xxx, <foo>, or unexplained sample values. Explain how to obtain each value.

Prefer environment variables for secrets:

export API_BASE_URL='https://sandbox.example.com' export ACCESS_TOKEN='replace-with-a-sandbox-token' curl --fail-with-body --silent --show-error \ --request GET "${API_BASE_URL}/orders/ord_123" \ --header "Authorization: Bearer ${ACCESS_TOKEN}" \ --header 'Accept: application/json'

The example still warns that the token is a placeholder, but it does not teach readers to paste a real secret into source code or a committed configuration file. Production documentation should also link to credential rotation and least-privilege guidance.

Do not use real customer records, email addresses, API keys, or production resource IDs in screenshots or output. Synthetic data should look realistic enough to teach the schema while being unmistakably fictional.

Show the Contract Before the Abstraction

Raw HTTP answers important questions: which method is sent, how the version is selected, which headers are required, what JSON crosses the network, and which status code indicates success. Include it even if an SDK is the recommended integration path.

Then show idiomatic SDK or standard-library usage. Avoid line-by-line transliteration from curl; use the language's normal resource-management and error-handling patterns.

The following Node.js 22 example uses the built-in fetch, validates required configuration, applies a timeout, checks the status, and preserves a request ID for support:

const apiBaseUrl = process.env.API_BASE_URL ?? 'https://sandbox.example.com'; const accessToken = process.env.ACCESS_TOKEN; if (!accessToken) { throw new Error('Set ACCESS_TOKEN to a sandbox API token.'); } const idempotencyKey = `docs-${crypto.randomUUID()}`; async function createOrder(key) { const response = await fetch(`${apiBaseUrl}/orders`, { method: 'POST', signal: AbortSignal.timeout(10_000), headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', 'Idempotency-Key': key, }, body: JSON.stringify({ customer_id: 'cus_123', items: [{ sku: 'mug-blue', quantity: 2 }], }), }); const contentType = response.headers.get('content-type') ?? ''; const rawBody = await response.text(); let body; if (contentType.includes('json')) { try { body = JSON.parse(rawBody); } catch { body = undefined; } } if (!response.ok) { const requestId = response.headers.get('x-request-id') ?? 'unknown'; throw new Error( `Order creation failed (${response.status}, request ${requestId}): ` + `${body?.title ?? 'Non-JSON or invalid error response'}`, ); } if (!body) { throw new Error( `Expected a JSON response but received ${contentType || 'no content type'}.`, ); } return body; } createOrder(idempotencyKey) .then((order) => console.log(`Created order ${order.id}`)) .catch((error) => { console.error(error.message); process.exitCode = 1; });

This is more useful than fetch(url).then(r => r.json()) because it models behavior developers need outside a demo. It does not include automatic retries because retry safety depends on the operation and provider policy. Generate the idempotency key once per logical operation, as shown, and pass that same value to every retry; generating a new key after an ambiguous timeout could create a duplicate order. The surrounding guide must still explain retention and conflict semantics.

Balance Completeness and Focus

Every sample should be runnable or clearly labeled as partial. A reference snippet can omit application scaffolding when the omission is obvious and explained. A quick start should include imports, configuration, entry point, and dependency installation.

Keep the main path visible. Move reusable logging or test fixtures into linked files rather than burying the API call under 100 lines of setup. When a production concern is intentionally omitted, state it: “This sample uses an in-memory store for clarity; production applications need durable storage and concurrency control.”

Teach Error Handling, Not Only Success

At minimum, show how the language or SDK exposes:

  • status codes and response headers;
  • structured error bodies;
  • network failures and timeouts;
  • authentication failures;
  • rate limits and Retry-After;
  • idempotency or conflict responses.

Do not wrap all errors in a generic catch block that discards details. Conversely, do not print entire production responses when they may contain personal data. Demonstrate the stable fields the API documents, as described in Best Practices for Documenting API Error Responses.

A Python 3.12 example can show a bounded retry policy without hiding behavior behind an SDK:

import math import os import random import time import requests API_BASE_URL = os.getenv("API_BASE_URL", "https://sandbox.example.com") ACCESS_TOKEN = os.environ["ACCESS_TOKEN"] MAX_RETRY_DELAY_SECONDS = 30 def get_order(order_id: str, max_attempts: int = 3) -> dict: """Fetch an order and retry only documented transient responses.""" for attempt in range(1, max_attempts + 1): response = requests.get( f"{API_BASE_URL}/orders/{order_id}", headers={"Authorization": f"Bearer {ACCESS_TOKEN}"}, timeout=(3.05, 10), ) if response.status_code == 200: if "application/json" not in response.headers.get("Content-Type", ""): raise RuntimeError("Expected an application/json response") return response.json() if response.status_code not in {429, 503} or attempt == max_attempts: response.raise_for_status() raise RuntimeError(f"Unexpected HTTP status {response.status_code}") retry_after = response.headers.get("Retry-After") try: requested_delay = float(retry_after) if retry_after else 2 ** (attempt - 1) if not math.isfinite(requested_delay) or requested_delay < 0: raise ValueError("Retry-After must be a finite non-negative number") except ValueError: requested_delay = 2 ** (attempt - 1) delay = min(requested_delay, MAX_RETRY_DELAY_SECONDS) time.sleep(delay + random.uniform(0, 0.25)) raise RuntimeError("unreachable") print(get_order("ord_123"))

The prerequisites should pin a compatible requests release in a separate requirements file. The text should clarify that a provider may send Retry-After as either delay seconds or an HTTP date; this short sample supports delay seconds because that is the documented contract of the example API.

Generate Samples Without Losing Quality

An OpenAPI document can generate request snippets and SDK calls at scale. Generation is especially useful for method, URL, parameters, and basic payload consistency. It is not a substitute for editorial review.

Generated samples commonly fail by:

  • choosing placeholder values that violate schemas;
  • embedding credentials or a production server;
  • omitting required setup and imports;
  • using deprecated libraries or methods;
  • ignoring non-success responses;
  • producing non-idiomatic code;
  • generating every possible optional field, obscuring the common case.

Use generation for the mechanical core, then apply templates and tests maintained by language owners. Keep generated files read-only and regenerate them from the contract; manual fixes to generated output disappear on the next build.

Test Samples as Product Code

Testing should match the sample's importance. A useful ladder is:

flowchart LR
    S["Source sample"] --> F["Format and parse"]
    F --> T["Type-check or compile"]
    T --> M["Run with mock contract"]
    M --> X["Run against sandbox"]
    X --> A["Assert documented outcome"]
    A --> P["Publish with verification date"]

    classDef check fill:#fff7e6,stroke:#d48806,color:#5c3b00;
    classDef ready fill:#f6ffed,stroke:#389e0d,color:#173b0b;
    class F,T,M,X,A check;
    class P ready;

Extract code from documentation into temporary files or, preferably, include source files into documentation so the tested artifact and displayed artifact are identical. Parse JSON and YAML examples, compile statically typed examples, lint scripts, and execute task-focused samples against a deterministic sandbox.

Assertions should prove the teaching goal. A process exit code of zero is not enough if the API returned an unexpected empty list. Check the expected status, schema, and important values. Isolate or clean up created resources so scheduled tests do not pollute the sandbox.

Use short-lived scoped credentials from the CI secret store. Redact logs and prevent pull requests from untrusted forks from accessing secrets. When live execution is too expensive or destructive, run contract-backed mocks on every change and schedule controlled end-to-end verification.

Detect Drift

Tie each sample to an API version and, when applicable, an SDK version. Trigger relevant tests when the OpenAPI contract, SDK, gateway route, or authentication flow changes. Search the repository for old hosts, deprecated paths, and package names before a release.

Expose freshness to maintainers through metadata or a report, not a false promise to readers. A “tested yesterday” badge is meaningful only if the test exercises the real scenario and the result is monitored.

Use Gateway Examples Responsibly

When documentation covers an API gateway, samples should distinguish the consumer-facing request from administrative configuration. A client calling a route should not need to know the upstream hostname. An operator configuring Apache APISIX needs a separate example with configuration prerequisites, scoped administrative credentials, and an explanation of the change's traffic impact.

Never publish a real Admin API key. Use environment variables and a non-production endpoint. For changes that affect routing, authentication, or rate limiting, include a verification request and rollback step. This keeps operational samples aligned with the same safety standard as application code.

Code Sample Review Checklist

  • Does the sample solve a real audience task?
  • Is it raw HTTP, idiomatic code, or a complete application—and is that scope clear?
  • Are prerequisites, versions, permissions, placeholders, and side effects documented?
  • Can a reader copy the code without editing hidden values?
  • Are secrets loaded securely and synthetic data used?
  • Are timeouts, errors, retries, and idempotency handled according to the contract?
  • Does expected output match the sample and current schema?
  • Is every omission explicitly marked?
  • Does automation parse, compile, or execute the displayed artifact?
  • Is there an owner and a trigger for updating or retiring it?

Conclusion

Great API code samples compress a trustworthy integration path into a form developers can learn from immediately. Choose scenarios deliberately, expose the underlying HTTP contract, write idiomatic code, model secure failure handling, and continuously test the exact artifact readers see.

The next chapter widens the audience. Learn how to explain capabilities and consequences without requiring every reader to implement a request in API Documentation for Non-Developers.