Prompting for API Documentation
What Good API Documentation Contains API documentation tells developers how to use an API: which endpoints exist, what requests they accept, what responses they return, and what can go wrong. Good documentation saves integration time and support effort; inaccurate documentation is worse than none, because developers trust it.
A complete endpoint description usually includes:
Section Content Endpoint Method and path, such as POST /api/enrollments/ Purpose One or two sentences on what it does Authentication Whether a token or session is required, and which permission Parameters Path, query, and body fields, with types, whether required, and constraints Example request A working request, for example with curl Example response A realistic response body with status code Errors Each error status code, when it happens, and the error body Notes Rate limits, pagination, side effects, and versioning Language models are good at turning code into this structure quickly. The main challenge is accuracy: documentation must describe what the API actually does, not what it plausibly does. This lesson shows prompts that generate documentation from the source of truth, and code that checks documentation against the API.
The Golden Rule: Generate From the Source of Truth Documentation should be generated from what defines the API's behavior:
Source What It Defines URL configuration Paths and methods Views Authentication, permissions, logic, status codes Serializers or forms Fields, types, required fields, validation Models Underlying data and constraints Settings Default authentication and permission classes, pagination Tests Real examples of requests and responses If you describe the API to the model in your own words instead, the documentation will reflect your memory of the API, which may be out of date.
Recipe 1: Endpoint Documentation From Code Use it when: documenting an endpoint in a framework such as Django REST Framework.
Prompt Write API documentation in Markdown for the endpoint implemented in .
Use only what the code shows. For anything the code does not determine, write "Not specified in code" instead of guessing.
Sections:
{METHOD} {path}
Purpose Authentication and permissions Request fields: a table with name, type, required, constraints, description Example request (curl) Example success response (status code and JSON body) Errors: a table with status code, when it happens, example body Notes: side effects, pagination, or other behavior visible in the code
{url_pattern} {view} {serializer} {model} {relevant_settings} Why It Works The URL, view, serializer, model, and relevant settings together contain the information needed for every section. "Not specified in code" prevents invented details such as rate limits or error formats the code does not implement. Tables for fields and errors make documentation easy to scan and compare with the code. Example Output (Abbreviated)
POST /api/enrollments/
Purpose: Enrolls the authenticated student in a course.
Authentication and permissions: Requires an authenticated user (IsAuthenticated).
Request fields:
Name Type Required Constraints Description course_id integer Yes Must be an existing course The course to join
Errors:
Status When Example body 400 course_id missing or invalid {"course_id": ["This field is required."]} 401 / 403 User not authenticated {"detail": "Authentication credentials were not provided."} 409 Student already enrolled {"error": "already_enrolled"}
A Detail Worth Verifying In Django REST Framework, a request without credentials receives the message "Authentication credentials were not provided.", but the status code depends on the authentication classes configured: token-based authentication typically produces 401, while session authentication produces 403. This is exactly the kind of detail a model can get wrong when it sees only the view, which is why the prompt includes relevant settings, and why you should confirm status codes with a test request. Error bodies for custom cases, such as 409 here, must come from the view code.
Recipe 2: Realistic Examples Use it when: documentation needs request and response examples that developers can copy.
Prompt Write example requests and responses for the endpoint in .
- Example request: a curl command using the base URL {base_url} and a placeholder token "YOUR_TOKEN". Use realistic but fictional data.
- Example responses: one success case, and one for each error case in the code.
- Every JSON example must match the serializer fields exactly: same names, types, and nesting. Do not add fields that the serializer does not return. Why It Works Placeholder tokens keep real credentials out of documentation. Fictional data avoids publishing real users' information. Exact field matching prevents the most common documentation error: examples with fields that do not exist or are named differently. Better: Use Real Responses From Tests The most reliable examples are real responses recorded from your test suite. Provide them to the model and ask it to format and annotate them, rather than invent them. Then the examples are guaranteed to match actual behavior, including status codes.
Recipe 3: OpenAPI Specification Snippets The OpenAPI Specification is a standard, machine-readable format, written in YAML or JSON, for describing HTTP APIs. Tools use it to generate interactive documentation, client libraries, and tests.
Use it when: your project maintains an OpenAPI file, or you want to add one.
Prompt Write an OpenAPI 3.0 path item, in YAML, for the endpoint in . Include: summary, parameters, requestBody with a schema, and responses for every status code the code returns, each with a schema. Use only fields and status codes that appear in the code. Return only the YAML. Prefer Generating From Code With Tools Many frameworks can generate OpenAPI documents automatically from code. For Django REST Framework, packages such as drf-spectacular do this. Automatic generation stays in sync with the code. Use a model to improve generated specifications, for example by writing clearer descriptions, rather than to write them from scratch.
Recipe 4: Error Documentation Use it when: documenting error responses, which are often incomplete.
Prompt From the view, serializer, and permission classes in , list every error the endpoint can return.
For each error:
- Status code
- Exact condition in the code that causes it, with the line reference
- The response body format
- What the API client should do (fix the request, re-authenticate, retry later, etc.)
{numbered_code} Why It Works Line references tie each documented error to the code, so it can be verified. "What the client should do" turns error documentation into practical guidance for developers integrating with your API. Recipe 5: Changelog for API Changes Use it when: an API changes, and clients need to know what to update.
Prompt Compare the old and new versions of the endpoint in and .
Write a changelog entry for API clients:
- Breaking changes: anything that could make existing clients fail, with required action
- New features: new endpoints, fields, or parameters
- Other changes: behavior or documentation changes that do not require action
If there are breaking changes, say so clearly at the top. Why It Works Breaking changes first, with the required action, is what API clients need most. A field renamed from fee to price silently breaks every client that reads fee, and this structure makes such changes impossible to overlook.
Checking Documentation Against the API Documentation drifts from code over time: fields are added, renamed, or removed. A simple automated check compares the fields in documented JSON examples with the fields the API actually returns.
import json import re
SERIALIZER_FIELDS = {"id", "title", "fee", "seats_left", "start_date"}
doc_markdown = '''
GET /api/courses/{id}/
Example response:
{"id": 2, "title": "Django REST APIs", "fee": 4999, "seats_left": 3, "instructor": "Priya"}
'''
def documented_json_examples(markdown: str) -> list[dict]: """Parse every json block in the documentation."""
blocks = re.findall(r"json\s*\n(.*?)`", markdown, re.DOTALL) return [json.loads(block) for block in blocks]
def compare_fields(example: dict, actual_fields: set[str]) -> dict[str, list[str]]: documented = set(example) return { "documented_but_not_in_api": sorted(documented - actual_fields), "in_api_but_not_documented": sorted(actual_fields - documented), }
for example in documented_json_examples(doc_markdown): print(compare_fields(example, SERIALIZER_FIELDS)) Output:
{'documented_but_not_in_api': ['instructor'], 'in_api_but_not_documented': ['start_date']} Code Explanation documented_json_examples() finds every JSON code block in the Markdown and parses it. Invalid JSON in documentation raises an error, which is itself a useful check: developers will copy these examples. compare_fields() uses set differences in both directions. The result shows that the documentation shows an instructor field the API does not return, and omits start_date, which it does return. Getting the Real Field List In a Django REST Framework project, the real fields can be read from the serializer, for example with set(CourseSerializer().fields). Running this comparison in your test suite catches documentation drift automatically whenever code changes.
Common Mistakes Mistake 1: Documenting From Memory What it is: Describing the API to the model instead of providing the code.
Why it happens: It is faster than collecting the relevant files.
What it causes: Documentation that reflects how the API used to work, or how you think it works.
How to fix it: Generate from URLs, views, serializers, models, settings, and tests.
Mistake 2: Invented Fields and Behavior What it is: Examples with plausible fields, rate limits, or error formats that do not exist.
Why it happens: The model fills gaps with typical API patterns.
What it causes: Developers write clients against documentation that is wrong.
How to fix it: Require "Not specified in code" for unknowns, and compare documented fields with real ones.
Mistake 3: Missing Error Documentation What it is: Documenting only the success case.
Why it happens: Success cases are what the endpoint is for.
What it causes: Clients that handle errors poorly.
How to fix it: Use Recipe 4 to list every error with its cause and client action, and confirm status codes with test requests.
Mistake 4: Real Credentials or Data in Examples What it is: Copying real tokens or user data into examples.
Why it happens: Real responses are convenient.
What it causes: Exposure of secrets and personal data.
How to fix it: Use placeholders and fictional data; mask real responses from tests.
Mistake 5: Documentation Without Maintenance What it is: Generating documentation once and never updating it.
Why it happens: Generation feels like the end of the task.
What it causes: Gradual drift until the documentation is misleading.
How to fix it: Automate checks in tests, generate from code where possible, and update documentation with every API change.
Best Practices Generate from the source of truth. Code, settings, and tests, not descriptions.
Use a consistent structure. Purpose, authentication, fields, examples, errors, notes.
Mark unknowns explicitly. "Not specified in code" is better than a guess.
Prefer real examples. Responses recorded from tests, with sensitive data removed.
Document errors thoroughly. Status codes, causes, bodies, and client actions.
Check documentation automatically. Compare documented fields with the real API in your tests.
Use automatic OpenAPI generation where available. Use models to improve descriptions.
Try It Yourself Exercise 1: Document an Endpoint Choose an endpoint from one of your Django projects. Provide its URL pattern, view, serializer, model, and relevant settings to Recipe 1. Compare every section of the result with the code, send an unauthenticated test request to confirm the real status code, and count the details that were invented or incorrect.
Exercise 2: Detect Documentation Drift Write documentation for an endpoint, then add a field to its serializer and rename another. Run compare_fields() with the new field list and confirm that both changes are detected.
Exercise 3: Breaking Change Changelog Change an endpoint so that fee becomes price and a new optional field currency is added. Use Recipe 5 to write a changelog entry and check that the rename is listed as a breaking change with a clear action for clients.