API documentation examples: Stripe, GitHub, Twilio and 6 samples

API documentation examples: how Stripe, GitHub and Twilio structure their docs, plus six annotated samples, from an endpoint reference to an OpenAPI file.

By Strokes Team · · 15 min read

Key takeaways

  • Good API docs pair a reference entry for every endpoint (method, path, parameters, a sample request, a sample response and errors) with hand-written guides such as a quickstart and authentication.
  • Stripe, GitHub and Twilio all put a copyable sample request beside the reference text, and all three name the API version a request uses.
  • Document the unhappy path: every error a reader can trigger, what it means and what to do next.
  • An OpenAPI description can hold the reference text next to the contract, but the guides, the quickstart and the reasons still have to be written by a person.
A laptop on a sunlit desk open to a documentation page with a navigation sidebar and colored code samples, beside a stack of three books, a purple one with a white emblem, a black one with the GitHub logo and a red one with the Twilio logo, an open notebook with a sketched flowchart and a pen, a mug of coffee and potted plants.
The reference says what every socket does. The guides tell you which cord to plug in first.
Table of contents
  1. What API documentation includes
  2. Stripe's API reference
  3. GitHub's REST API docs
  4. Twilio's Messages resource
  5. What to borrow from Stripe, GitHub and Twilio
  6. Endpoint reference sample
  7. Authentication section sample
  8. Error reference sample
  9. Pagination and rate limits sample
  10. Webhook event reference sample
  11. OpenAPI description sample
  12. Habits of good API docs
  13. Frequently asked questions

API documentation tells developers what an API does and how to call it. It has two halves: a reference, with an entry for every endpoint (method, path, parameters, a sample request, a sample response and the errors it can return), and guides, such as a quickstart, authentication and a changelog. Below, three well-known API docs, Stripe's, GitHub's and Twilio's, are described section by section, followed by six original samples of the parts most teams write by hand, each with notes on why it works.

ExampleWhat it shows
ExampleStripe's API referenceWhat it showsOne pattern for every resource, and errors that link back to the docs
ExampleGitHub's REST API docsWhat it showsGuides kept apart from the reference, and permissions per endpoint
ExampleTwilio's Messages resourceWhat it showsProperties labeled for personal data, and samples named after tasks
ExampleEndpoint reference sampleWhat it showsDescription, parameters, request, response, errors
ExampleAuthentication sampleWhat it showsKeys, scopes, test and live, what to do after a leak
ExampleError reference sampleWhat it showsA table of codes and an error body that helps
ExamplePagination and rate limits sampleWhat it showsDefaults, maximums and the last page
ExampleWebhook event sampleWhat it showsWhen an event fires, signatures, retries
ExampleOpenAPI description sampleWhat it showsThe reference written as a spec file
Three real API docs, then six samples written for one invented API.

What API documentation includes

Every set of API docs answers the same questions in roughly the same order. A missing page tends to show up as support tickets.

PartThe developer's questionUsually written by
PartOverviewThe developer's questionWhat does this API do, and is it the right one?Usually written byA person
PartQuickstartThe developer's questionHow do I make one successful call?Usually written byA person
PartAuthenticationThe developer's questionHow do I prove who I am, and with what permissions?Usually written byA person
PartEndpoint referenceThe developer's questionWhat exactly does this call take and return?Usually written byA person, often inside the spec file
PartErrorsThe developer's questionWhat went wrong, and what do I do now?Usually written byA person, with codes from the code
PartConventionsThe developer's questionHow do pagination, rate limits and versions work?Usually written byA person
PartCode samples and SDKsThe developer's questionWhat does a call look like in my language?Usually written byEngineers, tested like code
PartChangelogThe developer's questionWhat changed, and will it break me?Usually written byA person, at every release

Stripe's API reference

Stripe's reference opens with an introduction before any resource. It says the API is organized around REST, with “predictable resource-oriented URLs”, form-encoded request bodies, JSON responses and standard HTTP response codes. The same page gives the base URL, https://api.stripe.com, and links the official client libraries for Ruby, Python, PHP, Java, Node.js, Go and .NET. It explains that the API key you send decides whether a request runs in live mode or in a sandbox, and readers who log in see the docs with their own test key and data.

Every resource follows one pattern. The Customers section has a sentence on what the object represents, its endpoints named by task (create, update, retrieve, list all, delete and search customers) and the events it emits, such as customer.created. Each endpoint page has a one-line description, a curl request that pins the API version in a Stripe-Version header, the full JSON response, every parameter with its type and whether it is optional, and a Returns paragraph that says what comes back and when the call raises an error.

