Getting Started

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against FastAPI 0.142.2 (released 2026-09-30), Pydantic 2.13 and Starlette 1.7, checked on 2026-10-02. FastAPI supports Python 3.10 through 3.14; the code on this page was executed on Python 3.13. It gets you from an empty directory to a running, documented API, and shows every way the official documentation offers to start it. The app grows into the Bookshelf API over the following pages.

Create a Project and a Virtual Environment

Every Python project should have its own virtual environment so its packages do not collide with other projects'. The official documentation now recommends uv for FastAPI projects:

uv init bookshelf --bare
cd bookshelf
uv add "fastapi[standard]"
  • uv init bookshelf --bare creates a directory with only a minimal pyproject.toml (no sample main.py).

  • uv add "fastapi[standard]" creates the virtual environment in .venv (you do not activate it), adds FastAPI to pyproject.toml and records the exact resolved versions of everything in uv.lock, so the same versions can be installed on another machine or in a container.

  • Run commands inside the project environment with uv run, for example uv run fastapi dev.

If you prefer the standard library and pip, create and activate the environment yourself:

python -m venv .venv
source .venv/bin/activate      # Windows PowerShell: .venv\Scripts\Activate.ps1
pip install "fastapi[standard]"

Quote "fastapi[standard]"; without quotes some shells (zsh in particular) try to expand the square brackets. The mechanics of virtual environments and package managers are covered in the Python Reference, in Virtual Environments & Packaging, and are not repeated here.

What fastapi[standard] Installs

Installing plain fastapi gives you only the framework and its hard dependencies (Starlette, Pydantic, a few typing helpers and, since 0.142.0, opentelemetry-api). Since 0.112.0 (2024-08-02), the extras select the optional parts:

Install What you get

fastapi

The framework, Starlette and Pydantic only. No CLI, no server, no form or file support.

fastapi[standard]

The default for applications. Adds the fastapi command-line program (fastapi-cli) with the FastAPI Cloud client, Uvicorn with its standard extras (a faster event loop and HTTP parser, WebSocket support and a file watcher for reload), HTTPX (used by TestClient), Jinja2 (templates), python-multipart (form data and file uploads), email-validator (Pydantic’s EmailStr), pydantic-settings and pydantic-extra-types, and, since 0.142.0, the OpenTelemetry SDK and OTLP exporter.

fastapi[standard-no-fastapi-cloud-cli]

Everything in standard except fastapi-cloud-cli, the client for deploying to FastAPI Cloud. Use it if you do not want that dependency.

fastapi[all]

standard plus additional optional dependencies for Starlette features (for example itsdangerous, used by session middleware, and pyyaml).

The package list is read from the package metadata of FastAPI 0.142.2, which changes between releases, so check the metadata of the version you pin if you depend on a specific transitive package. The fastapi-slim package that older guides mention was deprecated in 0.129.2 and is no longer published; use fastapi or one of the extras.

The First App

Create main.py:

from fastapi import FastAPI

app = FastAPI(title="Bookshelf API", version="0.1.0")


@app.get("/")
async def root() -> dict[str, str]:
    return {"message": "Hello, Bookshelf"}


@app.get("/books/{book_id}")
def read_book(book_id: int, verbose: bool = False) -> dict:
    return {"book_id": book_id, "verbose": verbose}

What the code declares:

  • app = FastAPI(…​) creates the ASGI application. title and version end up in the OpenAPI document and the interactive documentation.

  • @app.get("/") is a path operation decorator: it binds the HTTP method GET and the path / to the function below it. The function is the path operation function. async def and plain def both work; see Async & Concurrency for how to choose.

  • {book_id} in the path is a path parameter and book_id: int its type. verbose: bool = False is not in the path, so FastAPI treats it as an optional query parameter with default False.

  • The return annotation (dict[str, str]) is used to serialize and document the response.

The tests below exercise the app without starting a server, using TestClient:

# test_main.py
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)
print(client.get("/").json())
print(client.get("/books/7?verbose=yes").json())
print(client.get("/books/seven").status_code)
for path in ("/docs", "/redoc", "/openapi.json"):
    print(path, client.get(path).status_code)
spec = client.get("/openapi.json").json()
print(spec["info"], list(spec["paths"]))

Running it prints the two JSON responses, 422 for /books/seven, 200 for the three documentation URLs and the info object ({'title': 'Bookshelf API', 'version': '0.1.0'}) with the paths it found.

Run It: fastapi dev vs fastapi run

The fastapi command comes with fastapi[standard] (0.111.0 introduced it). Two commands start the app; both use Uvicorn internally and detect the application automatically.

fastapi dev        # development: auto-reload, listens on 127.0.0.1
fastapi run        # production: no reload, listens on 0.0.0.0
uv run fastapi dev # the same, through uv
fastapi dev fastapi run

Reload on code changes

on (resource-intensive and less stable than off; development only)

off

Default host

127.0.0.1 (this machine only)

0.0.0.0 (all interfaces)

Intended for

your laptop

containers and servers, behind a TLS-terminating proxy

FASTAPI_ENV

set to development if not already set

left unchanged

