# pdoc: API documentation for Python projects, generated from your source

> pdoc renders Markdown docstrings, type annotations and inherited members into standalone HTML or a live-reloading local server. It is a small tool with a narrow scope, and it says so.

**mitmproxy/pdoc** — API Documentation for Python Projects

- Repository: https://github.com/mitmproxy/pdoc
- Website: https://pdoc.dev
- Stars: 2,513 · Forks: 231
- Language: Python
- License: MIT-0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/mitmproxy-pdoc

## What pdoc solves, and for whom

Most Python packages already carry their documentation inside the code: docstrings on functions and classes, type annotations on signatures. The gap is turning that into something a reader can browse. pdoc fills exactly that gap. It takes a module, a package or a single file, imports or parses it, and emits an HTML site where each identifier links to its own definition.

The intended audience is the maintainer of a Python library who wants a reference page without maintaining a separate source of truth. The README frames the design as a deliberate narrowing: "pdoc's main feature is a focus on simplicity: pdoc aims to do one thing and do it well." That sentence is also the honest statement of the tool's boundary. If your documentation needs are about prose guides, versioned multi-language output or a plugin ecosystem, this is not the tool, and the README says so directly by recommending Sphinx for "substantially more complex documentation needs."

One naming trap is worth clearing up before anything else. This project is not pdoc3. The README devotes a section to the distinction and quotes the original author of pdoc describing pdoc3 as a fork that relicensed his work and "associated it with Nazi symbols." If you find installation instructions elsewhere that mention pdoc3, they are not about this repository.

## How pdoc reads your code and builds the site

The mechanism has two halves: extraction and rendering.

On extraction, pdoc does more than read the module object. According to the README, it "will traverse the abstract syntax tree to extract type annotations and docstrings from constructors as well." That matters because a constructor's annotations are often invisible to a naive runtime introspection pass, and pdoc gets them by parsing the source. It also resolves type annotation string literals written as forward references, and it uses inheritance to resolve annotations and docstrings for class members, so a subclass member that inherits its documentation does not render blank. When a module defines __all__, pdoc respects it and treats that as the public surface.

On rendering, pdoc links identifiers mentioned in docstrings to their corresponding documentation automatically. Docstrings are parsed as Markdown, and the README states that both numpydoc and Google-style docstrings are understood, so you do not have to convert existing ones. Templates are Jinja2, and the output is standalone HTML with no additional dependencies, which means you can drop the generated directory on any static host.

The dependency list in pyproject.toml is short and worth reading as a design statement: Jinja2, pygments, MarkupSafe and markdown2. There is no database, no search index service and no Node toolchain. A documentation build that only needs four pure-Python packages is a different operational proposition from one that needs a full site generator.

## Installing pdoc and generating your first docs

Installation is one pip command. The README states pdoc is compatible with Python 3.10 and newer, and pyproject.toml sets requires-python to ">=3.10", so check your interpreter before you start.

```bash
pip install pdoc
```

Once installed, the pdoc entry point is available. Running it against a module prints the rendered documentation to standard output, which is the fastest way to see whether your docstrings are being picked up at all.

```bash
pdoc your_python_module
```

To write a browsable site instead, pass an output directory with -o. The README's own example is pdoc documenting itself, which produces the site hosted at pdoc.dev/docs.

```bash
pdoc -o ./html pdoc
```

After that command, ./html contains the generated pages; open ./html/index.html in a browser. For a single-file script rather than an installed package, the README gives a path form:

```bash
pdoc ./my_project.py
```

If you want to iterate on docstrings without rerunning the command, run pdoc without an output directory. The README lists a "builtin web server with live reloading" among the features, so edits to the source are reflected in the browser. The full set of command line flags is not enumerated in the README; it points to pdoc --help and to the hosted documentation for that.

## Where pdoc stops being the right tool

The limitations are mostly consequences of the design, and the project is unusually candid about them.

The first is scope. pdoc documents Python. There is no mechanism described for pulling in a C extension's headers beyond what Python can introspect, no cross-language output, and no narrative document pipeline. If your documentation site is half tutorial and half reference, you will be maintaining the tutorial half somewhere else and reconciling the two by hand.

The second is extensibility. The README lists customizable HTML templates, and the repository ships examples/custom-template/ and examples/dark-mode/ to show what that looks like. That is theming, not an extension API. Sphinx, by contrast, is built around extensions, and the README's own recommendation to use Sphinx for complex needs is a fair summary of where the ceiling sits.