Errors get their own page: codes in the 2xx range mean success, 4xx a problem with the request, 5xx a problem on Stripe's side, followed by a table of status codes and four error types. The error object carries a doc_url pointing to more information about the error code, and a param field that names the parameter at fault, so an app can show the message next to the right form field.

What to borrow. Task names for endpoints, a complete response instead of an excerpt, and error messages that lead back to the docs.

GitHub's REST API docs

GitHub keeps articles and reference apart. The landing page offers a quickstart, getting started, authentication, an overview of the REST API, API versions, breaking changes, best practices, and an article on the OpenAPI description that GitHub says fully describes the API. The reference is grouped by product area (Actions, Activity, Apps, Billing, Branches and so on), with endpoint groups inside each.

Each endpoint has the same parts. Create an issue shows its method and path, POST /repos/{owner}/{repo}/issues, and a description that starts with who may call it: “Any user with pull access to a repository can create an issue.” It also warns that the endpoint returns 410 Gone when a repository has issues turned off, and that creating content too quickly can trigger secondary rate limits. Four sections follow, each heading repeating the endpoint's name: the fine-grained access tokens that can call it, its parameters (grouped as headers, path parameters and body parameters), its HTTP response status codes, and code samples in cURL, JavaScript and GitHub CLI. The response appears twice, as an example and as a schema, and the sample request sends a dated X-GitHub-Api-Version header.

What to borrow. Permissions before parameters, and a status code list written for this endpoint, 410 Gone included, rather than a generic one.

Twilio's Messages resource

Twilio's reference page for messages opens with one sentence: “A Message resource represents an inbound or outbound message.” Then come the resource's properties, a table of status values, a section on how a long SMS is counted in segments, and one section per action: create, fetch, read multiple, update and delete. Each property is listed with its type, whether it is required, a PII classification and a description.

The create request goes to POST https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json, so the API version is a date inside the path. Code samples come in Node.js, Python, C#, Java, Go, PHP, Ruby, the twilio-cli and curl, and each is titled with the task it does, such as sending an SMS message, with variants for SMS, RCS, WhatsApp and Messaging Services.

What to borrow. A personal-data label on every property answers a security reviewer's first question. Status values belong in a table, because a message moves through them. And samples named after a job are easier to find than samples named after an endpoint.

What to borrow from Stripe, GitHub and Twilio

Each of the three is at its best for a different task. Give each one the job it does best, and copy the feature that makes it work.

The reader's taskStudyThe feature to copy
The reader's taskReproduce a requestStudyStripeThe feature to copyA curl sample on every endpoint page that pins its API version, set beside the complete JSON response; logged-in readers see their own test key and data
The reader's taskFind out what access a call needsStudyGitHubThe feature to copyA section on each endpoint listing the fine-grained token permissions it needs, placed before the parameters, after a description that says who may call it
The reader's taskFind the code sample for a jobStudyTwilioThe feature to copySamples titled by task, such as sending an SMS message, offered in nine languages and tools
From each company's public API reference, read in November 2026.

All three also name the API version a request uses, each in its own place: Stripe in a Stripe-Version header, GitHub in a dated X-GitHub-Api-Version header and Twilio in a date inside the URL path.

Endpoint reference sample

The six samples below document one invented API, Pressline, which publishes articles to a news site and a newsletter. For the first page a developer reads, the quick start guide template has an API quickstart example, and the technical writing examples include a second reference entry, for an invoicing API. This sample is one reference entry, with the scope it needs and a response that shows how values come back.

Pressline API reference: Create a draft

POST /v2/drafts

Creates a draft article in a publication. The draft stays private until you publish it with Publish a draft, or until publish_at if you set it. Requires a key with the drafts:write scope, plus publish if you set publish_at.

Body parameters

  • publication_id (string, required). The publication the draft belongs to.
  • title (string, required). Shown as the headline. Up to 200 characters.
  • body (string, optional). The article text in Markdown. Defaults to empty.
  • author_ids (array of strings, optional). Up to five authors. Defaults to the owner of the key.
  • publish_at (string, optional). An ISO 8601 date and time with a time zone offset. When set, the draft is scheduled.

Returns the Draft object with status draft or scheduled. Errors: 400 missing_title, 403 insufficient_scope, 404 publication_not_found, 422 publish_at_in_past.

Original example, written for this guide. Pressline is an invented product.

Sample request

curl https://api.pressline.example/v2/drafts \
  -H "Authorization: Bearer $PRESSLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "publication_id": "pub_8Kd2",
    "title": "Council approves the harbor budget",
    "publish_at": "2026-11-12T06:00:00-05:00"
  }'

Sample response (201 Created)

{
  "id": "drf_3Qp9",
  "object": "draft",
  "publication_id": "pub_8Kd2",
  "title": "Council approves the harbor budget",
  "status": "scheduled",
  "publish_at": "2026-11-12T11:00:00Z",
  "author_ids": ["aut_51Lx"],
  "created_at": "2026-11-10T14:22:07Z"
}