Both commands accept the options below (run fastapi dev --help and fastapi run --help to see the full list for your version):

  • --host, --port — the address to listen on (default port 8000).

  • --reload / --no-reload — turn auto-reload on or off, overriding the command’s default.

  • --reload-dir (dev) — limit the directories watched for changes.

  • --workers (run only) — the number of worker processes, mutually exclusive with --reload (see Deployment).

  • --root-path — the ASGI root_path, for apps served under a path prefix by a proxy.

  • --app and --entrypoint / -e — name the application object instead of relying on detection.

  • --proxy-headers / --no-proxy-headers and --forwarded-allow-ips — trust X-Forwarded-* headers from a proxy.

  • --verbose / -v — show detailed output.

The program prints something like this for fastapi dev main.py (abbreviated):

🐍 Using import string: main:app

🌐 Server started at http://127.0.0.1:8000
   Documentation at http://127.0.0.1:8000/docs

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [97762] using WatchFiles
INFO:     Application startup complete.

How the App Is Found, and the pyproject.toml Entrypoint

With no argument, the CLI looks for main.py, app.py, api.py, app/main.py, app/app.py and app/api.py. In the module it uses an object called app or api, or else the first FastAPI instance it finds. It also detects the directory to add to PYTHONPATH from the package structure (directories with __init__.py).

You can pass a file path (fastapi dev main.py) or an entrypoint (fastapi dev --entrypoint main:app), but you then have to repeat it on every command. The recommended way is to declare the entrypoint once in pyproject.toml:

[tool.fastapi]
entrypoint = "main:app"

For a project laid out as backend/main.py with backend/init.py, the value is "backend.main:app". Other tools — the VS Code extension and FastAPI Cloud — also read this setting, so a pyproject entrypoint avoids "it works in my terminal but nowhere else" problems.

The Interactive Documentation

Without any extra code, the running app serves three URLs next to your routes:

URL What it is

/docs

Swagger UI: browse operations, fill in parameters, press Try it out and see the real request and response.

/redoc

ReDoc: a read-only, single-page reference rendering of the same document.

/openapi.json

The OpenAPI 3.1 document (JSON). Everything above is generated from it, and client generators, linters and contract-testing tools consume it too.

What shows up in them follows directly from the code: the title and version passed to FastAPI(…​), one entry per path operation (the function name becomes the summary, so read_book becomes "Read Book"), the parameters with their types and required flags, and the response schema derived from the return annotation. A request that fails validation, like GET /books/seven, returns a 422 with a machine-readable explanation:

{
  "detail": [
    {
      "type": "int_parsing",
      "loc": ["path", "book_id"],
      "msg": "Input should be a valid integer, unable to parse string as an integer",
      "input": "seven"
    }
  ]
}

The URLs, titles and the whole document can be customized or switched off; see OpenAPI & Interactive Docs. Because the documentation is public by default, remember to disable or protect it where the API schema must not be exposed.

Calling the API: curl, HTTPie and HTTPX

With the dev server running (fastapi dev), call it from another terminal. curl is available everywhere:

curl 'http://127.0.0.1:8000/books/3?verbose=true'
curl -i 'http://127.0.0.1:8000/books/seven'      # -i also prints the status line and headers
{"book_id": 3, "verbose": true}

HTTPie has a friendlier syntax: == adds a query-string parameter, and a bare :8000 means localhost:8000.

http :8000/books/3 verbose==true

Source: HTTPie

HTTPX is the Python client; it comes with fastapi[standard] and TestClient is built on the same style of API. This script starts the app with Uvicorn in a thread and calls it over a real socket:

# call_api.py
import threading, time
import httpx, uvicorn
from main import app

server = uvicorn.Server(uvicorn.Config(app, host="127.0.0.1", port=8765, log_level="warning"))
t = threading.Thread(target=server.run, daemon=True); t.start()
while not server.started: time.sleep(0.05)
r = httpx.get("http://127.0.0.1:8765/books/3", params={"verbose": "true"})
print(r.status_code, r.json())
server.should_exit = True; t.join()

Source: HTTPX

It prints 200 {'book_id': 3, 'verbose': True}. In real tests you use TestClient, as shown above, instead of opening a socket; see Testing.

Other Ways to Start the App

  • python -m fastapi — the same CLI as the fastapi command, started as a module. Useful when the scripts directory of the environment is not on PATH: python -m fastapi dev. (Added in 0.112.0 together with the standard extra.)

  • Uvicorn directly — uvicorn main:app --reload --host 127.0.0.1 --port 8000. The argument is an import string (module:variable); --reload and --workers need it so they can re-import the app in a new process. This is what older guides use, and it is still valid — fastapi dev and fastapi run are a convenience layer on top.

  • Another ASGI server — any server that speaks ASGI (Hypercorn, Daphne, Granian) can run main:app; see ASGI vs WSGI, and ASGI Servers.

Debugging

You can attach your editor’s debugger to a FastAPI app. The official approach is to call Uvicorn from your own script, so the debugger launches the process and owns it:

# main.py
import uvicorn
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def root():
    a = "a"
    b = "b" + a  # set a breakpoint here
    return {"hello world": b}


