Skip to main content

Data Model Architecture

Overview

The Events Content Calendar API uses a hierarchical data model that reflects the real-world structure of sports events. Understanding this model is essential for effective API usage and query optimization.


Core Entities

Event → Edition → Race → Activity

The data model follows a four-level hierarchy:

Event (Marathon Series)
└── Edition (2024 Marathon)
└── Race (Full Marathon, Half Marathon)
└── Activity (Running)

This structure allows us to represent:

  • Events that occur annually (e.g., "Boston Marathon")
  • Editions for specific years (e.g., "Boston Marathon 2024")
  • Races for different distances (e.g., "Full Marathon", "Half Marathon")
  • Activities for multi-sport events (e.g., "Swim", "Bike", "Run" in a triathlon)

Entity Definitions

Event

An Event represents a recurring sports event series. It contains the base information that remains consistent across years.

Key Characteristics:

  • Unique identifier (id)
  • Localized name in 16+ languages
  • Geographic location (city, country, coordinates)
  • Primary sport classification
  • Base event information

Collapsed Data Structure: The API returns events in a "collapsed" format, where the most recent or relevant edition's data is merged into the event object for simplified querying.

Example Fields:

type Event {
id: Int
name: LocalizedString
location: LocalizedString
city: LocalizedString
country: LocalizedCountry
countryIso: String
lonlat: [Float!]
sport: eventSport
startDate: String
races: [Race!]
editionId: Int
editionStatus: editionStatus
}
tip

When querying events, you're actually getting a collapsed view that combines event metadata with current edition data. This simplifies most use cases without requiring separate edition queries.


Edition

An Edition represents a specific occurrence of an event in a given year.

Key Characteristics:

  • Links to parent Event
  • Specific year/date
  • Registration status
  • Edition-specific status (ok, canceled, postponed, etc.)

Edition Status Values:

  • ok - Event is happening as scheduled
  • canceled - Event has been canceled
  • postponed - Event date has been moved
  • turned_virtual - Event moved to virtual format
  • no_race - Edition skipped this year
  • deleted - Edition removed from catalog
note

The editionStatus field helps you filter out canceled or postponed events in your application.


Race

A Race represents a specific competition within an edition, typically differentiated by distance or category.

Key Characteristics:

  • Distance and unit information
  • Start date and time
  • Participation type (solo, relay, team)
  • Sport classification
  • List of activities

Example: A marathon edition might have multiple races:

  • Full Marathon (42.195 km)
  • Half Marathon (21.0975 km)
  • 10K Fun Run (10 km)
  • 5K Kids Run (5 km)

Important Fields:

type Race {
id: Int
raceName: String
distanceKm: Float
distanceType: distanceType
startDate: String
startTime: String
sport: sport
participant: participation
activities: [Activity!]
tags: [tags!]
}
warning

Race distances can be expressed in different units. Always check the distanceUnit field or use distanceKm for normalized kilometer values.


Activity

An Activity represents a specific discipline within a race, most relevant for multi-sport events like triathlons.

Key Characteristics:

  • Distance and elevation metrics
  • Course profile (flat, hilly, extreme)
  • Terrain types (road, trail, mixed)
  • Environmental tags
  • Course details (loops, elevation gain)

Example - Triathlon Activities:

  1. Swim - 1.5 km in open water
  2. Bike - 40 km road cycling
  3. Run - 10 km road running

Important Fields:

type Activity {
distance: Float
distanceKm: Float
distanceUnit: distanceUnit
elevationGain: Float
elevationDrop: Float
courseProfile: courseProfile
courseType: courseType
terrain: [terrain!]
environment: [environments!]
order: Int
}

Localization Model

LocalizedString

All user-facing text fields use the LocalizedString type, providing translations in 16 languages.