Why it works. The description says what happens next (the draft stays private) and which scope the key needs. Every parameter has a type, whether it is required, its limit and its default. The request runs as pasted, with the key in an environment variable instead of the page. And the response shows publish_at converted to UTC, which answers the time zone question before anyone files it as a bug.

Authentication section sample

Pressline docs: Authentication

Pressline uses API keys. Send your key in the Authorization header of every request: Authorization: Bearer YOUR_API_KEY.

Keys that start with pl_test_ work only in your test workspace, which has sample publications and never sends email. Keys that start with pl_live_ act on real publications. Create and revoke keys in Settings › API keys.

Each key has scopes: drafts:read, drafts:write, publish and subscribers:read. Give a key only the scopes its integration needs.

Keep keys out of browser code and out of version control. If a key leaks, revoke it at once. A request with no key or a revoked key returns 401 invalid_api_key; a key without the right scope returns 403 insufficient_scope, and the message names the scope it needs.

Original example, written for this guide.

Why it works. One header, shown exactly. The key's prefix tells a developer which workspace a request will touch, and the test workspace promises it can't email real subscribers. Scopes are listed with the advice to use as few as possible, and the section says what to do after a leak, which is the moment people actually read it. Separating 401 from 403 tells the developer whether to fix the key or its permissions.

Error reference sample

StatusCodeWhat it meansWhat to do
Status400Codeinvalid_jsonWhat it meansThe body isn't valid JSONWhat to doCheck quotes and commas; the message gives the line and column
Status400Codemissing_titleWhat it meansThe draft has no titleWhat to doSend a title of 1 to 200 characters
Status401Codeinvalid_api_keyWhat it meansThe key is missing, mistyped or revokedWhat to doSend a current key in the Authorization header
Status403Codeinsufficient_scopeWhat it meansThe key lacks a scope this call needsWhat to doUse a key with the scope named in the message
Status404Codepublication_not_foundWhat it meansNo publication with that ID in this workspaceWhat to doCheck the ID, and whether a test key is calling live data
Status409Codealready_publishedWhat it meansAnother request published the draft firstWhat to doFetch the draft; don't retry the publish
Status422Codepublish_at_in_pastWhat it meansThe scheduled time has passedWhat to doSend a future time with a time zone offset
Status429Coderate_limitedWhat it meansToo many requestsWhat to doWait the number of seconds in the Retry-After header
Status500Codeserver_errorWhat it meansSomething failed on Pressline's sideWhat to doRetry with backoff; give support the request_id
Pressline's error codes. Programs branch on the code; people read the message.

Error response body

{
  "error": {
    "status": 422,
    "code": "publish_at_in_past",
    "message": "publish_at must be in the future. You sent 2026-11-09T06:00:00-05:00.",
    "param": "publish_at",
    "request_id": "req_7Hc1",
    "doc_url": "https://docs.pressline.example/errors#publish_at_in_past"
  }
}

Why it works. Codes are stable strings a program can branch on, while the message is for people and repeats the value that was wrong. The param field names the input at fault, the request_id lets support find the call in seconds, and the doc_url leads back to this table, the same idea as the doc_url in Stripe's error object. The What to do column is what turns a list of codes into help.

Pagination and rate limits sample

Pressline docs: Pagination and rate limits

Pagination. List endpoints return 25 items by default and 100 at most; set the number with limit. Items are sorted newest first by created_at. Each response includes next_cursor: send it back as cursor to get the next page. When next_cursor is null, you have the last page. Cursors expire after 24 hours.

Rate limits. Each key can make 50 requests a second, and each workspace 200. Over the limit you get 429 rate_limited with a Retry-After header in seconds. Wait that long before you retry; retrying at once only extends the wait.

Original example, written for this guide.

A list request and its first page

GET /v2/drafts?publication_id=pub_8Kd2&limit=2

{
  "data": [
    { "id": "drf_3Qp9", "title": "Council approves the harbor budget" },
    { "id": "drf_3Qn4", "title": "Ferry timetable changes in December" }
  ],
  "next_cursor": "c_9Fk2"
}

Why it works. It states the default, the maximum and the sort order, then says exactly how to recognize the last page, which is the question every loop needs answered. The limits are given per key and per workspace, so a team running several integrations can plan for both. This is the reference version; a concept page that explains how a limit behaves under bursts is a separate document, and the technical writing examples include one.

Webhook event reference sample

Pressline docs: the draft.published event

Sent when a draft goes live, either because you called Publish a draft or because its publish_at time arrived. Not sent when you schedule a draft: scheduling sends draft.scheduled.

