Includes & Filters
Contents

Includes / Expansions

What are includes?

Includes (also known as expansions) let clients get related resources in one API call, avoiding extra round-trips. You control what the payload is made up of by specifying which relationships to load.

Example:

GET /v3/football/fixtures/{fixture_id}?api_token=…&include=localTeam,visitorTeam,events

This enriches the main fixture object with full details about both teams and match events, right off the bat.

Syntax & nested includes

Sportmonks uses a simple comma- or semicolon-separated list. You can also use nested includes to dive deeper into relationships. Example:

GET /v3/football/fixtures{fixture_id}?api_token={api_token}&include=events.type

This brings back match events plus the event-type objects (e.g., goal, yellow card) for each event. You can further customise nested includes:

&include=events.player.country

…returns each event’s player, and each player’s country. 

Field selection within includes

To reduce payload size, selects can be applied to includes. Example:

GET /v3/football/fixtures/{fixture_id}?api_token=…&include=events:player_name,minute

This returns only the player_name and minute fields from the included events. 

You can also chain filters with selects:

GET /v3/football/fixtures/{fixture_id}?api_token={api_token}&include=events:player_name,related_player_name,minute;events.type&filters=eventTypes:18,14

This fetches only goal and substitution events, with a minimal set of fields plus event type details.

Benefits & design takeaways

Performance: Bundling multiple resources (e.g. teams, events) eliminates extra calls and latency.
Bandwidth control: Precise field selection keeps payload sizes lean.
Clarity: API consumers decide exactly what to include and what to omit.
Nestable: Supports multi-level relationships (e.g., events → player → country).

Best practices

  1. Whitelist allowed includes: Avoid exposing relationships that could harm performance or internal structure.
  2. Limit include depth: Prevent excessive joins by capping nested includes (e.g., max depth 3).
  3. Document clearly: Detail ideal use cases and available relationships for each endpoint.
  4. Combine with filters/selects: Use both to fine-tune payloads, e.g., “events only with goals” plus only essential fields.
  5. Monitor payload size: Automatically reject requests where payload is too large or complex.

Filters

Filters empower clients to narrow results based on criteria, leading to more efficient and relevant responses. Sportmonks’ API supports both static and dynamic filters, which can also apply to included data.

Filter types

– Static filters are built-in, endpoint-specific filters that perform predefined functions. Examples on the /fixtures endpoint include: todayDate, venues, participantSearch, leagues, and fixtureStates
Dynamic filters consist of an entity-based clause that filters included (or base) resources by ID. Format: filters=eventTypes:ID1,ID2, statisticTypes:42,49, fixtureLeagues:384,501, etc.

Syntax: Dynamic filters

?filters=<entityToFilter>:<commaSeparatedIDs>[;<anotherFilter>:<IDs>…]

– Example: events, statistics, lineupdetailTypes, fixtureLeagues, coachCountries, and more.

Filter on includes

By combining includes and dynamic filters, you can enrich and narrow your data in a single request.

Example 1: Fetch only goal events for a fixture

GET /v3/football/fixtures/{fixture_id}?api_token=YOUR_TOKEN&include=events&filters=eventTypes:18

Returns only match events of type 18 (goals) 

Example 2: Filter multiple include types

GET /v3/football/fixtures/{fixture_id}?api_token=YOUR_TOKEN&include=events;statistics&filters=eventTypes:18,14;statisticTypes:42,49

– Fetches goal and substitution events (types 18, 14)
– Appends statistics only of types 42 & 49.

Example 3: Filter teams or leagues across resources

GET /v3/football/fixtures?api_token=YOUR_TOKEN&include=localTeam,visitorTeam&filters=fixtureLeagues:384

Only retrieves fixtures within Serie A (league ID 384), including team details.

Combining filters with field selection & nested includes

You can mix filters, select, and nested includes:

GET /v3/football/fixtures/{fixture_id}?api_token=YOUR_TOKEN&include=events:player_name,minute;events.type&filters=eventTypes:18,14

– Returns only goal or substitution events
– Only essential fields: player_name, minute
– Includes type objects for each event

Filtering workflows & best practices

  1. Optimise data transfer: Ensure only needed IDs are used (e.g., only “goals”) to reduce payload size.
  2. Whitelist filters: Allow only supported entity filters (avoid exposing internal data).
  3. Maximise filter depth: Clearly define which includes support which filters in docs.
  4. Combine filters smartly: Use filtering on both base entities (e.g., leagues, venues) and includes for precision.
  5. Test payload complexity: Avoid overly complex filters that could strain performance.

Implementation strategies & patterns

In this section, we’ll examine how to build robust API support for includes and filters, covering parsing logic, security, performance, and real-world implementation examples.

Parsing & validating query parameters

Parameter parsing

– Split include by commas/semicolons and support nested keys like events.type.
– Split filters by semicolons, parse <entity>:id1,id2, validating both entity and IDs.
– Use a reusable routine/framework to convert these into internal query clauses.

Validation & whitelisting

– Only allow includes and filters tied to predefined relationships or entities, rejecting unknown ones (400 Bad Request).
– Enforce nested include depth (e.g., max 3 levels) to prevent performance issues.