if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)

Run the file with uv run python main.py (or the "Current File" run configuration in the debugger); breakpoints inside path operation functions are hit like in any other Python program.

  • In Visual Studio Code open the Run and Debug panel, add a Python configuration and use "Python: Current File".

  • In PyCharm use the Run menu, Debug, then pick the file.

The if name == "main": guard makes uvicorn.run(…​) execute only when the file is run as a script, not when another module imports app (for example a test, or fastapi dev, which imports the module itself).

Pitfall: uvicorn.run(app, reload=True) does not work with an app object. Uvicorn logs that you must pass the application as an import string to enable reload or workers. Debug without reload, or run fastapi dev and attach a debugger to that process using your IDE’s attach-to-process feature.

Editor Support

The official FastAPI extension for Visual Studio Code and Cursor (published by FastAPI Labs; it also works in vscode.dev and github.dev) understands FastAPI projects:

  • a Path Operation Explorer in the sidebar to jump to any route or router definition, and route search by path, method or name;

  • CodeLens links above test-client calls such as client.get('/items') that jump to the matching path operation;

  • one-click deployment to FastAPI Cloud and streaming of application logs from a cloud deployment.

It finds applications by scanning the workspace for FastAPI() calls; if that fails, set the [tool.fastapi] entrypoint in pyproject.toml (or the fastapi.entryPoint extension setting, in module:variable notation).

Beyond this extension, type-hint-based declarations already give any editor completion and type checking for parameters, models and return values, because FastAPI uses standard Python annotations.

The FastAPI Agent Skill

FastAPI ships an official skill for AI coding agents, bundled inside the package so its guidance always matches the installed FastAPI version and updates when you upgrade FastAPI. After installing FastAPI in your project, install the skill with Library Skills:

uvx library-skills

uvx is an alias for uv tool run: it runs the tool in a temporary, isolated environment while Library Skills scans the packages installed in your project. The skill works with Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI and other coding agents; for Claude Code, choose .claude/skills when prompted. As with any generated code, review what an agent writes against the official documentation. (The skill and its documentation arrived in 0.140.0, 2026-07-24.)

Version Pinning

FastAPI is a 0.x project: any MINOR version can break things, PATCH versions are bug fixes (see the versioning policy in Introduction & Architecture). Pin the version you tested with, using whatever tool you use:

uv add "fastapi[standard]>=0.142.2,<0.143.0"   # compatible range, recorded in uv.lock
pip install "fastapi[standard]==0.142.2"      # exact pin
# requirements.txt
fastapi[standard]>=0.142.2,<0.143.0

Do not pin Starlette (each FastAPI release chooses compatible versions); Pydantic may be pinned to any v2 release that works for you. Upgrade by raising the pin, running your tests and reading the release notes, which mark breaking changes.

The Development Loop

flowchart LR A["Edit main.py"] --> B["fastapi dev
reloads on save"] B --> C["/docs, curl or HTTPie
try the endpoint"] C --> D{"Behaves as expected?"} D -- "no" --> A D -- "yes" --> E["Write a TestClient test"] E --> F["Commit, pin versions,
deploy with fastapi run"]

Pitfalls

  • Module named like a package. A file called fastapi.py or uvicorn.py in your project shadows the installed package and produces confusing import errors. Name files after what they contain.

  • App not found. The CLI looks for specific file names and an object called app or api. Pass the file path, or set [tool.fastapi] entrypoint.

  • Port already in use. Another process (often a stale fastapi dev) holds port 8000; pass --port.

  • Unreachable from a container or another machine. fastapi dev listens on 127.0.0.1 only; use fastapi run or --host 0.0.0.0 — and put a TLS proxy in front of it in production.

  • Reload in production. --reload costs resources and can be unstable; keep it to development.

  • Forgotten quotes. pip install fastapi[standard] fails in zsh; use "fastapi[standard]".

  • FASTAPI_ENV in production. fastapi run does not set it to production; if your startup code branches on it, set it yourself.

Where Older Guides Are Outdated, and What Changed Recently

  • Installation. Older guides run pip install fastapi uvicorn[standard] and start uvicorn main:app --reload. Since 0.111.0 the fastapi command exists (fastapi dev, fastapi run), and since 0.112.0 pip install "fastapi[standard]" installs it with Uvicorn and the usual optional dependencies. Uvicorn invoked directly still works.

  • Package manager. The official documentation now uses uv projects by default (changed in 0.140.0, 2026-07-24) instead of manually activated environments.

  • Entrypoint configuration. [tool.fastapi] entrypoint replaces passing paths on every command.

  • Extras. fastapi-slim is gone; standard-no-fastapi-cloud-cli exists for installs without the cloud client.

  • Deployment. fastapi deploy and the FastAPI Cloud client arrived in 0.116.0 (2025-07-07).

  • Python versions. Python 3.8 was dropped in 0.125.0 and 3.9 in 0.129.0; supported versions are 3.10 to 3.14.

  • Testing client note. The installed Starlette 1.7 TestClient currently warns that using it with httpx is deprecated and suggests the httpx2 package. This does not change how tests are written; the Testing page covers TestClient in detail.