cd /news/machine-learning/i-built-my-first-machine-learning-ap… · home topics machine-learning article
[ARTICLE · art-96071] src=dev.to ↗ pub= topic=machine-learning verified=true sentiment=↑ positive

I Built My First Machine Learning API — Here's Everything I Learned

A developer built a customer churn prediction API using the Telco Customer Churn dataset, training three classifiers and serving the best model via FastAPI. The project packages the entire preprocessing and modeling pipeline into reusable joblib artifacts, enabling any application to request churn predictions over the web.

read6 min views1 publishedAug 13, 2026

Predicts whether a telecom customer will churn using the Telco Customer Churn dataset. The project covers data cleaning and EDA, comparison of three classifiers, and a FastAPI service that serves the final model.

.
├── API/
│   └── main.py                                 # FastAPI app serving the model
├── Data/
│   └── WA_Fn-UseC_-Telco-Customer-Churn.csv    # raw dataset (7,043 customers)
├── Models/
│   ├── ml_pipeline.joblib                      # fitted preprocessing + classifier pipeline
│   └── target_labels.joblib                    # LabelEncoder for the churn target
├── Notebooks/
│   └── customer_churn_prediction.ipynb         # data cleaning, EDA, model training
├── cpp/                                        # (Python virtual environment)
├── .env                                        # environment variables (not committed)
├── .gitignore
├── requirements.txt
└── README.md

main.py

loads ../models/ml_pipeline.joblib

and ../models/target_labels.joblib

, so it must be run from inside API/

for those relative paths to resolve.

The raw data is the Telco Customer Churn dataset 7,043 rows and 21 columns covering customer demographics, account…

If you've ever trained a model in a Jupyter notebook and then wondered "okay... now what?" — this post is for you. I recently built a Customer Churn Prediction API, and I want to walk you through it the way I wish someone had walked me through my first one: no jargon dump, just what each piece does and why it's there.

Think of it like a Safaricom customer care team trying to figure out which subscribers are about to switch to Airtel — before they actually leave. That's churn prediction. And instead of leaving that insight trapped inside a notebook, I turned it into an API anyone (or any app) can call.

I started, like most people do, in a Jupyter notebook. I had a dataset of telecom customers — their contract type, monthly charges, whether they had streaming services, and so on — with a label saying whether they churned (left) or not.

I cleaned the data, explored it, trained a few models, and picked a winner. Cool. But here's the thing: a trained model sitting in a notebook is useless to anyone else. A frontend developer can't "import your notebook" into an app. A business analyst can't click a button in your .ipynb

file to get a prediction.

That's the gap an API fills.

Before any API talk, the notebook had to do its job. Here's the flow, in plain English:

gender

, tenure

, Contract

, MonthlyCharges

, and the target column, Churn

(Yes/No).TotalCharges

becomes totalcharges

), and a numeric column that was secretly stored as text gets converted properly. A handful of broken rows get dropped.tenure

and MonthlyCharges

) get scaled, and categorical columns (like Contract

or PaymentMethod

) get one-hot encoded, because models only understand numbers.joblib

, I saved the entire fitted pipeline (preprocessing + model, bundled together) plus the label encoder, so predictions later come back as "Yes"/"No" instead of 1

/0

.That last step is the bridge to everything that follows. Once your pipeline is saved to disk, it stops being "notebook code" and becomes a reusable artifact — a file any Python program can load and use.

An API is just a way for two programs to talk to each other over the web, using a set of agreed-upon rules. You send it something, it sends something back.

For this project, the deal is simple:

"Send me a customer's details, and I'll tell you whether they're likely to churn."

That's it. No magic. Just a structured request in, a structured response out.

I used FastAPI, a Python framework built specifically for creating APIs quickly, with automatic validation and interactive documentation baked in.

Here's the skeleton of what's happening in main.py

:

import joblib
from fastapi import FastAPI
from pydantic import BaseModel
import pandas as pd

labels = joblib.load('../models/target_labels.joblib')
model = joblib.load('../models/ml_pipeline.joblib')

app = FastAPI(
    title="Customer Churn Prediction API",
    description="An API for predicting customer churn using a pre-trained machine learning model."
)

