Skip to content

Environment and first endpoint

  • Lesson 4
  • 35 min
  • Module 2: Build a small FastAPI service

What you will be able to do

Run your own API on your computer, and prove that it answers. Every later lesson starts from this project and this check.

Before you startWhat to know and to have readyAbout 35 minutes

What you need to know

What you need on your computer

Tested with Python 3.14.6 and FastAPI 0.142.2 on macOS.

A web API needs two parts. A server program listens on a port, reads the HTTP text of each request, and writes the HTTP text of each response. A framework turns that request into a call to one of your Python functions, and turns the function’s return value into a response.

FastAPI is a Python framework for APIs. You write normal functions with type hints. FastAPI uses the hints to read the input, check it, convert the output to JSON, and write the documentation. The server program that runs it is Uvicorn. FastAPI installs it for you, and starts it with one command.

An endpoint is one method and one path that the API answers, for example GET /health. Each endpoint has one Python function.

The project for this course is ticket-api, next to the ticket-cleaner project from the Python course.

  1. Make the project folder, and move into it:

    Terminal window
    mkdir ~/projects/ticket-api
    cd ~/projects/ticket-api
  2. Make and activate a virtual environment:

    Terminal window
    python3 -m venv .venv
    source .venv/bin/activate

    The prompt starts with (.venv).

  3. Install FastAPI with its standard tools. The quotes are needed, because [ ] has a special meaning in some terminals:

    Terminal window
    python -m pip install "fastapi[standard]==0.142.2"

    [standard] adds the tools that this course uses: Uvicorn, the fastapi command and httpx.

  4. Record the dependencies, as in the Python course:

    requirements.txt
    # Needed to run the API
    fastapi[standard]==0.142.2
    Terminal window
    python -m pip freeze > requirements-lock.txt

    The lock file has about 56 lines: FastAPI, Uvicorn, Pydantic and the packages that they need.

Make the file .gitignore in ticket-api, as in Quality and handoff: A clean project layout:

.gitignore
.venv/
__pycache__/
.pytest_cache/
.env
.fastapicloud/

Each computer makes the environment and the caches again, and .env can hold secrets, so these files must not leave your computer. Git and the deploy command in Module 7 leave out every file that .gitignore lists. Most of these files do not exist yet: later lessons make them.

The code goes in a package, ticket_api, as in Modules and imports. Make the folder ticket_api with two files:

ticket_api/__init__.py
"""A small API that classifies support tickets."""
ticket_api/main.py
from fastapi import FastAPI
app = FastAPI(title="Ticket Classifier API")
@app.get("/health")
def health() -> dict[str, str]:
return {"status": "ok"}
Line Meaning
app = FastAPI(...) The application object. The server gives every request to it.
@app.get("/health") A decorator: it registers the function below it as the endpoint GET /health.
def health() -> dict[str, str]: A normal function. The return type tells FastAPI what the response contains.
return {"status": "ok"} FastAPI turns the dictionary into a JSON body, with status 200.

A health endpoint answers “the service runs”. It does no real work, so it is fast and almost never fails. Hosting platforms and monitoring tools call it to check that the service is alive. Every API in this course has one.

Terminal
fastapi dev ticket_api/main.py
Output
⚡️ Starting FastAPI in development mode
🐍 Using import string: ticket_api.main:app
🌐 Server started at http://127.0.0.1:8000
Documentation at http://127.0.0.1:8000/docs
Logs:
INFO: Will watch for changes in these directories: ['/Users/amira/projects/ticket-api']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [48081] using WatchFiles
INFO: Started server process [48088]
INFO: Waiting for application startup.
INFO: Application startup complete.

fastapi dev starts Uvicorn in development mode. It listens only on your computer (127.0.0.1), on port 8000. It restarts the server each time that you save a Python file. The terminal stays busy while the server runs. That is normal: it is waiting for requests.

Open a second terminal, because the first one is running the server. Then send a request:

