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]"
Source: FastAPI: Tutorial - User Guide
-
uv init bookshelf --barecreates a directory with only a minimalpyproject.toml(no samplemain.py). -
uv add "fastapi[standard]"creates the virtual environment in.venv(you do not activate it), adds FastAPI topyproject.tomland records the exact resolved versions of everything inuv.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 exampleuv 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]"
Source: FastAPI: Virtual Environments
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 |
|---|---|
|
The framework, Starlette and Pydantic only. No CLI, no server, no form or file support. |
|
The default for applications. Adds the |
|
Everything in |
|
|
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}
Source: FastAPI: First Steps
What the code declares:
-
app = FastAPI(…)creates the ASGI application.titleandversionend up in the OpenAPI document and the interactive documentation. -
@app.get("/")is a path operation decorator: it binds the HTTP methodGETand the path/to the function below it. The function is the path operation function.async defand plaindefboth work; see Async & Concurrency for how to choose. -
{book_id}in the path is a path parameter andbook_id: intits type.verbose: bool = Falseis not in the path, so FastAPI treats it as an optional query parameter with defaultFalse. -
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"]))
Source: FastAPI: Testing
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
Source: FastAPI: FastAPI CLI
fastapi dev |
fastapi run |
|
|---|---|---|
Reload on code changes |
on (resource-intensive and less stable than off; development only) |
off |
Default host |
|
|
Intended for |
your laptop |
containers and servers, behind a TLS-terminating proxy |
|
set to |
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 port8000). -
--reload/--no-reload— turn auto-reload on or off, overriding the command’s default. -
--reload-dir(dev) — limit the directories watched for changes. -
--workers(runonly) — the number of worker processes, mutually exclusive with--reload(see Deployment). -
--root-path— the ASGIroot_path, for apps served under a path prefix by a proxy. -
--appand--entrypoint/-e— name the application object instead of relying on detection. -
--proxy-headers/--no-proxy-headersand--forwarded-allow-ips— trustX-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.
Source: FastAPI: FastAPI CLI
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"
Source: FastAPI: FastAPI CLI
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 |
|---|---|
|
Swagger UI: browse operations, fill in parameters, press Try it out and see the real request and response. |
|
ReDoc: a read-only, single-page reference rendering of the same document. |
|
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"
}
]
}
Source: FastAPI: Handling Errors
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}
Source: FastAPI: First Steps
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 thefastapicommand, started as a module. Useful when the scripts directory of the environment is not onPATH:python -m fastapi dev. (Added in 0.112.0 together with thestandardextra.) -
Uvicorn directly —
uvicorn main:app --reload --host 127.0.0.1 --port 8000. The argument is an import string (module:variable);--reloadand--workersneed it so they can re-import the app in a new process. This is what older guides use, and it is still valid —fastapi devandfastapi runare 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)
Source: FastAPI: Debugging
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
Source: FastAPI: Tutorial - User Guide
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
Source: FastAPI: About FastAPI versions
# requirements.txt
fastapi[standard]>=0.142.2,<0.143.0
Source: FastAPI: About FastAPI versions
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
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.pyoruvicorn.pyin 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
apporapi. 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 devlistens on127.0.0.1only; usefastapi runor--host 0.0.0.0— and put a TLS proxy in front of it in production. -
Reload in production.
--reloadcosts resources and can be unstable; keep it to development. -
Forgotten quotes.
pip install fastapi[standard]fails in zsh; use"fastapi[standard]". -
FASTAPI_ENVin production.fastapi rundoes not set it toproduction; 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 startuvicorn main:app --reload. Since 0.111.0 thefastapicommand exists (fastapi dev,fastapi run), and since 0.112.0pip install "fastapi[standard]"installs it with Uvicorn and the usual optional dependencies. Uvicorn invoked directly still works. -
Package manager. The official documentation now uses
uvprojects by default (changed in 0.140.0, 2026-07-24) instead of manually activated environments. -
Entrypoint configuration.
[tool.fastapi] entrypointreplaces passing paths on every command. -
Extras.
fastapi-slimis gone;standard-no-fastapi-cloud-cliexists for installs without the cloud client. -
Deployment.
fastapi deployand 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
TestClientcurrently warns that using it withhttpxis deprecated and suggests thehttpx2package. This does not change how tests are written; the Testing page coversTestClientin detail.