Two things happen right at the top, before the API even starts handling requests:

ml_pipeline.joblib

) is loaded into memory. This is the exact same preprocessing + model combo we saved from the notebook.target_labels.joblib

) is loaded too, so we can turn a 0

/1

prediction back into a human-readable "No"/"Yes".This only happens once, when the server starts — not on every request. That's important for speed.

This is the part that impressed me most as a beginner. FastAPI uses something called Pydantic to define exactly what shape your input data must be:

class InputData(BaseModel):
    gender: str
    seniorcitizen: int
    partner: str
    dependents: str
    tenure: int
    phoneservice: str
    multiplelines: str
    internetservice: str
    onlinesecurity: str
    onlinebackup: str
    deviceprotection: str
    techsupport: str
    streamingtv: str
    streamingmovies: str
    contract: str
    paperlessbilling: str
    paymentmethod: str
    monthlycharges: float
    totalcharges: float

Every field here mirrors a column the model was trained on. If someone sends tenure

as "twelve"

instead of 12

, FastAPI rejects the request automatically — before it ever reaches your model. You don't write a single line of manual validation code. That alone saved me from a bunch of silent bugs.

An API exposes "endpoints" — specific URLs that do specific things. This project has two.

A health check, so you (or a monitoring tool) can confirm the API is alive:

@app.get("/health", tags=["Health Check"])
def health_check():
    return {"status": "API is running"}

The actual prediction endpoint:

@app.post("/predict", tags=["Prediction"])
def predict_churn(payload: InputData):
    input_df = pd.DataFrame([payload.model_dump()])

    prediction = model.predict(input_df)

    decoded_prediction = labels.inverse_transform(prediction)

    return {"prediction": decoded_prediction[0]}

Walking through it:

payload

.payload.model_dump()

turns it into a plain dictionary, which gets wrapped into a one-row pandas DataFrame — because that's the format the trained pipeline expects.model.predict(...)

runs that row through the 0

or 1

), so labels.inverse_transform(...)

converts it back into "No"

or "Yes"

.That's the whole loop: JSON in → DataFrame → pipeline → prediction → JSON out.

Once you run:

uvicorn main:app --reload

FastAPI gives you a free interactive docs page at http://127.0.0.1:8000/docs

, where you can literally fill in a form and hit "Execute" to test /predict

without writing a single line of client code. As a beginner, this is where it finally clicked for me — seeing my model respond to a request in real time made it feel like a real product, not just a school notebook exercise.

Send it something like:

{
  "gender": "Female",
  "seniorcitizen": 0,
  "partner": "Yes",
  "dependents": "No",
  "tenure": 1,
  "phoneservice": "No",
  "multiplelines": "No phone service",
  "internetservice": "DSL",
  "onlinesecurity": "No",
  "onlinebackup": "Yes",
  "deviceprotection": "No",
  "techsupport": "No",
  "streamingtv": "No",
  "streamingmovies": "No",
  "contract": "Month-to-month",
  "paperlessbilling": "Yes",
  "paymentmethod": "Electronic check",
  "monthlycharges": 29.85,
  "totalcharges": 29.85
}

And you get back:

{"prediction": "No"}

Training a model answers the question "does this work?" Building an API answers a completely different question: "can anyone else use this?"

Those are two separate skills, and honestly, the second one felt more like real software engineering — dependency management, input validation, documentation, deployment thinking — than the modeling part did. If you've been putting off learning how to serve your models, don't. It's a smaller leap than it looks, and FastAPI in particular makes it a genuinely beginner-friendly one.

If you're working through something similar — maybe predicting M-Pesa transaction fraud, or matatu route demand — the pattern is identical: train, save the pipeline, load it in an API, validate input, predict, respond. Once you've done it once, you'll do it in your sleep.

Have you built your first ML API yet? What tripped you up the most — validation, deployment, or something else entirely? Let me know in the comments.

── more in #machine-learning 4 stories · sorted by recency
── more on @fastapi 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-built-my-first-mac…] indexed:0 read:6min 2026-08-13 ·