Second terminal (on Windows: curl.exe)
curl -i http://127.0.0.1:8000/health
Output
HTTP/1.1 200 OK
date: Tue, 06 Oct 2026 20:39:29 GMT
server: uvicorn
content-length: 15
content-type: application/json
{"status":"ok"}

In the first terminal, the server writes one log line for each request:

First terminal
INFO: 127.0.0.1:59501 - "GET /health HTTP/1.1" 200 OK

You can also open http://127.0.0.1:8000/health in your browser. It shows the same JSON.

To stop the server, go to its terminal and press Ctrl+C.

The project now runs, so write down how to run it. A README tells the next person, and you in a month, how to set up and start the project. Make README.md in ticket-api:

README.md
# ticket-api
A small web API that classifies support tickets.
## Set up
```sh
python3 -m venv .venv # Windows: python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
python -m pip install -r requirements-lock.txt
```
## Run
```sh
fastapi dev ticket_api/main.py # http://127.0.0.1:8000
```

Keep it small for now. When a later lesson changes a command, change it in the README too. The sections follow Quality and handoff: The README.

  1. Do steps 1 to 5 on your computer.

  2. Request a path that does not exist: curl -i http://127.0.0.1:8000/nothing.

    The status is 404 Not Found, and the body is {"detail":"Not Found"}. FastAPI answers every unknown path for you.

  3. Change the health endpoint to return {"status": "ok", "service": "ticket-api"}. Change the return type to match. Save the file.

    In the server’s terminal, you see that it restarts by itself. Send the request again: the new field is there.

  4. Stop the server with Ctrl+C, and send the request again. Read the error, and say at which step from Module 1 it failed.

  5. Change the endpoint back to {"status": "ok"}, and start the server again.

Show the answer for step 4

curl answers Failed to connect to 127.0.0.1 port 8000 after 0 ms: Couldn't connect to server. It failed at the connection step: no program listens on port 8000 any more. There is no status code, because no application answered.

Add an endpoint GET /version that returns {"version": "0.1.0"}. Check it with curl and in the browser. Then predict: what does curl -i -X POST http://127.0.0.1:8000/version return, and why?

Show the answer
ticket_api/main.py (add)
@app.get("/version")
def version() -> dict[str, str]:
return {"version": "0.1.0"}

The POST returns 405 Method Not Allowed with {"detail":"Method Not Allowed"}. The path exists, but only for GET. The response also has the header allow: GET, which tells the client which methods are allowed. You can delete this endpoint now. The next lessons do not use it.

Common mistake

Address already in use

Symptom: fastapi dev stops at once with an error like one of these:

ERROR: [Errno 48] Address already in use
ERROR: [Errno 98] Address already in use
ERROR: [WinError 10048] Only one usage of each socket address is normally permitted

Cause: another program already listens on port 8000. Usually it is the same API, still running in a different terminal or a terminal tab that you forgot. Only one program can listen on a port.

Fix:

  1. Find the other terminal that runs fastapi dev, and stop it with Ctrl+C.
  2. If you cannot find it, use a different port for now: fastapi dev ticket_api/main.py --port 8001, and use 8001 in your requests.
  3. Closing all terminal windows also stops servers that you started in them.

Check your understanding

You call GET /health, and the API answers 200. What does that prove, and what does it not prove?

Show the answer

It proves that the server runs, listens on the port, and that the application handles requests. It does not prove that the real work works: in later lessons, the classifier could be down while /health still answers 200. Module 7 adds a second check, /ready, for that.

Recap

  • FastAPI turns HTTP requests into calls to your functions. Uvicorn is the server program that runs it.
  • An endpoint is a method and a path, registered with a decorator such as @app.get("/health").
  • fastapi dev ticket_api/main.py starts a development server on http://127.0.0.1:8000, which restarts when you save.
  • Keep the server in one terminal, and send requests from a second one.
  • A health endpoint proves that the service runs.
  • .gitignore keeps the environment, the caches and secrets on your computer. The README says how to set up and run the project.

Next, you add the real endpoint: it reads a ticket as JSON, checks it, and returns a classification.

Next: Request and response models