Building Your Integration Guide#
Once you have reviewed the API's using the Getting Started Guide you'll want to start building your application integration. The following are a few things to think about along with some requirements and recommendations.
Your API connection is secured by an API key. This key is secret and must not be shared with external parties or users. Always check to be sure the code you write does not inadvertently expose your API key.
Danger
The API key is a secret key associated with your account. Do not share it or expose it in public web pages. Doing so would allow anyone to make queries as if they were you. If you think your API key may have been compromised, contact your Quarter4 Account representative immediately to disable your current key and receive a new key.
Expected Data Flow#
To load data and distribute it to your users, you must:
- Query the Quarter4 API on a server-to-server layer of your application,
- store the result in a local database or similar storage system,
- and then send information to your users as they request it.
Here is a sequence diagram that outlines the general process:
This will allow you to use the API key for your Quarter4 API queries from your server, while sending the result to your application users without the api key information.
Important
The response may contain reference to the request URL for links to previous and next pages. These links may contain query variables including the API key. It is important that you do not simply forward the response onto your users but instead store and cache the information on your server and incorporate it into your existing application API.
Changes and Caching#
The pre-game predictions may be updated roughly once every 15 minutes as changes occur. We recommend you query our changes api endpoints for updates no quicker than every 15 minutes to retrieve the most recent changes.
Pre-game predictions will not change once a game has started so if you have already retrieved predictions, you do not need to update them once the startDate
passes.
Caching the responses will also improve the overall experience as you can store common entities such as the team and player, then later you can reference that stored information by uuid when querying for event and prediction details instead of reloading or requesting the team and player details with each request.
To check for changes on individual entities, each entity collection has and updatedAt
filter option. For example, to retrieve win_loss
entities after a specific date, specify a date for the updatedAt[after]
filter:
GET https://api.quarter4.io/{sport}/v2/win_losses?updatedAt[after]=2023-04-20T16:56:59+00:00`
The result will contain all the win_loss entities that have been modified after the specific date. Use the last time you checked as the date to retrieve only changes since you last checked.
Additionally, you can use the /{sport}/v2/changes
endpoint to retrieve a list of all changes in the last 24 hours (which can also be filtered using updatedAt
). For example:
GET https://api.quarter4.io/{sport}/v2/changes?updatedAt[after]=2023-04-20T16:56:59+00:00`
{
"links": {
"self": "\/{sport}\/v2\/changes?count=300\u0026updatedAt%5Bafter%5D=2023-04-20T16%3A56%3A59%2B00%3A00"
},
"meta": {
"totalItems": 2,
"itemsPerPage": 300,
"currentPage": 1
},
"data": [
{
"id": "\/{sport}\/v2\/events\/dd86eab1-2e19-324a-8e4e-67199e118d55",
"attributes": {
"updatedAt": "2023-04-20T16:57:02+00:00"
}
},
{
"id": "\/{sport}\/v2\/win_losses\/2baccb07-3ada-3da8-9325-bd95bf19f530",
"attributes": {
"updatedAt": "2023-04-20T16:57:02+00:00"
}
}
]
}
The result contains references to multiple entity types. Use the id
property to construct the URL you need to query to get the specific entities changes. For example to retrieve the two entities above you would query:
GET https://api.quarter4.io/{sport}/v2/events/dd86eab1-2e19-324a-8e4e-67199e118d55`
GET https://api.quarter4.io/{sport}/v2/win_losses/2baccb07-3ada-3da8-9325-bd95bf19f530`
Note
The entity updatedAt
date changes when any value for the record changes so it may not necessarily indicate a prediction change, just that something in the record changed. In some cases it may not be a property you are interested in but using the changes
endpoint and/or the updatedAt
filters will drastically cut down on the number of queries you ned to make to ensure your system is up to date with the latest data updates.
Rate Limiting and Optimizing the use of JSON:API#
By Default, your Quarter4 APIs key has a rate limit of 3000 queries per 5 minutes (roughly 10 queries per second). To allow you to optimize your usage, all Quarter4 pull API responses use the JSON:API response format. This format minimizes repetitive data in the request and allows you to provide some control over what data is included or excluded from each request including related items.
Here's a simplified example with a few key details:
GET https://api.quarter4.io/{sport}/v2/events?startDate[after]=2022-03-18T15:01:22+00:00&order[startDate]=asc&include=winloss&fields[Event]=uuid,startDate,title&count=1&fields[winloss]=uuid,homeWinProbability,awayWinProbability HTTP/1.1
Accept: application/vnd.api+json
Note
The above example URI shows unencoded [
and ]
characters simply for readability. In practice, these characters should be urlencoded, as noted in the base specification. See “Square Brackets in Parameter Names” in the JSON:API documentation.
{
"meta": {
"totalItems": 265,
"itemsPerPage": 1,
"currentPage": 1
},
"data": [
{
"id": "\/{sport}\/v2\/events\/ae4c3bbd-b357-4b66-904a-5dc25b3d3a96",
"type": "Event",
"attributes": {
"startDate": "2022-03-18T16:15:00+00:00",
"title": "Team A vs Team B",
"uuid": "ae4c3bbd-b357-4b66-904a-5dc25b3d3a96"
},
"relationships": {
"winloss": {
"data": {
"type": "WinLoss",
"id": "\/{sport}\/v2\/win_losses\/ae4c3bbd-b357-4b66-904a-5dc25b3d3a96"
}
}
}
}
],
"included": [
{
"id": "\/{sport}\/v2\/win_losses\/ae4c3bbd-b357-4b66-904a-5dc25b3d3a96",
"type": "WinLoss",
"attributes": {
"uuid": "ae4c3bbd-b357-4b66-904a-5dc25b3d3a96",
"homeWinProbability": 33,
"awayWinProbability": 67
}
}
]
}
The preceding example shows a list of Event
entity results and related prediction information for the events in the response.
A few key parameters to note are:
The count
(and page
) parameters#
The count
in the query parameters limits the number of results per page in the response. The default is 30
results per page. The response's meta
data will contain the totalItems
which you can use to determine if there are more items available beyond the current page. To query a specific page, include the page
parameter (starting with 1
as the first page). In this case, for the next result you would include &count=1&page=2
for 1 result per page and results for page 2.
The order
parameter (sorting)#
The order
query parameter orders the result is a specific order using the following syntax ?order[property]=<asc|desc>
for example:
&order[startDate]=asc&order[title]=desc
This orders the results first by startDate
in asc
ending order then by title
in desc
ending order for items with the same start date. Some APIs may have a default order but in most cases some form of ordering will be desired.
The include
parameter#
This is useful in reducing the number of queries you need to make to retrieve all information. JSON:API returns results in a relational format and allows you to request a compound document including related resources. In this example, the full entity for the winloss
relationship is included in teh result as well:
"relationships": {
"winloss": {
"data": {
"type": "WinLoss",
"id": "\/{sport}\/v2\/win_losses\/ae4c3bbd-b357-4b66-904a-5dc25b3d3a96"
}
}
}
it will be included in the response under the included
list:
"included": [
{
"id": "\/{sport}\/v2\/win_losses\/ae4c3bbd-b357-4b66-904a-5dc25b3d3a96",
"type": "WinLoss",
"attributes": {
"uuid": "ae4c3bbd-b357-4b66-904a-5dc25b3d3a96",
"homeWinProbability": 33,
"awayWinProbability": 67
}
}
]
Your server side application will need to parse the response to locate included items and relate them back to the main entities in teh response. This format is most useful when requesting longer lists of information where the relationship could be repeated.
For example, if you query a list of events with multiple results you can include the homeTeam
and awayTeam
. An entry for "Team A" will be in the included
list, but multiple events may reference that same team as the home or away team. This reduces the overall result size and repetition by including the Team data once and indicating the relationship for that data.
Multiple related resources can be requested in a comma-separated list such as include=winloss,homeTeam,awayTeam
Note
Due to the size of data resources, the Quarter4 api limits the api to as single depth. You may include and Event's homeTeam
but not the homeTeam.nextEvent
. To perform deeper includes, call the associated endpoint and cache the result to use as necessary.
The fields
parameter#
The JSON:API response also include support for Sparse Fieldsets. This allows you to be more specific with which data you would like to see in the response's attributes for specific resources. By default ALL attributes will be included but in many cases you are likely only looking at a few so you may wish to exclude others to reduce the size of the response.
In the example above, the Event's fields are uuid
, startDate
and title
:
fields[Event]=uuid,startDate,title
"id": "\/{sport}\/v2\/events\/ae4c3bbd-b357-4b66-904a-5dc25b3d3a96",
"type": "Event",
"attributes": {
"startDate": "2022-03-18T16:15:00+00:00",
"title": "Team A vs Team B",
"uuid": "ae4c3bbd-b357-4b66-904a-5dc25b3d3a96"
},
while the fields from the winloss for the Even't relationship are: uuid
, homeWinProbability
, awayWinProbability
:
fields[winloss]=uuid,homeWinProbability,awayWinProbability
"id": "\/{sport}\/v2\/win_losses\/ae4c3bbd-b357-4b66-904a-5dc25b3d3a96",
"type": "WinLoss",
"attributes": {
"uuid": "ae4c3bbd-b357-4b66-904a-5dc25b3d3a96",
"homeWinProbability": 33,
"awayWinProbability": 67
}
Note
the fields
are case sensitive. In the above example fields[Event]
refers to the type of resouse you are querying (Event
) however the fields[winloss]
refers to the Event's winloss
attribute so the winloss
case must match that of the Events's attribute. Using fields[WinLoss]
would not filter the Event winloss attribute properly.
Additional parameters#
Date field types#
If the API refers to a date, for example the Event start date, there may be additional options for filtering by date range. All system dates are in UTC and use the following syntax ?property[<after|before|strictly_after|strictly_before>]=value
for example:
&startDate[after]=2022-09-06T15:37:02+00:00
This results in items where start date is after 2022-09-06T15:37:02+00:00
UTC.
For example to query upcoming events after Sept 6th, 2022 UTC would be:
https://api.quarter4.io/{sport}/v2/events?startDate[after]=2022-09-06&order[startDate]=asc&league.uuid=38344248-9889-11eb-a8ab-0647cdb505d0&page=1&count=30