Mapping to database queries

– Join includes by constructing ORM or SQL joins. For example, include=events.type triggers an SQL LEFT JOIN events and event_types.
– Apply filters post-join so events are restricted to specific IDs (e.g., WHERE event_types.id IN (18,14)).
– Paginate and sort base entity separately to avoid over-fetching included data.

Sportmonks-inspired example

SportmonksFootballApi::fixture()

  ->setInclude('events;statistics.type')

  ->setFilter('eventTypes:18,14;fixtureStatisticTypes:45')

  ->byId(fixture_id);

This builds includes and filters across multiple relationships in one fluent call 

Security & injection protection

– Sanitise IDs strictly as integers or using regex.
– Prepare SQL statements or ORM builder chains to avoid injection risks.
– Reject invalid or duplicate parameter names, returning 400 Bad Request for unexpected or malformed input.

Performance optimisation

  1. Index frequently-filtered fields, like event_type_id, to avoid slow table scans.
  2. Limit join depth to prevent performance degradation.
  3. Cache heavy responses, especially static or historical queries. We recommend client-side caching of unchanged entities (teams, stats).
  4. Use pagination even on single-resource endpoints, to prevent huge included lists from overloading the system.

Error handling & UX feedback

– Return clear validation errors, e.g., “Invalid include: teamPlayers.stats – max depth 2.”
– Validate filter IDs: return “Filter IDs not found” if filtering on non-existent values.
– Align with RFC 8040 rules: reject duplicate query parameters or conflicting request formats.

Implementation example (Node.js + Express pseudo-code)

app.get('/fixtures/:id', async (req, res) => {

  const { include, filters } = req.query;

  const includes = parseInclude(include); // e.g. ['events', 'events.type']

  const dynamicFilters = parseFilters(filters); // { eventTypes: [18,14] }




  // Validate

  if (!validateIncludes(includes) || !validateFilters(dynamicFilters)) {

    return res.status(400).json({ error: "Invalid include/filter format" });

  }




  let query = db.fixtures

    .where('id', req.params.id)

    .joinIncludes(includes)

    .applyFilters(dynamicFilters)

    .limit(1);




  const fixture = await query.first();

  res.json({ data: fixture });

});

Aligning with REST best practices

– Consistent naming & URL structure, query parameters behave like filters/indicated relationships.
– Support sorting and pagination out-of-the-box, even if only one record is returned.
– Provide clean, full documentation (like Sportmonks’ Request options page with demos) to improve developer adoption.

Sportmonks: A real‑world case study in optimising API data requests

Sportmonks’ Football API offers a polished, developer-friendly implementation of includes (expansions) and filters, making it a textbook example of efficient data fetching.

Purposeful includes for rich responses

Sportmonks treats includes as a core feature, available across endpoints to request related entities in one shot:

Standard includes: Fetch fixtures with associated events, participants, scores, lineups, etc.
(&include=events,participants)
Nested includes: Dive deeper, e.g., events.player.country to pull player and country data alongside events.
(&include=events.player)

This flexibility helps developers to design requests precisely aligned with their data needs.

Field selections for lean payloads

With the syntax include=events:player_name,minute, SportMonks lets you pick only essential fields:

GET …/fixtures/{id}?include=events:player_name,minute

Granular filters on includes and base data

Sportmonks supports two key filter types:

– Static filters (e.g., filtering fixtures by league or date)
– Dynamic entity filters (e.g., filters=eventTypes:18, fetching only goal events).

GET …/fixtures/{id}?include=events&filters=eventTypes:18

Fine-tune your data fetching with Sportmonks

Stop overloading your app with unnecessary data. Sportmonks’ Football API gives you full control with Includes and Filters, letting you expand related data like events, players, or stats in one efficient call, and trim your payload to exactly what you need. Whether you’re building live score apps, stat dashboards, or betting tools, this means faster responses and leaner integrations.

Make your API requests smarter, faster, and cleaner with Sportmonks. Start optimising today and deliver the right data, the right way.

Faqs about includes & filters

What’s the difference between static and dynamic filters?
Static filters are endpoint-specific and always behave the same (e.g. filtering fixtures by todayDate, venues, or leagues). Dynamic filters let you filter based on entity IDs, such as eventTypes:18 to retrieve only goals, which can be used on both base entities and includes.
How do nested includes work?
Sportmonks supports nested includes via dot notation. For example:
?include=events.player.country
This returns events and enriches them with each player’s country details.
Why should I use includes & filters together?
Using both lets you fetch precisely the data you need: - Includes: Load related data (e.g. events, teams). - Filters: Limit results (e.g. only goals). - Field selectors: Strip any unnecessary details. The result? Lean, performant API responses with minimal bandwidth usage.

Written by David Jaja

David Jaja is a technical content manager at Sportmonks, where he makes complex football data easier to understand for developers and businesses. With a background in frontend development and technical writing, he helps bridge the gap between technology and sports data. Through clear, insightful content, he ensures Sportmonks' APIs are accessible and easy to use, empowering developers to build standout football applications