TUTORIAL

How to Get Football Data with C# (.NET) - Complete API Tutorial

Step-by-step C# and .NET tutorial for fetching football fixtures, match stats and odds via API using HttpClient. Working code examples you can copy.

4 min read

C# and .NET are a strong choice for football data backends, ASP.NET APIs, and desktop or Unity apps. TheStatsAPI is a plain REST JSON API, so the built-in HttpClient and System.Text.Json are all you need - no NuGet package required.

This tutorial shows how to fetch football data with C#: competitions, fixtures, pagination, match stats, and odds.

Prerequisites

You need:

  • .NET 6+ (the examples use top-level statements and async/await)
  • A TheStatsAPI API key

Set your API key as an environment variable:

export THESTATSAPI_KEY="your_api_key"

If you do not have a key yet, sign up at thestatsapi.com for a 7-day free trial.

Create a Small API Client

Create FootballApi.cs. Note the trailing slash on BaseAddress and that endpoints are passed without a leading slash, so relative paths combine correctly.

using System.Net.Http.Headers;
using System.Text.Json;

public class FootballApi
{
    private readonly HttpClient _client = new()
    {
        BaseAddress = new Uri("https://api.thestatsapi.com/api/")
    };

    public FootballApi(string apiKey)
    {
        _client.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", apiKey);
        _client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
    }

    public async Task<JsonElement> GetAsync(string endpoint)
    {
        using var response = await _client.GetAsync(endpoint);
        response.EnsureSuccessStatusCode();

        await using var stream = await response.Content.ReadAsStreamAsync();
        using var doc = await JsonDocument.ParseAsync(stream);
        return doc.RootElement.Clone();
    }
}

Cloning the root element lets you return it safely after the JsonDocument is disposed. Every example below reuses GetAsync.

Fetch Competitions

using System.Text.Json;

var api = new FootballApi(Environment.GetEnvironmentVariable("THESTATSAPI_KEY")!);

var result = await api.GetAsync("football/competitions?search=Premier%20League");

foreach (var competition in result.GetProperty("data").EnumerateArray())
{
    Console.WriteLine($"{competition.GetProperty("id").GetString()} " +
                      $"{competition.GetProperty("name").GetString()} " +
                      $"{competition.GetProperty("country").GetString()}");
}

Typical output:

comp_3039 Premier League England

Save the competition ID for match and season queries. Use Uri.EscapeDataString to encode search terms with spaces.

Get Fixtures for a League

using System.Text.Json;

var result = await api.GetAsync("football/matches?competition_id=comp_3039&per_page=10");

foreach (var match in result.GetProperty("data").EnumerateArray())
{
    var home = match.GetProperty("home_team").GetProperty("name").GetString();
    var away = match.GetProperty("away_team").GetProperty("name").GetString();
    var status = match.GetProperty("status").GetString();
    var date = match.GetProperty("utc_date").GetString();
    Console.WriteLine($"{date} - {home} vs {away} ({status})");
}

Add date_from, date_to, season_id, team_id, and status to filter. See the fixtures API.

Handle Pagination

List endpoints return a meta block with total_pages.

using System.Text.Json;

async Task<List<JsonElement>> GetAllMatches(string query)
{
    var rows = new List<JsonElement>();
    var page = 1;

    while (true)
    {
        var result = await api.GetAsync($"football/matches?{query}&page={page}");

        foreach (var match in result.GetProperty("data").EnumerateArray())
            rows.Add(match.Clone());

        var totalPages = result.TryGetProperty("meta", out var meta)
            && meta.TryGetProperty("total_pages", out var tp)
                ? tp.GetInt32()
                : 1;

        if (page >= totalPages) break;
        page++;
    }

    return rows;
}

var matches = await GetAllMatches("competition_id=comp_3039");
Console.WriteLine($"Fetched {matches.Count} matches");

Run backfills in a hosted service or background worker and cache rows in your database.

Get Match Stats

using System.Text.Json;

var data = (await api.GetAsync("football/matches/mt_010249745/stats")).GetProperty("data");
var overview = data.GetProperty("overview");

var xg = overview.GetProperty("expected_goals").GetProperty("all");
var shots = overview.GetProperty("total_shots").GetProperty("all");

Console.WriteLine($"Shots: {shots.GetProperty("home")} - {shots.GetProperty("away")}");
Console.WriteLine($"xG: {xg.GetProperty("home")} - {xg.GetProperty("away")}");

See the match stats API and xG API.

Get Pre-Match Odds

using System.Text.Json;

var data = (await api.GetAsync("football/matches/mt_010249745/odds")).GetProperty("data");

foreach (var book in data.GetProperty("bookmakers").EnumerateArray())
{
    if (book.GetProperty("markets").TryGetProperty("match_odds", out var matchOdds))
    {
        Console.WriteLine($"{book.GetProperty("bookmaker").GetString()}: {matchOdds}");
    }
}

Use /football/matches/{match_id}/odds/live for in-play odds. See the Football Odds API.

Production Tips

  • Keep the API key in environment variables, user secrets, or a key vault.
  • Register HttpClient with IHttpClientFactory in ASP.NET instead of creating new instances per request.
  • Cache responses (IMemoryCache or a distributed cache) to respect plan limits.
  • Use pagination for backfills inside hosted/background services.
  • Check availability flags such as xg_available and live_odds_available.

FAQ

Can I use C# with TheStatsAPI?

Yes. TheStatsAPI is a REST JSON API, so .NET's built-in HttpClient and System.Text.Json work directly. The examples above need no external packages.

Do I need a .NET SDK package?

No. The built-in libraries are enough. You can use System.Net.Http.Json helpers or Newtonsoft.Json if you prefer strongly typed models.

How do I fetch football odds in C#?

Use /football/matches/{match_id}/odds for pre-match odds and /football/matches/{match_id}/odds/live for live odds where available.

How do I get xG data in C#?

Call /football/matches/{match_id}/stats and read overview.expected_goals.all for team xG.

Start building today

Ready to Power Your Sports App?

Start your 7-day free trial. All endpoints included on every plan.

Cancel anytime
7-day free trial
Setup in 5 minutes