Blood Pressure Risk Classifier API
Build an ML-powered blood pressure risk API using FastAPI and Claude Code.
Introduction
30 Second Summary
Every time you visit the doctor, they wrap a cuff around your arm and check your blood pressure. Those numbers get compared against clinical thresholds to determine your risk level for heart disease.
In this project, you will train a machine learning model to automate that classification. You'll serve it through a REST API that accepts patient measurements and returns a predicted hypertension risk category with a confidence score.
What You'll Build
A FastAPI application powered by a Random Forest machine learning model that classifies blood pressure readings into four risk levels: Normal, Elevated, Hypertension Stage 1, and Hypertension Stage 2.
By the end of this project, you'll have:
- A trained Random Forest model that categorizes patient health data into hypertension risk levels.
- A running FastAPI REST API with endpoints for health checks and blood pressure risk prediction.
- Hands-on experience using Claude Code as an AI pair-programmer to build the entire application from scratch.
- Secret Mission: Add a prediction history endpoint that tracks patient trends over time.
Are there any prerequisites?
You need basic Python knowledge (variables, functions, lists). No prior machine learning or API experience is required.
Claude Code requires a paid Claude plan (Pro at $20/mo minimum). All other tools used in this project are free.
Set Up Your Development Environment
To build a machine learning API, you need the right tools in place. This project uses Claude Code as your AI pair-programmer, Python for the application code, and several libraries for ML and API development.
In this step, you will install Claude Code, create a project folder with a Python virtual environment, and configure the project so Claude Code understands your conventions.
In this step, get ready to:
- Install and authenticate Claude Code.
- Create a project directory with a Python virtual environment and dependencies.
- Configure Claude Code with a CLAUDE.md file and start a session.
Install Claude Code
Claude Code is an AI coding assistant that lives in your terminal. It reads your entire codebase, edits files, and runs commands through natural language. You will use it throughout this project to generate and refine your ML model and API code.
Paid plan required
Claude Code requires a paid Claude plan (Pro at $20/month minimum, or Max, Team, or Enterprise). The free Claude.ai plan does not include Claude Code access.
- Open your terminal. Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
- Install Claude Code by running the command for your operating system:
macOS
curl -fsSL https://claude.ai/install.sh | bash
What does this command do?
This downloads the official Claude Code installer script from Anthropic and runs it. The native installer bundles its own runtime, so you do not need Node.js installed.
Windows
irm https://claude.ai/install.ps1 | iex
What does this command do?
This uses PowerShell's Invoke-RestMethod to download the official installer script, then pipes it to Invoke-Expression to run it. The native installer bundles its own runtime.
- After the installer finishes, authenticate by running:
claude
Claude Code will open your browser and ask you to log in to your Anthropic account. Follow the prompts to authorize access.
- Once authenticated, exit the session by pressing Ctrl+D or typing exit.
- Verify your installation by running:
claude --version
✔️ I see a version number
Claude Code is installed and ready. You can move on to the next substep.
ⓧ Command not found
Your terminal cannot find the claude binary. Try these fixes:
- Close your terminal completely and reopen it. The installer may have updated your PATH, which only takes effect in a new session.
- On macOS, check that ~/.local/bin is in your PATH. Run echo $PATH to confirm.
- Re-run the install command from above and look for any error messages in the output.
Create the project and install dependencies
Python 3.11+ is required for this project because scikit-learn 1.8.0 only supports Python 3.11 and above.
- Check your Python version by running:
python --version
✔️ I see version 3.11 or higher
Your Python version is compatible. Continue to the next instruction.
ⓧ I see a version lower than 3.11
You need Python 3.11+ to continue. Your current version will cause silent failures when installing dependencies.
- On Windows, download the latest Python from python.org and run the installer. Make sure to check Add Python to PATH during installation.
- On macOS, if you use Homebrew, run brew install python@3.11 and then use python3.11 instead of python in all commands below. macOS ships with an older Python by default that the python3 command may still point to.
- After installing, close and reopen your terminal, then run python --version again to confirm.
ⓧ Command not found
Python is not installed on your system.
- On Windows, download Python from python.org and run the installer. Check Add Python to PATH during setup.
- On macOS, install via Homebrew by running brew install python@3.11 in Terminal.
- Close and reopen your terminal after installing, then verify with python --version.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir bp-monitor-api
cd bp-monitor-api
- Set up a Python virtual environment by running:
macOS
python3 -m venv venv
source venv/bin/activate
Windows
python -m venv venv
venv\Scripts\activate
What is a virtual environment?
A virtual environment is an isolated Python installation inside your project folder. It keeps this project's packages separate from your system Python, preventing version conflicts between projects.
- Install the project dependencies by running:
pip install "fastapi[standard]" scikit-learn joblib numpy
What are these packages?
- fastapi[standard] installs FastAPI plus Uvicorn and other tools needed to run a development server.
- scikit-learn provides machine learning algorithms including the Random Forest classifier you will train.
- joblib handles saving and loading your trained model to disk.
- numpy provides the array operations used to format data for the model.
You should see packages downloading and installing. The process may take a minute.
Install failing with errors?
- If you see a permissions error, make sure your virtual environment is activated. You should see (venv) at the beginning of your terminal prompt.
- If scikit-learn fails to build, confirm your Python version is 3.11+ by running python --version. Older versions are not compatible with scikit-learn 1.8.0.
Still stuck? Help me troubleshoot my pip install failure
Create CLAUDE.md and start Claude Code
A CLAUDE.md file in your project root tells Claude Code about your project's conventions, tech stack, and file structure. Claude loads this file automatically when you start a session, so it always has the right context.
- Create the CLAUDE.md file inside your bp-monitor-api folder. If you have a code editor open, create a new file named CLAUDE.md. Otherwise, create it from your terminal.
- Paste the following content into CLAUDE.md:
# BP Monitor API Project
## Tech Stack
- Python 3.11+
- FastAPI for the REST API
- scikit-learn for machine learning
- Pydantic for data validation
## Conventions
- Use type hints everywhere
- Use Pydantic models for request/response schemas
- Keep schemas in schemas.py, app in main.py, training in train_model.py
- Use the lifespan context manager for model loading (not deprecated on_event)
- Save trained models to the model/ directory
## Commands
- Run API: fastapi dev main.py
- Train model: python train_model.py
- Test: open http://127.0.0.1:8000/docs
Why does this file matter?
Without CLAUDE.md, Claude Code would have to guess your project conventions every time. With it, Claude Code knows to use Pydantic for schemas, the lifespan pattern for model loading, and your exact file naming structure. This means less correcting and faster results.
- Save CLAUDE.md.
- Confirm the file exists by running:
ls
You should see CLAUDE.md and venv listed in the output.
- Start your Claude Code session in the project directory by running:
claude
Claude Code reads your CLAUDE.md file and is now ready to help you build the blood pressure classifier. You will keep this session open for the rest of the project.
Claude Code not starting?
- Make sure you are inside the bp-monitor-api folder. Run pwd (macOS) or cd (Windows) to check your current directory.
- If you see an authentication error, run claude again and follow the browser login flow.
Still stuck? Help me get Claude Code running in my project
✔️ Awesome, I've got everything!
Great. Make sure you have saved CLAUDE.md and your Claude Code session is running.
ⓧ I'd like to double check the full code
# BP Monitor API Project
## Tech Stack
- Python 3.11+
- FastAPI for the REST API
- scikit-learn for machine learning
- Pydantic for data validation
## Conventions
- Use type hints everywhere
- Use Pydantic models for request/response schemas
- Keep schemas in schemas.py, app in main.py, training in train_model.py
- Use the lifespan context manager for model loading (not deprecated on_event)
- Save trained models to the model/ directory
## Commands
- Run API: fastapi dev main.py
- Train model: python train_model.py
- Test: open http://127.0.0.1:8000/docs
Your development environment is fully set up. Next, you will use Claude Code to train a machine learning model that classifies blood pressure readings into risk categories.
Train the Blood Pressure Classification Model
Your development environment is set up and Claude Code is ready to go. Now it's time to build the machine learning model that powers your API.
In this step, you'll use Claude Code to generate a training script that creates synthetic patient data, trains a Random Forest classifier on clinical blood pressure thresholds, and saves the trained model for your API to use later.
In this step, get ready to:
- Generate a training script with synthetic patient health data.
- Train a Random Forest classifier on the dataset.
- Verify the model achieves reasonable accuracy.
Use Claude Code to generate the training script
Your blood pressure classifier needs training data to learn from. Instead of using real patient data (which involves privacy concerns), you'll generate a synthetic dataset of 1000 patient records with features like age, BMI, and blood pressure readings.
- In your Claude Code session, type the following prompt and press Enter to submit it:
Create a train_model.py script that: 1) Generates a synthetic dataset of 1000 patient records with features age, bmi, systolic_bp, diastolic_bp, heart_rate, and exercise_hours. Use numpy with random seed 42. Make BP values influenced by age and BMI. 2) Classifies patients into 4 categories based on clinical BP thresholds (Normal: systolic<120 AND diastolic<80, Elevated: systolic 120-129 AND diastolic<80, Stage 1: systolic>=130 OR diastolic>=80, Stage 2: systolic>=140 OR diastolic>=90). 3) Trains a RandomForestClassifier with 100 estimators and 80/20 train-test split (stratified, random_state=42). 4) Prints accuracy score and full classification report with target names Normal, Elevated, Stage 1, Stage 2. 5) Saves the model to model/bp_model.joblib using os.makedirs for the directory.
What is Claude Code doing?
Claude Code reads your prompt, plans the file structure, and writes train_model.py directly to your project folder. It will ask for permission before creating the file.
When Claude Code asks to write the file, accept the change. You'll see the generated code appear in your terminal output.
What does the synthetic data represent?
Each of the 1000 records represents a patient with six health measurements: age, BMI, systolic blood pressure, diastolic blood pressure, heart rate, and weekly exercise hours.
The script makes blood pressure values realistic by basing them on age and BMI. Older patients with higher BMI tend to have higher blood pressure, while exercise lowers it. This mimics real-world medical data patterns.
- Confirm that Claude Code created the train_model.py file in your bp-monitor-api directory.
Claude Code generated something different?
Claude Code is an AI tool, so its output may vary slightly from what's shown in this project. The important parts to check: the script uses numpy with random_seed(42), trains a RandomForestClassifier, and saves the model to model/bp_model.joblib.
If the generated code looks very different, you can ask Claude Code to revise it: "Update train_model.py to match these requirements exactly" and re-state the key details.
Help me check if my train_model.py has the right structure
Review the model training and evaluation code
The script Claude Code generated handles training, evaluation, and saving in one pass. Let's walk through what the training portion does.
How does the training work?
The script splits the 1000 patient records into 800 training samples and 200 test samples using an 80/20 split. The stratify=y parameter ensures each split has the same proportion of Normal, Elevated, Stage 1, and Stage 2 patients.
A Random Forest classifier works by building 100 decision trees (n_estimators=100), each trained on a random subset of the data. When making a prediction, all 100 trees vote and the majority wins. This ensemble approach is why Random Forest performs well for medical classification tasks.
After training, the script prints an accuracy score and a classification report showing precision, recall, and F1-score for each risk category. Finally, it saves the trained model to model/bp_model.joblib so your API can load it later without retraining.
- Double-check that your train_model.py matches the expected code below.
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before running it in the next substep.
ⓧ I'd like to double check the full code
Here is the full train_model.py your project should have. If Claude Code generated something different, you can replace the file contents with this code:
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score
import joblib
import os
# Create synthetic BP dataset
np.random.seed(42)
n_samples = 1000
# Generate features
age = np.random.randint(18, 80, n_samples)
bmi = np.random.uniform(18.5, 40.0, n_samples)
heart_rate = np.random.randint(55, 110, n_samples)
exercise_hours = np.random.uniform(0, 10, n_samples)
# Generate BP values influenced by age and BMI
systolic_bp = (
100
+ (age * 0.4)
+ (bmi * 0.8)
- (exercise_hours * 1.5)
+ np.random.normal(0, 8, n_samples)
)
diastolic_bp = (
60
+ (age * 0.2)
+ (bmi * 0.4)
- (exercise_hours * 0.8)
+ np.random.normal(0, 5, n_samples)
)
# Classify based on clinical BP thresholds
# Normal: systolic < 120 AND diastolic < 80
# Elevated: systolic 120-129 AND diastolic < 80
# Stage 1: systolic 130-139 OR diastolic 80-89
# Stage 2: systolic >= 140 OR diastolic >= 90
categories = []
for s, d in zip(systolic_bp, diastolic_bp):
if s >= 140 or d >= 90:
categories.append(3) # Stage 2
elif s >= 130 or d >= 80:
categories.append(2) # Stage 1
elif s >= 120:
categories.append(1) # Elevated
else:
categories.append(0) # Normal
categories = np.array(categories)
# Combine features
X = np.column_stack([age, bmi, systolic_bp, diastolic_bp, heart_rate, exercise_hours])
y = categories
# Split data
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Train Random Forest
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Evaluate
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Model Accuracy: {accuracy:.2%}")
print("\nClassification Report:")
print(
classification_report(
y_test,
y_pred,
target_names=["Normal", "Elevated", "Stage 1", "Stage 2"],
)
)
# Save model
os.makedirs("model", exist_ok=True)
joblib.dump(model, "model/bp_model.joblib")
print("\nModel saved to model/bp_model.joblib")
Run the training script and verify results
With the script ready, it's time to train the model and see how well it classifies blood pressure readings.
- Exit Claude Code temporarily by pressing Ctrl+D or typing exit so you can run the training script directly.
- Run the training script by entering this command:
python train_model.py
You should see output showing the model accuracy (likely above 90%) followed by a classification report with precision, recall, and F1-score for each category. The last line confirms the model was saved.
What should I see?
Your terminal output should look similar to this:
Model Accuracy: 95.50%
Classification Report:
precision recall f1-score support
Normal 0.95 0.97 0.96 39
Elevated 0.85 0.79 0.82 14
Stage 1 0.94 0.96 0.95 55
Stage 2 0.99 0.98 0.98 92
accuracy 0.96 200
macro avg 0.93 0.92 0.93 200
weighted avg 0.95 0.96 0.95 200
Model saved to model/bp_model.joblib
Your exact numbers may differ slightly, but accuracy should be above 90%. The classification report shows how well the model performs on each risk category. Higher precision means fewer false positives, and higher recall means fewer missed cases.
- Confirm the model file was saved by running:
ls model/
You should see bp_model.joblib listed in the output.
Seeing an import error?
If you see ModuleNotFoundError: No module named 'sklearn', your virtual environment may not be active. Activate it by running venv\Scripts\activate (Windows) or source venv/bin/activate (macOS/Linux), then try the command again.
If you see a syntax error, open train_model.py in a text editor and compare it against the full code in the double-check tab above.
Help me fix an error when running train_model.py
Your trained model is saved and ready to serve predictions. Next up, you'll build a FastAPI application that loads this model and exposes it through a REST API endpoint.
Build the FastAPI Application
You have a trained model sitting in a file, but nobody can use it yet. To make your classifier accessible, you need to wrap it in a web API that accepts patient data and returns predictions.
In this step, you'll use Claude Code to build a FastAPI application with Pydantic data validation. The API will load your trained model at startup and expose a /predict endpoint that accepts patient measurements and returns a risk classification.
In this step, get ready to:
- Create Pydantic schemas for request and response validation.
- Build a FastAPI application with health check and prediction endpoints.
- Run the development server and test the API with sample data.
Create the data schemas with Claude Code
Before building the API itself, you need to define the shape of data going in and coming out. Pydantic models act as contracts that automatically validate incoming requests and format outgoing responses.
- In your Claude Code session, ask Claude Code to create the schemas file by typing this prompt:
Create a schemas.py file with two Pydantic models. First, a PatientData model for validating incoming patient measurements with these fields and constraints: age (int, 1-120), bmi (float, 10.0-60.0), systolic_bp (float, 70-250), diastolic_bp (float, 40-150), heart_rate (int, 30-200), and exercise_hours_per_week (float, 0-40). Second, a PredictionResponse model with fields: risk_category (str), confidence (float), and recommendation (str). Use Field from pydantic with ge/le constraints and descriptions.
Claude Code will create schemas.py in your project directory with both models.
What does this code do?
The PatientData model defines every field the API expects from a request. Each field has type constraints (like ge=1, le=120 for age) that FastAPI enforces automatically. If someone sends invalid data, the API returns a 422 error without your code needing to check anything manually.
The PredictionResponse model shapes what the API sends back. It guarantees every response includes the risk category, a confidence score, and a health recommendation.
- Confirm Claude Code created schemas.py by checking that the file appears in your project directory.
Claude Code generated something different?
Claude Code may name fields slightly differently or use different constraint values. Compare the output against the reference below and ask Claude Code to fix any differences.
If Claude Code asks clarifying questions instead of generating the file, answer them or rephrase your prompt to be more specific about the field names and constraints.
help me fix my schemas.py file
✔️ Awesome, I've got everything!
Great. Double check you've saved your file and move on to building the API.
ⓧ I'd like to double check the full code
Your schemas.py should look like this:
from pydantic import BaseModel, Field
class PatientData(BaseModel):
age: int = Field(ge=1, le=120, description="Patient age in years")
bmi: float = Field(ge=10.0, le=60.0, description="Body Mass Index")
systolic_bp: float = Field(ge=70, le=250, description="Systolic blood pressure in mmHg")
diastolic_bp: float = Field(ge=40, le=150, description="Diastolic blood pressure in mmHg")
heart_rate: int = Field(ge=30, le=200, description="Heart rate in bpm")
exercise_hours_per_week: float = Field(
ge=0, le=40, description="Hours of exercise per week"
)
class PredictionResponse(BaseModel):
risk_category: str = Field(description="Predicted hypertension risk level")
confidence: float = Field(description="Model confidence score (0-1)")
recommendation: str = Field(description="Health recommendation based on risk level")
Build the API endpoints with Claude Code
The API needs to load your trained model once at startup, then use it to classify every incoming request. FastAPI's lifespan context manager handles this startup and shutdown lifecycle cleanly.
- Ask Claude Code to create the main application file by typing this prompt:
Create main.py with a FastAPI application. Define two dictionaries at the top: RISK_CATEGORIES mapping 0-3 to "Normal", "Elevated", "Hypertension Stage 1", "Hypertension Stage 2", and RECOMMENDATIONS mapping 0-3 to health advice strings for each level. Use a lifespan async context manager to load the model from model/bp_model.joblib into a module-level ml_model dict at startup. Create two endpoints: GET /health that returns status and model_loaded boolean, and POST /predict that takes PatientData, builds a numpy array of features, calls predict and predict_proba on the classifier, and returns a PredictionResponse with the risk category, rounded confidence, and recommendation.
Claude Code will create main.py with the full FastAPI application, model loading logic, and both endpoints.
What does the lifespan pattern do?
The lifespan function runs once when the server starts. It loads your trained model from disk into memory so every request can use it without reloading the file. The yield statement separates startup logic (above) from shutdown logic (below).
This is the modern replacement for the deprecated @app.on_event("startup") decorator. Your CLAUDE.md file told Claude Code to use this pattern instead.
How does the /predict endpoint work?
The predict_risk function takes the validated PatientData and converts it into a numpy array matching the feature order the model was trained on. It calls predict to get the category and predict_proba to get the confidence score for that prediction.
The response maps the numeric prediction (0-3) to a human-readable risk category and a tailored health recommendation.
- Confirm Claude Code created main.py in your project directory.
Model path or import issues?
Make sure main.py imports PatientData and PredictionResponse from schemas (not schema or models). The model path should be model/bp_model.joblib (matching the directory train_model.py created in the previous step).
help me fix my main.py imports
✔️ Awesome, I've got everything!
Great. Double check you've saved the file before starting the server.
ⓧ I'd like to double check the full code
Your main.py should look like this:
from contextlib import asynccontextmanager
import joblib
import numpy as np
from fastapi import FastAPI
from schemas import PatientData, PredictionResponse
RISK_CATEGORIES = {
0: "Normal",
1: "Elevated",
2: "Hypertension Stage 1",
3: "Hypertension Stage 2",
}
RECOMMENDATIONS = {
0: "Your blood pressure is normal. Maintain a healthy lifestyle with regular exercise and balanced diet.",
1: "Your blood pressure is elevated. Consider reducing sodium intake and increasing physical activity.",
2: "You may have Stage 1 hypertension. Consult a healthcare provider about lifestyle changes and possible medication.",
3: "You may have Stage 2 hypertension. Seek medical attention promptly for evaluation and treatment.",
}
ml_model = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
ml_model["classifier"] = joblib.load("model/bp_model.joblib")
yield
ml_model.clear()
app = FastAPI(
title="Blood Pressure Risk Classifier API",
description="ML-powered API for classifying blood pressure risk levels",
version="1.0.0",
lifespan=lifespan,
)
@app.get("/health")
def health_check():
return {"status": "healthy", "model_loaded": "classifier" in ml_model}
@app.post("/predict", response_model=PredictionResponse)
def predict_risk(patient: PatientData):
features = np.array(
[
[
patient.age,
patient.bmi,
patient.systolic_bp,
patient.diastolic_bp,
patient.heart_rate,
patient.exercise_hours_per_week,
]
]
)
prediction = ml_model["classifier"].predict(features)[0]
probabilities = ml_model["classifier"].predict_proba(features)[0]
confidence = float(probabilities[prediction])
return PredictionResponse(
risk_category=RISK_CATEGORIES[prediction],
confidence=round(confidence, 3),
recommendation=RECOMMENDATIONS[prediction],
)
Run the API and test predictions
With both files in place, you can start the development server. FastAPI includes a built-in CLI that launches Uvicorn with auto-reload, so any code changes you make take effect immediately.
- Exit your Claude Code session first by pressing Ctrl+C or typing /exit.
- Start the FastAPI development server by running this command:
fastapi dev main.py
You should see output including a line that says Uvicorn running on http://127.0.0.1:8000 and Application startup complete. This confirms your model loaded successfully.
What is fastapi dev?
The fastapi dev command starts a development server with auto-reload enabled. It watches your files for changes and restarts automatically. It listens on 127.0.0.1:8000 (localhost only).
FastAPI also auto-generates interactive API documentation from your Pydantic models. This documentation is available at the /docs endpoint and lets you test your API directly in the browser.
- Open your browser and navigate to http://127.0.0.1:8000/docs.
- Click on the POST /predict endpoint to expand it.
- Click Try it out.
- Replace the example request body with this sample patient data:
{
"age": 55,
"bmi": 28.5,
"systolic_bp": 145,
"diastolic_bp": 92,
"heart_rate": 78,
"exercise_hours_per_week": 2.0
}
- Click Execute.
You should see a 200 response with a JSON body containing risk_category, confidence, and recommendation fields. The patient above has high blood pressure readings, so you should see a Stage 2 or Stage 1 classification.
Seeing a 500 Internal Server Error?
This usually means the model file failed to load at startup. Check that model/bp_model.joblib exists in your project directory. If it's missing, run python train_model.py again from the previous step.
Also verify that the feature order in main.py matches the training order: age, bmi, systolic_bp, diastolic_bp, heart_rate, exercise_hours_per_week.
help me debug my FastAPI 500 error
Your ML model is now live behind a real API. Next up, you'll stress-test it with different patient profiles and explore how the model's confidence scores work across edge cases.
Test and Validate Your API
Your API is live and you've confirmed a single prediction works. A single successful request proves the wiring is correct, but it doesn't prove the model handles the full range of real-world inputs.
In this step, you'll stress-test the API by sending diverse patient profiles through the Swagger UI. You'll verify the model classifies healthy patients, high-risk patients, and invalid data correctly.
In this step, get ready to:
- Test the /predict endpoint with patient profiles spanning all four risk categories.
- Confirm Pydantic validation rejects invalid input with 422 errors.
- Use Claude Code to explore the model's confidence scores across all categories.
Test the /predict endpoint with diverse patient profiles
A well-tested API should handle the full spectrum of inputs. You'll submit three types of requests: a clearly healthy patient, a clearly high-risk patient, and deliberately invalid data.
Each test case targets a different behavior of the system. The healthy profile should return Normal, the high-risk profile should return Stage 2, and the invalid data should trigger a validation error before it ever reaches the model.
- Switch back to the Swagger UI at http://127.0.0.1:8000/docs in your browser.
- Click the POST /predict endpoint to expand it.
- Click Try it out to enable the request editor.
First, test with a healthy young person who exercises regularly. This profile has blood pressure well below the 120/80 threshold, so the model should classify it as Normal.
- Replace the request body with this JSON and click Execute:
{
"age": 25,
"bmi": 22.0,
"systolic_bp": 110,
"diastolic_bp": 70,
"heart_rate": 68,
"exercise_hours_per_week": 5.0
}
What should I see?
The response should show a risk_category of Normal with a high confidence score (close to 1.0). The recommendation should suggest maintaining a healthy lifestyle.
Next, test with an elderly patient who has high blood pressure readings and limited exercise. With systolic above 140, this should trigger a Stage 2 classification.
- Replace the request body with this JSON and click Execute:
{
"age": 72,
"bmi": 32.0,
"systolic_bp": 165,
"diastolic_bp": 95,
"heart_rate": 88,
"exercise_hours_per_week": 1.0
}
What should I see?
The response should show Hypertension Stage 2 as the risk category. The recommendation should advise seeking medical attention promptly.
Finally, test what happens when someone sends data that violates your Pydantic validation rules. Your PatientData schema requires age to be between 1 and 120.
- Replace the request body with this JSON and click Execute:
{
"age": -5,
"bmi": 22.0,
"systolic_bp": 110,
"diastolic_bp": 70,
"heart_rate": 68,
"exercise_hours_per_week": 5.0
}
Why does this matter?
The Swagger UI should display a 422 status code with a detailed error message explaining that age must be greater than or equal to 1. This is Pydantic's automatic validation at work.
The invalid request never reaches your model. FastAPI catches it at the schema layer and returns a structured error response. This protects your ML model from nonsensical inputs.
Not seeing the expected risk categories?
If the healthy profile returns something other than Normal, double-check the systolic_bp and diastolic_bp values. Both must be below the clinical thresholds (systolic < 120 and diastolic < 80) for a Normal classification.
If you see a 500 error instead of a response, the model file may not have loaded. Check the terminal running your server for error messages.
Help me debug my /predict endpoint responses.
Use Claude Code to explore prediction confidence
When your model returns a classification like "Hypertension Stage 2", it also calculates a confidence score. This score comes from the predict_proba method, which shows the model's probability estimate for each category.
Understanding these probabilities reveals how certain the model is about its predictions. A confidence of 0.95 means the model is very sure, while 0.55 means the classification is borderline.
- Return to your Claude Code session in the terminal.
- Ask Claude Code to explain how the model's confidence scoring works by typing this prompt:
Explain how predict_proba works in our /predict endpoint. Show me an example of what the probability array looks like for a borderline case where systolic_bp is around 130.
What does predict_proba tell you?
Claude Code will explain that predict_proba returns an array with one probability per class. For example, a patient with borderline readings might get probabilities like [0.05, 0.15, 0.60, 0.20].
Each number represents the model's estimated likelihood for that category (Normal, Elevated, Stage 1, Stage 2). The highest probability becomes the predicted class, and its value becomes the confidence score returned in your API response.
- Ask Claude Code a follow-up question to see how feature importance affects predictions:
Which features have the most influence on the model's predictions? Show me the feature importances from our trained Random Forest model.
Why feature importance matters
Claude Code will show that systolic_bp and diastolic_bp dominate the model's decision-making. This makes sense because the classification thresholds are defined directly by blood pressure values.
Features like age and bmi have moderate importance because they influence blood pressure in the synthetic data generation. Exercise hours has a smaller but meaningful impact.
Claude Code not responding?
If your Claude Code session has timed out, run claude again in your project directory to start a new session. Claude Code will re-read your CLAUDE.md and pick up context from your existing files.
Help me get Claude Code working again in my project.
Secret mission
Add a Prediction History Endpoint
Your API can classify risk, but what about tracking a patient over time? Real health monitoring means seeing trends. Add a history endpoint that stores predictions per patient and calculates whether their risk level is improving, stable, or worsening.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely on your local machine. There are no cloud resources or ongoing costs to worry about.
Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- bp-monitor-api project folder (contains all Python files, trained model, and virtual environment).
- FastAPI development server (running process in your terminal).
- Claude Code (installed via the native PowerShell installer).
- Python virtual environment (venv folder inside your project).
Keep everything running
No action needed. Choose this if you're still actively building or experimenting with your API.
- Your FastAPI dev server continues running in your terminal. You can stop and restart it anytime with fastapi dev main.py.
- Claude Code remains installed and ready for your next project.
- Your trained model stays saved at model/bp_model.joblib and loads automatically when the server starts.
Pause - I'll come back to this later
Stop the running server to free up your terminal, but keep all files for later.
- In the terminal where your FastAPI server is running, press Ctrl+C to stop the server.
- Deactivate your virtual environment by running:
deactivate
- Your project files remain on your Desktop. When you're ready to resume, navigate back to the bp-monitor-api folder, reactivate the virtual environment with venv\Scripts\activate, and run fastapi dev main.py.
Delete - I don't want to use this again
Remove all project files and optionally uninstall Claude Code to start completely fresh.
- In the terminal where your FastAPI server is running, press Ctrl+C to stop the server.
- Deactivate your virtual environment by running:
deactivate
- Delete the entire project folder by running:
cd ~/Desktop
Remove-Item -Path "bp-monitor-api" -Recurse -Force
- To uninstall Claude Code, run:
Remove-Item -Path "$env:USERPROFILE\.local\bin\claude.exe" -Force
Remove-Item -Path "$env:USERPROFILE\.local\share\claude" -Recurse -Force
- To also remove Claude Code's configuration files and session history, run:
Remove-Item -Path "$env:USERPROFILE\.claude" -Recurse -Force
Remove-Item -Path "$env:USERPROFILE\.claude.json" -Force
- Confirm the project folder is gone by running:
ls ~/Desktop
- You should no longer see bp-monitor-api in the output.
Nice Work!
Nice Work!
Nice work! You just built an ML-powered health API from scratch using AI-assisted development.
You've learned how to:
- Train a Random Forest classifier on synthetic patient data to categorize blood pressure readings into 4 hypertension risk levels.
- Build a FastAPI REST API with automatic Pydantic validation, auto-generated interactive documentation, and a health check endpoint.
- Use Claude Code as an AI pair-programmer to accelerate the entire development workflow, from model training to API deployment.
- Added prediction history tracking with trend analysis for patient monitoring.
Ready to quiz yourself?