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
}
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 scheduledcanceled- Event has been canceledpostponed- Event date has been movedturned_virtual- Event moved to virtual formatno_race- Edition skipped this yeardeleted- Edition removed from catalog
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!]
}
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:
- Swim - 1.5 km in open water
- Bike - 40 km road cycling
- 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- Englishen_US- English (United States)en_GB- English (United Kingdom)es- Spanishes_419- Spanish (Latin America)fr- Frenchde- Germanit- Italianpt- Portuguesept_BR- Portuguese (Brazilian)nl- Dutchsv- Swedishru- Russianja- Japaneseko- Koreanzh- Chinesezh_HANT- Chinese (Traditional)th- Thaitr- Turkishid- 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"
}
}
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:
| Field | Type | Description |
|---|---|---|
city | LocalizedString | City name in multiple languages |
location | LocalizedString | Full location description |
country | LocalizedCountry | Country object with ISO code and localized names |
countryIso | String | ISO 3166-1 alpha-2 country code |
lonlat | [Float!] | [longitude, latitude] coordinates |
region1 | eventRegion1 | Primary administrative region |
region2 | eventRegion2 | Secondary 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
}
}
}
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:
| Category | Filter | Type | Description |
|---|---|---|---|
| Location | countries | [String!] | Filter by country ISO codes |
regions1 | [Region1Enum!] | Filter by primary region | |
regions2 | [Region2Enum!] | Filter by secondary region | |
city | String | Filter by city name | |
around | Around | Geo-radius filter | |
| Sport | sports | [SportEnum!] | Filter by sport type |
| Date | dateRange | DateRange | Filter by date range |
| Distance | distanceRange | DistanceRange | Filter by race distance |
distanceTypes | [DistanceTypeEnum!] | Filter by standard distances | |
| Course | courseProfiles | [CourseProfileEnum!] | Filter by terrain difficulty |
courseTypes | [CourseTypeEnum!] | Filter by course layout | |
terrains | [TerrainEnum!] | Filter by terrain type | |
| Tags | environments | [EnvironmentEnum!] | Filter by environment tags |
participations | [ParticipationEnum!] | Filter by participation type | |
others | [OtherEnum!] | Filter by other attributes | |
| Status | eventStatus | EventStatusEnum | Filter by event status |
editionStatus | EditionStatusEnum | Filter by edition status | |
| Search | text | String | Full-text search |
Enumeration Types
Sport Types
Current supported sports:
run- Road runningtrail_run- Trail running
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 eventshalf_marathon- Half marathon (21.0975 km)marathon- Marathon (42.195 km)other- Non-standard distances
Course Profiles
Terrain difficulty classification:
flat- Minimal elevation changeundulating- Rolling hillshilly- Significant elevation changesextreme- Very challenging elevation
Course Types
Course layout patterns:
point_to_point- Start and finish at different locationsout_and_back- Run out and return on same routesingle_loop- One continuous looploops- Multiple laps of the same circuit
Terrain Types
Surface characteristics:
road- Paved roadstrail- Off-road trailstrack- Athletic tracksmulti_terrain- Mixed surfacesindoor- Indoor venuesgravel- Gravel pathssnow- Snow-covered terrainsand- Beach or sandurban_trail- City trails
Environment Tags
Event atmosphere and location features:
scenic- Scenic viewscity- Urban environmentnature- Natural settingmountain- Mountain locationbeach- Beach locationforest- Forest trailshistorical- Historical sitesnight- Night racesmusic- Music festival atmosphere- And many more...
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 sportcountry- Event counts by countrycities- Event counts by citymonths- Event counts by monthdistance- Event counts by distance categorycourseProfile- Event counts by difficultycourseType- Event counts by course layoutterrain- Event counts by surface typeenvironments- Event counts by environment tagsparticipation- 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 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
-
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
}
}
} -
Use Appropriate Page Sizes
- Start with
size: 20for list views - Use
size: 100maximum for performance - Larger page sizes increase response time
- Start with
-
Leverage Facets Wisely
- Request facets only when building filter UIs
- Omit facets for simple list queries
-
Filter Early
- Apply filters to reduce result set size
- Combine multiple criteria for precise targeting
-
Handle Localization Efficiently
- Request only needed languages
- Use
nameLanguageto detect source language
Next Steps
- 📝 Review Query Examples for practical use cases
- 📚 Explore the GraphQL Schema Reference for complete type definitions
- 🔐 Check Authentication for API access setup