The third is the runtime dependency during generation. pdoc needs to understand your package, and the AST traversal described in the README exists precisely because importing alone is not enough. That said, generating docs for a package with heavy or platform-specific import-time side effects is the scenario where a purely import-based documentation tool becomes awkward. The README does not document a sandboxed or import-free mode, so if your package cannot be imported in the build environment, verify that pdoc still produces complete output before you commit to it.

Finally, pdoc is a reference generator. It does not version your documentation, host it, or manage redirects when a symbol moves. Those are separate concerns you would solve with your own static hosting.

## pdoc compared with Sphinx

The honest comparison is with Sphinx, and pdoc's own README makes it rather than dodging it.

The difference in approach is where the source of truth lives. Sphinx is a document-first system: you write reStructuredText or Markdown files, and autodoc directives pull docstrings in as one ingredient among many. Cross-references, toctrees, extensions and multiple output formats all hang off that document tree. pdoc is a code-first system: the module is the input, and the documentation is a rendering of what is already in it. There is no document tree to maintain, and consequently no place to put a hand-written introduction unless you put it in the package docstring.

That single choice explains most of the downstream differences. pdoc's install is one package with four dependencies; Sphinx brings a theme, a builder framework and a configuration file. pdoc's output is standalone HTML you can copy anywhere; Sphinx output is typically a Read the Docs or hosted build. pdoc gives you a live-reloading server out of the box; Sphinx gives you a much larger set of things you can configure.

Neither is better in the abstract. If your team already writes long-form guides, Sphinx's model will fit. If your library's documentation is its docstrings and you want a build that never breaks because a toctree entry went stale, pdoc's model removes a whole class of maintenance work.

## Licence, maintenance and upgrade cost

pdoc is released under MIT-0, which pyproject.toml records as the license text and which the Python classifiers describe as "License :: Public Domain." MIT-0 is the MIT licence with the attribution requirement removed, so redistributing generated documentation or vendoring the tool does not carry the notice obligations that a standard MIT or BSD licence would. That is a permissive position, and it is worth confirming with whoever handles licensing in your organisation rather than taking a summary as legal advice.

The repository is not archived, and the last push was on 2026-07-01. The project describes itself in pyproject.toml as "Development Status :: 5 - Production/Stable" and supports Python 3.10 through 3.14 in its classifiers, which means the version support is kept current rather than pinned to an old interpreter.

Upgrade cost is low by construction. There is no plugin surface to break, no configuration schema beyond command line flags and templates, and the runtime dependency set is four libraries with minimum versions rather than exact pins. The realistic upgrade risks are two: a change in how the Jinja2 templates are structured, which would affect you only if you have a custom template, and a change in docstring parsing behaviour, which would show up as formatting differences in rendered output. The repository ships a CHANGELOG.md at the top level, so that file is the place to look before bumping the version in a project where the docs are published automatically.

## Conclusion

Adopt pdoc if your project is a Python library or package whose public surface is already described by docstrings and type annotations, and you want HTML output you can ship without a build pipeline. Do not adopt it if you need cross-language docs, a large extension ecosystem, or hand-authored narrative pages stitched into the same tree; the README points at Sphinx for that. Before committing, run pdoc against your package and check two things: whether the members you consider public actually appear (pdoc respects __all__ when it is present), and whether the constructor annotations you rely on survive into the output, since those are read from the abstract syntax tree rather than at import time.

## FAQ

### What is pdoc?

pdoc is a documentation generator for Python projects, described in its README as "API Documentation for Python Projects." It reads docstrings and type annotations from your source and renders them as Markdown-based HTML, either printed to standard output or written to a directory.

### What Python versions does pdoc support?

The README states that pdoc is compatible with Python 3.10 and newer, and pyproject.toml sets requires-python to ">=3.10" with classifiers listing 3.10 through 3.14.

### Is pdoc the same project as pdoc3?

No. The README has a dedicated section stating that this project is not associated with pdoc3, and quotes the original author of pdoc criticising that fork for relicensing his work and associating it with Nazi symbols.

### Does pdoc understand Google-style and numpydoc docstrings?

Yes. The README lists "Understands numpydoc and Google-style docstrings" among the features, and states that documentation is plain Markdown.

### Does pdoc need a separate configuration file?

The README describes usage entirely through the pdoc command and its flags, and points to pdoc --help for the full flag list. No configuration file is documented in the README or in pyproject.toml.

## Sources

- [Issues](https://github.com/mitmproxy/pdoc/issues)
- [License: MIT-0](https://github.com/mitmproxy/pdoc/blob/main/LICENSE)
- [mitmproxy/pdoc on GitHub](https://github.com/mitmproxy/pdoc)
- [Project website](https://pdoc.dev)
- [README](https://github.com/mitmproxy/pdoc/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mitmproxy-pdoc