Supported Languages:

  • en - English
  • en_US - English (United States)
  • en_GB - English (United Kingdom)
  • es - Spanish
  • es_419 - Spanish (Latin America)
  • fr - French
  • de - German
  • it - Italian
  • pt - Portuguese
  • pt_BR - Portuguese (Brazilian)
  • nl - Dutch
  • sv - Swedish
  • ru - Russian
  • ja - Japanese
  • ko - Korean
  • zh - Chinese
  • zh_HANT - Chinese (Traditional)
  • th - Thai
  • tr - Turkish
  • id - Indonesian

Usage Example:

query {
events(page: 0, size: 10) {
data {
name {
en
es
fr
}
location {
en
}
}
}
}

Response:

{
"name": {
"en": "Berlin Marathon",
"es": "Maratón de Berlín",
"fr": "Marathon de Berlin"
},
"location": {
"en": "Berlin, Germany"
}
}
Best Practice

Request only the languages you need to minimize response payload size and improve performance.


Geographic Model

Location Data

Events include rich geographic information for location-based queries and mapping.

Geographic Fields:

FieldTypeDescription
cityLocalizedStringCity name in multiple languages
locationLocalizedStringFull location description
countryLocalizedCountryCountry object with ISO code and localized names
countryIsoStringISO 3166-1 alpha-2 country code
lonlat[Float!][longitude, latitude] coordinates
region1eventRegion1Primary administrative region
region2eventRegion2Secondary administrative region

Geo-Spatial Queries:

The API supports radius-based searches using the around filter:

query {
events(
criteria: {
around: {
point: { lat: 40.7128, lon: -74.0060 }
distance: "50km"
}
}
sort: geopoint_asc
) {
data {
name { en }
city { en }
lonlat
}
}
}
note

When using geo-spatial queries, you can sort by distance using geopoint_asc (nearest first) or geopoint_desc (farthest first).


Filtering & Search Model

Search Criteria

The searchCriteria input type provides powerful filtering options across all entity levels.

Available Filters:

CategoryFilterTypeDescription
Locationcountries[String!]Filter by country ISO codes
regions1[Region1Enum!]Filter by primary region
regions2[Region2Enum!]Filter by secondary region
cityStringFilter by city name
aroundAroundGeo-radius filter
Sportsports[SportEnum!]Filter by sport type
DatedateRangeDateRangeFilter by date range
DistancedistanceRangeDistanceRangeFilter by race distance
distanceTypes[DistanceTypeEnum!]Filter by standard distances
CoursecourseProfiles[CourseProfileEnum!]Filter by terrain difficulty
courseTypes[CourseTypeEnum!]Filter by course layout
terrains[TerrainEnum!]Filter by terrain type
Tagsenvironments[EnvironmentEnum!]Filter by environment tags
participations[ParticipationEnum!]Filter by participation type
others[OtherEnum!]Filter by other attributes
StatuseventStatusEventStatusEnumFilter by event status
editionStatusEditionStatusEnumFilter by edition status
SearchtextStringFull-text search

Enumeration Types

Sport Types

Current supported sports:

  • run - Road running
  • trail_run - Trail running
note

The sports catalog is customizable per tenant. Additional sports may be available based on your API configuration.

Distance Types

Standard distance classifications:

  • _5k - 5 kilometer events
  • _10k - 10 kilometer events
  • half_marathon - Half marathon (21.0975 km)
  • marathon - Marathon (42.195 km)
  • other - Non-standard distances

Course Profiles

Terrain difficulty classification:

  • flat - Minimal elevation change
  • undulating - Rolling hills
  • hilly - Significant elevation changes
  • extreme - Very challenging elevation

Course Types

Course layout patterns:

  • point_to_point - Start and finish at different locations
  • out_and_back - Run out and return on same route
  • single_loop - One continuous loop
  • loops - Multiple laps of the same circuit

Terrain Types

Surface characteristics:

  • road - Paved roads
  • trail - Off-road trails
  • track - Athletic tracks
  • multi_terrain - Mixed surfaces
  • indoor - Indoor venues
  • gravel - Gravel paths
  • snow - Snow-covered terrain
  • sand - Beach or sand
  • urban_trail - City trails

Environment Tags