Pressline signs every delivery. The Pressline-Signature header holds an HMAC-SHA256 of the raw request body, made with your endpoint's signing secret. Check it before you trust the payload.

Respond with any 2xx status within 10 seconds. Failed deliveries are retried up to 8 times over 24 hours, with longer gaps each time. The same event can arrive more than once, so use the event id to skip duplicates.

Original example, written for this guide.

Payload

{
  "id": "evt_5Rt8",
  "type": "draft.published",
  "created_at": "2026-11-12T11:00:02Z",
  "data": {
    "object": {
      "id": "art_7Lm2",
      "object": "article",
      "draft_id": "drf_3Qp9",
      "url": "https://harborherald.example/council-approves-harbor-budget"
    }
  }
}

Why it works. It says when the event fires and when it doesn't, which is where webhook bugs come from. Security, timeouts, retries and duplicates each get one sentence, and the payload shows the object the developer will actually receive.

OpenAPI description sample

The OpenAPI Specification “defines a standard, programming language-agnostic interface description for HTTP APIs”, written in JSON or YAML. It grew out of the Swagger 2.0 specification, which SmartBear Software donated in 2015, and the OpenAPI Initiative, a Linux Foundation project, now maintains it. Documentation tools such as Swagger UI render the file as interactive reference pages. Here is the Create a draft entry again, as it would sit in the file. The excerpt uses OpenAPI 3.1; the latest published version, as of November 2026, is 3.2.1.

openapi.yaml (excerpt)

openapi: 3.1.0
info:
  title: Pressline API
  version: "2026-11-01"
servers:
  - url: https://api.pressline.example/v2
paths:
  /drafts:
    post:
      operationId: createDraft
      summary: Create a draft
      description: >
        Creates a draft article in a publication. The draft stays
        private until you publish it, or until publish_at if you set it.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DraftCreate"
      responses:
        "201":
          description: The draft was created.
        "422":
          description: publish_at is in the past.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
  schemas:
    DraftCreate:
      type: object
      required: [publication_id, title]
      properties:
        publication_id:
          type: string
          examples: [pub_8Kd2]
        title:
          type: string
          maxLength: 200
          description: Shown as the headline.
        publish_at:
          type: string
          format: date-time
          description: When set, the draft is scheduled for this time.

Why it works. The reference text lives next to the contract, so a renamed field changes in one place. The summary becomes the page title, the description is written by a person, and limits such as maxLength appear wherever the file is shown. What the file can't hold is the rest of the docs: the quickstart, the authentication guide and the reasons behind each design choice. Teams that keep the file and those guides in one repository, reviewed like code, are practicing docs as code.

If you write the guides in Strokes, attach the OpenAPI file to a chat with the Strokes Assistant and ask which parameters, defaults or limits the hand-written pages get wrong.

Habits of good API docs

Says what the reader needs

  • GET /v2/drafts: Lists a publication's drafts, newest first. Scheduled drafts are included; published ones are not.
  • title (string, required): Shown as the headline. Up to 200 characters.
  • Returns: the Draft object, with status draft or scheduled.
  • Errors: 403 insufficient_scope if the key lacks drafts:read.

Restates the name

  • GET /v2/drafts: Gets drafts.
  • title (string): The title.
  • Returns: the response.
  • Errors: see error codes.
  • Samples that run as pasted. Test keys, realistic IDs, environment variables for secrets, and a full response to compare against. Run every sample before each release.
  • One name for each thing. The parameter in the table, the sample and the SDK must match. A short style guide settles naming, capitalization and how code appears in text; the style guide examples include Google's developer documentation guide.
  • Versions you can see. Say which version each page describes and keep a dated changelog; the changelog examples include an API changelog with deprecations.
  • The unhappy path. Errors, limits, timeouts and retries are where integrations fail, and where docs often stop short.

Frequently asked questions

What is the least documentation a small API can ship with?

An overview, a quickstart that ends in one successful call, an authentication page, a reference entry for every endpoint with a sample request and response, a list of errors with what to do about each, and a dated changelog. Webhooks, SDK guides, tutorials and a page on versioning can wait until the API has them or readers ask.

Is OpenAPI the same as Swagger?

Not exactly. OpenAPI is the specification, a standard way to describe an HTTP API in JSON or YAML. It was based on the Swagger 2.0 specification, which SmartBear Software donated in 2015. Swagger is now the name of a set of tools built around the OpenAPI Specification, such as Swagger UI and Swagger Editor.

Who writes API documentation?

Usually engineers and technical writers together. Engineers know the behavior and often write the reference descriptions inside the spec file; technical writers shape the guides, the quickstart and the wording across the set. Whoever writes it, someone who didn't build the API should try every sample before it ships.

Read next