Football Prediction Neural Network in Python: Where to Get Data, How to Train It, and How to Test Accuracy
Step by step: what data a football prediction model needs, where to get it with SportAPI, how to train a neural network in Python, and how to test it honestly against the market.

A football prediction neural network is less about network architecture and more about data and honest testing. The model needs three things: historical results to learn from, upcoming matches to predict, and odds as a benchmark for accuracy. SportAPI covers all three: the Results API returns final scores with 20 years of history and match statistics, and the line API returns upcoming matches and odds from $30 a month per sport. The rest is Python with pandas and scikit-learn.
First things first: no model guarantees winnings. A good model differs from a bad one not by bold promises but by producing probabilities that are more accurate than the market’s on data it has never seen. Here is how to get there step by step.
What data a prediction model needs
| Data | Why | Where to get it in SportAPI |
|---|---|---|
| Historical results: date, teams, score | Training the model | Results API, 20 years of history |
| Match statistics: shots, corners, cards | Extra features | Results API, extra stats |
| Upcoming matches | Daily predictions | Match calendar in the line API |
| Odds | A feature and a benchmark | Line API, prematch |
One honest caveat: SportAPI does not provide historical odds. Start saving odds from the first day of your subscription — in a few months you will have your own history to test the model against the market.
Step 1. Collect the data in one table
The model learns from a single table where one row is one match. The minimum set of columns:
date, home, away, home_goals, away_goals, odds_home, odds_draw, odds_away
Load past results from the Results API and start saving odds every day. This script takes today’s matches from the line API calendar once a day and appends 1 / X / 2 odds to a file:
import csv
from datetime import date
import requests
API = "https://YOUR_API_DOMAIN/v1"
HEADERS = {"Package": "YOUR_SPORTAPI_KEY"}
url = f"{API}/events/1/0/sub/50/line/0/0/en"
response = requests.get(url, headers=HEADERS, timeout=10)
response.raise_for_status()
with open("odds_history.csv", "a", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
for tournament in response.json()["body"]:
for match in tournament["events_list"]:
odds = {
outcome["oc_name"]: outcome["oc_rate"]
for group in match.get("game_oc_list", []) if group["group_name"] == "1x2"
for outcome in group["oc_list"]
}
writer.writerow([
date.today(), match["game_id"], match["game_start"],
tournament["tournament_name"], match["opp_1_name"], match["opp_2_name"],
odds.get("W1"), odds.get("X"), odds.get("W2"),
])
The request /events/1/0/sub/50/line/0/0/en returns every football match of the day from the match calendar. Run the script daily and your odds history builds itself.
Step 2. Prepare features
A network does not understand team names — it needs numbers. Simple, useful features:
- form: average goals scored and conceded in the last 5 matches;
- home or away: most teams perform noticeably differently at home and away;
- market probabilities: odds converted to probabilities without the bookmaker margin;
- statistics: average shots and corners in recent matches — if you load extra stats.
The key rule: features may only use what was known before kick-off. If form includes the match itself, the model “peeks” at the answer and shows fantastic accuracy in testing that never appears in real life.
Step 3. Train the neural network
Tabular data does not need a complex network: start with a small multilayer perceptron from scikit-learn. The code below computes team form without peeking, converts odds to probabilities, and trains on the first 80% of matches in time order:
import numpy as np
import pandas as pd
from sklearn.metrics import log_loss
from sklearn.neural_network import MLPClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
df = pd.read_csv("matches.csv", parse_dates=["date"]).sort_values("date").reset_index(drop=True)
df["match_id"] = df.index
df["result"] = np.select(
[df.home_goals > df.away_goals, df.home_goals < df.away_goals], ["H", "A"], default="D"
)
# Form: average goals in the team's previous 5 matches, excluding the current one
columns = ["match_id", "date", "team", "scored", "conceded"]
home = df[["match_id", "date", "home", "home_goals", "away_goals"]].set_axis(columns, axis=1)
away = df[["match_id", "date", "away", "away_goals", "home_goals"]].set_axis(columns, axis=1)
games = pd.concat([home.assign(side="home"), away.assign(side="away")]).sort_values(["date", "match_id"])
for col in ["scored", "conceded"]:
games[f"{col}_form"] = games.groupby("team")[col].transform(
lambda s: s.shift().rolling(5, min_periods=3).mean()
)
form = games.pivot(index="match_id", columns="side", values=["scored_form", "conceded_form"])
form.columns = [f"{side}_{name}" for name, side in form.columns]
df = df.join(form, on="match_id").dropna()
# Market probabilities without the margin
inverse = 1 / df[["odds_home", "odds_draw", "odds_away"]]
market = inverse.div(inverse.sum(axis=1), axis=0)
market.columns = ["p_home", "p_draw", "p_away"]
X = pd.concat([df[form.columns], market], axis=1)
y = df["result"]
split = int(len(df) * 0.8)
model = make_pipeline(
StandardScaler(),
MLPClassifier(hidden_layer_sizes=(32, 16), early_stopping=True, max_iter=500, random_state=42),
)
model.fit(X[:split], y[:split])
model_proba = model.predict_proba(X[split:])
market_proba = market[split:][["p_away", "p_draw", "p_home"]].to_numpy()
print("Neural network, log loss:", log_loss(y[split:], model_proba, labels=model.classes_))
print("Market, log loss:", log_loss(y[split:], market_proba, labels=["A", "D", "H"]))
The model returns probabilities for three outcomes: H — home win, D — draw, A — away win. That is more useful than “right or wrong”: a probability can be compared with the odds.
Step 4. Test accuracy honestly
Most “accurate” models on the internet fail right here.
- Split data by time, not randomly. The model trains on the past and is tested on the future — as in real life. Random shuffling inflates accuracy.
- Do not look only at the share of correct picks. A model that always picks the home team gets almost half of matches right and is still useless.
- Use log loss and the Brier score. They measure the quality of probabilities: lower is better.
- Compare with the market. Odds are the strongest free forecast. If the model’s log loss is not lower than the market’s, the model knows less about the matches than the line does.
- Check calibration. Of the matches where the model says 60%, about 60% should actually happen.
Only then does it make sense to simulate bets on historical data — with flat stakes and the margin taken into account. The result of such a test is an estimate, not a promise of future profit.
Step 5. Daily predictions on real matches
Once the model passes testing, the work becomes a daily routine:
- In the morning, a script takes the day’s matches from the line API calendar.
- It computes features from recent results and current odds.
- The model outputs probabilities and stores the predictions.
- After the matches, final scores come from the Sports Results API and accuracy statistics update automatically.
You can publish the predictions on a Telegram channel — we covered auto-posting in our article on a sports channel on autopilot.
Mistakes that ruin a prediction model
- Leaking future data. Form, ratings, or statistics calculated including the match itself.
- Random train-test split. The model sees future matches during training.
- Overfitting. A large network on a few thousand matches memorizes noise. Start small and compare with logistic regression.
- Ignoring the margin. Probabilities implied by odds add up to more than 100% — normalize them.
- Too little data. A model trained on one season of one league knows almost nothing. Use several seasons and leagues — SportAPI results history covers 20 years.
If you have no time to build the model yourself
SportAPI develops turnkey sports strategies: it formalizes your match selection rules or builds a model from scratch, connects the data, evaluates results on history, and sends signals to Telegram, email, or a webhook. And if you want to sell predictions, a predictions website with paid access can run on the same data.
FAQ
Can I build a sports prediction neural network without big data?
You can start with a few thousand matches and simple features, but accuracy will be limited. More seasons and leagues make the model more stable. The 20-year results history in SportAPI solves the volume problem.
What is better for predictions: a neural network, logistic regression, or Poisson?
On tabular data, simple models often perform as well as a neural network. The right approach is to train several models and pick the one with the best log loss on held-out matches.
Where can I get historical odds?
SportAPI does not provide historical odds. Start saving current odds from the line API every day — the script in this article does it automatically.
How do I know the model works?
Its log loss on matches it never saw during training is lower than the market’s, and its probabilities are well calibrated. The share of correct picks alone proves nothing.
Can I check the data before paying?
Yes. A 2-day trial lets you look at the line, odds, and data format and run the odds collection script.
Where to start today
- Get a SportAPI trial key and start the daily odds collection script.
- Load several seasons of results into a table.
- Compute team form without peeking and the market probabilities.
- Train a small neural network and compare its log loss with the market.
- If the model beats the market in testing, start daily predictions.
Message the SportAPI manager on Telegram with the leagues and sports your model needs — you will get trial access to the Sports Odds API and can collect your first data today.