FastAPIInteractive

FastAPI Playground

Write FastAPI and run it immediately. A real Python interpreter and a real FastAPI server start inside this tab — no installation, no account, and nothing leaves your browser.

Press Run to start the server and send this request.

What you can try

Each of these loads into the editor above, replacing the current project. They run as written.

Starter

Path parameters, an optional query parameter and a validated request body — the three things a type hint gets you for free.

# A real FastAPI app, running entirely in your browser.
# Edit anything, press Run, then open /docs for the interactive API docs.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI(title="Playground")
class Item(BaseModel):
name: str
price: float = Field(gt=0)
tags: list[str] = []
@app.get("/")
async def root():
return {"message": "It works. Try /items/42?q=hello"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.post("/items")
async def create_item(item: Item):
return {"created": item.name, "with_tax": round(item.price * 1.23, 2)}

Path & query

Types on the function signature become the parsing and validation layer. Ask for /users/abc and the 422 writes itself.

from fastapi import FastAPI
app = FastAPI(title="Path & query parameters")
@app.get("/users/{user_id}")
async def get_user(user_id: int, verbose: bool = False):
user = {"id": user_id, "name": f"User {user_id}"}
if verbose:
user["joined"] = "2026-01-01"
return user
@app.get("/search")
async def search(q: str, limit: int = 10, offset: int = 0):
return {"q": q, "limit": limit, "offset": offset}

Validation

Pydantic constraints on a model. Send this body as-is and you get a 422 naming both broken fields and why.

from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI(title="Validation")
class Signup(BaseModel):
username: str = Field(min_length=3, max_length=20)
age: int = Field(ge=13, le=120)
tags: list[str] = []
@app.post("/signup")
async def signup(user: Signup):
return {"ok": True, "username": user.username}

Dependencies

Depends() factors shared request logic out of handlers. The same pagination dependency can serve every list endpoint.

from fastapi import Depends, FastAPI, HTTPException
app = FastAPI(title="Dependencies")
async def pagination(limit: int = 10, offset: int = 0):
if limit > 100:
raise HTTPException(status_code=400, detail="limit must be 100 or less")
return {"limit": limit, "offset": offset}
@app.get("/items")
async def list_items(page: dict = Depends(pagination)):
start = page["offset"]
return {
"page": page,
"items": [f"item-{n}" for n in range(start, start + page["limit"])],
}

Auth

A bearer-token dependency. The request below has no token, so FastAPI returns 401 before your handler runs — open /docs and use Authorize to send one.

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
app = FastAPI(title="Auth")
security = HTTPBearer()
TOKENS = {"secret-token": "alice"}
@app.get("/me")
async def me(creds: HTTPAuthorizationCredentials = Depends(security)):
user = TOKENS.get(creds.credentials)
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
)
return {"user": user}

Background tasks

Work that happens after the response is sent. POST to /notify, then GET /log to see that it ran.

from fastapi import BackgroundTasks, FastAPI
app = FastAPI(title="Background tasks")
log: list[str] = []
def write_log(message: str) -> None:
log.append(message)
@app.post("/notify/{name}")
async def notify(name: str, background: BackgroundTasks):
background.add_task(write_log, f"notified {name}")
return {"queued": name}
@app.get("/log")
async def read_log():
return {"log": log}

Bigger application3 files

A real project layout: routers in their own package, models in their own module, imported across files. This is the one thing an online Python REPL cannot do.

main.py

from fastapi import FastAPI
from routers import items
app = FastAPI(title="Bigger application")
app.include_router(items.router)
@app.get("/")
async def root():
return {"try": ["/items", "/items/1", "/items/99"]}

models.py

from pydantic import BaseModel, Field
class Item(BaseModel):
name: str
price: float = Field(gt=0)

routers/items.py

from fastapi import APIRouter, HTTPException
from models import Item
router = APIRouter(prefix="/items", tags=["items"])
ITEMS: dict[int, Item] = {
1: Item(name="Widget", price=9.99),
2: Item(name="Gadget", price=24.50),
}
@router.get("")
async def list_items():
return {"items": ITEMS}
@router.get("/{item_id}")
async def get_item(item_id: int):
if item_id not in ITEMS:
raise HTTPException(status_code=404, detail="Item not found")
return ITEMS[item_id]

Questions

Do I need to install anything?
No. Python, FastAPI, Pydantic and Uvicorn run inside your browser tab through WebAssembly. Nothing is installed and nothing is sent to a server — your code never leaves your machine.
Is this a real FastAPI server?
Yes. It is the real FastAPI package handling real ASGI requests, not a simulation. Validation errors, status codes, dependency injection and the generated OpenAPI schema all behave exactly as they would locally.
Can I see the interactive API docs?
Press the /docs button. FastAPI generates Swagger UI from your code automatically, and it runs here against the app you just wrote, including the Authorize button for secured endpoints.
Which Python version and packages are available?
Python 3.12 with FastAPI, Pydantic v2, Starlette, SQLModel and python-jose preinstalled. Anything relying on threads or native sockets will not work, because WebAssembly has neither.
Can I create more than one file?
Yes. Add files in the explorer and import between them as you would locally — a path like routers/items.py becomes an importable package. The Bigger application example ships as three files precisely to show it. This is the part an ordinary online Python REPL cannot do.
Is my code saved?
Your most recent code is kept in this browser only, so a refresh will not lose it. It is not synced to an account and is not included in a data export.

Want it structured?

The playground is a blank page on purpose. If you would rather be walked through it, there are 61 graded lessons that run in the same engine — each one checks your code by calling the endpoints you wrote.