Event atmosphere and location features:

  • scenic - Scenic views
  • city - Urban environment
  • nature - Natural setting
  • mountain - Mountain location
  • beach - Beach location
  • forest - Forest trails
  • historical - Historical sites
  • night - Night races
  • music - Music festival atmosphere
  • And many more...
tip

Combine multiple filters to find very specific events. For example: trail runs in mountains with scenic views under 50km.


Pagination Model

Page-Based Pagination

The API uses offset-based pagination with comprehensive page information.

Pagination Parameters:

  • page - Zero-indexed page number (default: 0)
  • size - Items per page (default: 20, max recommended: 100)

Pagination Response:

type Events {
data: [Event!]
totalCount: Int
pageCount: Int
pageInfo: PageInfo
facets: Facets
}

type PageInfo {
currentPage: Int
startPage: Int
endPage: Int
hasNextPage: Boolean
hasPreviousPage: Boolean
}

Example Query:

query {
events(page: 0, size: 20, criteria: { sports: [run] }) {
totalCount
pageCount
pageInfo {
currentPage
hasNextPage
hasPreviousPage
}
data {
name { en }
}
}
}

Facets Model

Aggregations for Filtering

Facets provide aggregated counts for filter options, enabling faceted navigation interfaces.

Available Facets:

  • sport - Event counts by sport
  • country - Event counts by country
  • cities - Event counts by city
  • months - Event counts by month
  • distance - Event counts by distance category
  • courseProfile - Event counts by difficulty
  • courseType - Event counts by course layout
  • terrain - Event counts by surface type
  • environments - Event counts by environment tags
  • participation - Event counts by participation type
  • And more...

Facet Structure:

type FacetValue {
key: String
count: Int
}

Example Usage:

query {
events(criteria: { countries: ["US"] }) {
totalCount
facets {
sport {
key
count
}
distance {
key
count
}
}
}
}

Example Response:

{
"totalCount": 1247,
"facets": {
"sport": [
{ "key": "run", "count": 892 },
{ "key": "trail_run", "count": 355 }
],
"distance": [
{ "key": "marathon", "count": 234 },
{ "key": "half_marathon", "count": 456 },
{ "key": "_10k", "count": 312 }
]
}
}
Use Case

Use facets to build dynamic filter UIs where users can see how many events match each filter option before applying it.


Data Relationships

Entity Relationship Diagram

┌─────────────────────┐
│ Event │
│ │
│ - id │
│ - name │
│ - location │
│ - sport │
│ - permalink │
└──────────┬──────────┘
│ 1:N

┌──────────▼──────────┐
│ Edition │
│ │
│ - editionId │
│ - startDate │
│ - editionStatus │
│ - registrationUrl │
└──────────┬──────────┘
│ 1:N

┌──────────▼──────────┐
│ Race │
│ │
│ - id │
│ - raceName │
│ - distanceKm │
│ - startTime │
│ - participant │
└──────────┬──────────┘
│ 1:N

┌──────────▼──────────┐
│ Activity │
│ │
│ - distance │
│ - elevationGain │
│ - courseProfile │
│ - terrain │
│ - order │
└─────────────────────┘

Best Practices

Querying Strategy

  1. Request Only What You Need

    # Good - Specific fields
    query {
    events(page: 0, size: 10) {
    data {
    id
    name { en }
    startDate
    }
    }
    }

    # Avoid - Over-fetching
    query {
    events(page: 0, size: 10) {
    data {
    name { en de es fr it pt nl sv ru ja ko zh }
    # ... requesting all fields
    }
    }
    }
  2. Use Appropriate Page Sizes

    • Start with size: 20 for list views
    • Use size: 100 maximum for performance
    • Larger page sizes increase response time
  3. Leverage Facets Wisely

    • Request facets only when building filter UIs
    • Omit facets for simple list queries
  4. Filter Early

    • Apply filters to reduce result set size
    • Combine multiple criteria for precise targeting
  5. Handle Localization Efficiently

    • Request only needed languages
    • Use nameLanguage to detect source language

Next Steps