The API
Your code works with local files. After this section, it talks to the internet.
1. What is an API
Your app needs data it doesn't own. Weather, maps, payments, user posts. That data lives on someone else's server. The API is the door between your app and that server.
π§βπ» The Restaurant
| Restaurant | Code |
|---|---|
| You (customer) | Your App |
| The waiter | The API |
| The kitchen | The Server |
| The menu | The Documentation |
| Your order | The Request |
| The food | The Response |
You never enter the kitchen. Your app never touches the database. The API carries everything back and forth.
The Pattern

Every API call follows this shape:
π Summary
| Term | Meaning |
|---|---|
| API | The door between your app and a server |
| Request | What you send (URL + method) |
| Response | What comes back (status code + data) |
| Server | Someone else's computer with the data you need |
2. JSON
The format your app and the server agree on. Keys and values.
π§βπ» Example
{
"id": 1,
"name": "Bariis iyo Hilib",
"category": "main",
"price": 12.99
}Rules:
- Keys are always strings in double quotes
- Values can be: strings, numbers, booleans, arrays, objects
- No trailing commas
An array of items:
[
{"id": 1, "name": "Bariis iyo Hilib", "price": 12.99},
{"id": 2, "name": "Suugo Suqaar", "price": 10.99}
]π Summary
| Term | Meaning |
|---|---|
| JSON | The language APIs speak |
| Object {} | One item (like a Python dictionary) |
| Array [] | A list of items |
3. Methods and Status Codes
Four ways to talk to an API. Each one tells the server what you want.
π§βπ» The Four Methods
| Method | What it does | Like saying |
|---|---|---|
| GET | Read data | "Show me the menu" |
| POST | Create data | "Add a new dish" |
| PUT | Update data | "Change the price" |
| DELETE | Remove data | "Remove this dish" |
π§βπ» Status Codes
The server's answer in one number:
| Code | Meaning |
|---|---|
| 200 | OK |
| 201 | Created |
| 204 | No Content (deleted successfully) |
| 400 | Bad Request (you sent bad data) |
| 404 | Not Found (doesn't exist) |
| 500 | Server Error (something broke on their end) |
4. Setup: Mangio Express
We need a restaurant before we can order food. You get a pre-built API called Mangio Express. Don't read the code. Don't modify it. We build our own in the next section.
π§βπ» Steps
1. Install dependencies:
uv add requests fastapi uvicorn2. Create app.py in your project:
import json
from pathlib import Path
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
DB = Path("menu.json")
DEFAULT_MENU = [
{"id": 1, "name": "Bariis iyo Hilib", "category": "main", "price": 12.99},
{"id": 2, "name": "Suugo Suqaar", "category": "main", "price": 10.99},
{"id": 3, "name": "Sambusa", "category": "appetizer", "price": 4.99},
{"id": 4, "name": "Shaah Cadays", "category": "drink", "price": 2.99},
{"id": 5, "name": "Malawax", "category": "dessert", "price": 5.99},
{"id": 6, "name": "Cambuulo", "category": "main", "price": 8.99},
]
def load_menu():
if DB.exists():
return json.loads(DB.read_text())
return DEFAULT_MENU.copy()
def save_menu(menu):
DB.write_text(json.dumps(menu, indent=2))
def get_next_id(menu):
if not menu:
return 1
return max(dish["id"] for dish in menu) + 1
class Dish(BaseModel):
name: str
category: str
price: float
@app.get("/menu")
def get_menu(category: str = None):
menu = load_menu()
if category:
return [d for d in menu if d["category"] == category]
return menu
@app.get("/menu/{dish_id}")
def get_dish(dish_id: int):
menu = load_menu()
for dish in menu:
if dish["id"] == dish_id:
return dish
raise HTTPException(status_code=404, detail="Dish not found")
@app.post("/menu", status_code=201)
def add_dish(dish: Dish):
menu = load_menu()
new = {"id": get_next_id(menu), **dish.model_dump()}
menu.append(new)
save_menu(menu)
return new
@app.delete("/menu/{dish_id}", status_code=204)
def delete_dish(dish_id: int):
menu = load_menu()
for i, dish in enumerate(menu):
if dish["id"] == dish_id:
menu.pop(i)
save_menu(menu)
return
raise HTTPException(status_code=404, detail="Dish not found")
@app.put("/menu/{dish_id}")
def update_dish(dish_id: int, dish: Dish):
menu = load_menu()
for i, d in enumerate(menu):
if d["id"] == dish_id:
menu[i] = {"id": dish_id, **dish.model_dump()}
save_menu(menu)
return menu[i]
raise HTTPException(status_code=404, detail="Dish not found")3. Start the server:
uv run uvicorn app:app --reload4. Open your browser: localhost:8000/menu
JSON comes back. The restaurant is open.
π Summary
| Part | Meaning |
|---|---|
| uv run | Run using your project's environment |
| uvicorn | The server that listens for requests |
| app:app | File app.py, variable app inside it |
| --reload | Restart when you save changes |
| menu.json | Where data is saved (delete to reset) |
5. GET
Read data from the server.
π§βπ» Get the full menu
import requests
response = requests.get("http://localhost:8000/menu")
menu = response.json()
for dish in menu:
print(f"{dish['name']} - ${dish['price']}")π§βπ» Get one dish
response = requests.get("http://localhost:8000/menu/3")
dish = response.json()
print(dish)π§βπ» GET with Streamlit
import streamlit as st
import requests
st.title("Mangio Express")
response = requests.get("http://localhost:8000/menu")
menu = response.json()
for dish in menu:
st.write(f"**{dish['name']}** β ${dish['price']}")π Summary
| What | How |
|---|---|
| Send GET request | requests.get(url) |
| Read the data | response.json() |
| Check the status | response.status_code |
6. POST
Send new data to the server.
π§βπ» Add a new dish
import requests
new_dish = {
"name": "Canjeero",
"category": "main",
"price": 6.99
}
response = requests.post("http://localhost:8000/menu", json=new_dish)
print(response.status_code) # 201
print(response.json())π§βπ» Prove it worked
response = requests.get("http://localhost:8000/menu/7")
print(response.json())π§βπ» POST with Streamlit
import streamlit as st
import requests
st.title("Mangio Express")
st.subheader("Add a Dish")
name = st.text_input("Dish name")
category = st.selectbox("Category", ["main", "appetizer", "drink", "dessert"])
price = st.number_input("Price", min_value=0.0, step=0.5)
if st.button("Add to Menu"):
new_dish = {"name": name, "category": category, "price": price}
response = requests.post("http://localhost:8000/menu", json=new_dish)
st.success(f"Added {name}!")
st.subheader("Current Menu")
response = requests.get("http://localhost:8000/menu")
menu = response.json()
for dish in menu:
st.write(f"**{dish['name']}** β ${dish['price']}")π Summary
| What | How |
|---|---|
| Send POST request | requests.post(url, json=data) |
| json= | Sends the dictionary as JSON |
| 201 | Server created the dish |
7. DELETE
Remove data from the server.
π§βπ» Delete a dish
import requests
response = requests.delete("http://localhost:8000/menu/3")
print(response.status_code) # 204π§βπ» Prove it's gone
response = requests.get("http://localhost:8000/menu/3")
print(response.status_code) # 404π§βπ» DELETE with Streamlit
import streamlit as st
import requests
st.title("Mangio Express")
response = requests.get("http://localhost:8000/menu")
menu = response.json()
for m in menu:
col1, col2 = st.columns([4, 1])
col1.write(f"**{m['name']}** β ${m['price']}")
if col2.button("Delete", key=m["id"]):
requests.delete(f"http://localhost:8000/menu/{m['id']}")
st.rerun()π§βπ» The Inline Delete Pattern
| Part | Why |
|---|---|
| st.columns([4, 1]) | Content left, button right |
| key=m["id"] | Each button needs a unique key |
| st.rerun() | Refresh the page after deleting |
π Summary
| What | How |
|---|---|
| Send DELETE request | requests.delete(url) |
| 204 | Deleted (no content comes back) |
| Delete again? | 404 (already gone) |
8. PUT
Update existing data on the server. PUT replaces the entire item. Send all fields, not just the one you changed.
π§βπ» Update a dish
import requests
updated_dish = {
"name": "Sambusa",
"category": "appetizer",
"price": 5.99
}
response = requests.put("http://localhost:8000/menu/3", json=updated_dish)
print(response.status_code) # 200
print(response.json())π Summary
| What | How |
|---|---|
| Send PUT request | requests.put(url, json=data) |
| URL includes ID | /menu/3 (which dish to update) |
| Send all fields | PUT replaces the whole dish |
Quick Reference
| Action | Code |
|---|---|
| Get all | requests.get("http://localhost:8000/menu") |
| Get one | requests.get("http://localhost:8000/menu/3") |
| Create | requests.post("http://localhost:8000/menu", json=data) |
| Update | requests.put("http://localhost:8000/menu/3", json=data) |
| Delete | requests.delete("http://localhost:8000/menu/3") |
| Read response | response.json() |
| Check status | response.status_code |