REST API
OpenAPI SpecificationAuthentication
Most endpoints are open. A few carry player data and need an API key in the Saphi-Api-Key header - ask us for one. The API is currently read-only; write-endpoints might be added over time.
# open endpoint, no credentials
curl https://www.projectsaphi.com/api/v2/tracks
# player data, needs a key
curl -H "Saphi-Api-Key: your-api-key" \
https://www.projectsaphi.com/api/v2/players
Rate limits
Every request costs budget, and expensive endpoints cost more. Each response reports what you have left in X-RateLimit-Remaining. A 429 tells you how long to wait in Retry-After.
# every response
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 2
# budget spent
HTTP/1.1 429
Retry-After: 5
{ "error": "Rate limit exceeded.", "code": "RATE_LIMITED" }
Caching
Shared reads carry an ETag. Send it back as If-None-Match and an unchanged response comes back as a bodyless 304, charged 1 instead of the endpoint's cost.
# first response
ETag: "8f14e45fceea167a"
# ask again with the validator
curl -H 'If-None-Match: "8f14e45fceea167a"' \
https://www.projectsaphi.com/api/v2/tracks
HTTP/1.1 304 # unchanged, no body sent
Event 2 endpoints
List the kinds of timeline event that exist.
display_in_sr_history marks the types that /site-records/progression interleaves into its meta; the others are timeline-only.
Example
curl https://www.projectsaphi.com/api/v2/event-types
Response
{
"data": [
{
"id": 2,
"name": "Track Update",
"color": "#4a7fd4",
"display_in_sr_history": true
}
],
"meta": {
"current_page": 1,
"per_page": 3,
"total": 3,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List timeline events.
Newest first. An event with a null track_id applies to the whole site rather than one track, and a null category_id applies to both categories. The timeline is small enough to fetch whole, so there is no name search - filter the result client-side.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
track_id
|
integer | No | Only events attached to this track |
event_type_id
|
integer | No | Only events of this type, as listed by /event-types |
category_id
|
integer | No | Only events attached to this category: 1 (Course) or 2 (Lap) |
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/events
Response
{
"data": [
{
"id": 4,
"event_type_id": 2,
"event_type_name": "Shortcut Discovery",
"event_type_color": "#4a7fd4",
"event_type_display_in_sr_history": true,
"track_id": 1,
"track_name": "Drive Thru Danger",
"category_id": null,
"category_name": null,
"name": "MisterM Skip",
"description": "Dropping down at the start to the S section was discovered.",
"event_date": "2026-03-14 12:00:00",
"created_at": "2026-03-14 19:02:11"
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 12,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Leaderboard 4 endpoints
Fetch the leaderboard for a single track.
Entries are ranked, and carry the achieved standard, SR:PR ratio, points and percentile.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
track_id
|
integer | Yes | Track to read |
category_id
|
integer | No | Category to filter by: 1 (Course) or 2 (Lap) |
engine_id
|
integer | No | Only times set with this engine |
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/leaderboards?track_id=1
Response
{
"data": [
{
"id": 25523,
"rank": 1,
"user_id": 31,
"username": "Infernal",
"display_name": null,
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"category_id": 1,
"category_name": "Course",
"character_id": 14,
"character_name": "Penta Penguin",
"engine_id": 4,
"engine_name": "Turn",
"paintjob_id": 14,
"paintjob_name": "Penta White",
"skin_id": null,
"skin_name": null,
"wheels_id": 1,
"wheels_name": "Standard",
"default_cam": 0,
"time": 9223,
"time_formatted": "1:32.23",
"submitted_at": "2026-02-10 21:49:39",
"standard_id": 2,
"standard_name": "God",
"sr_pr": 1,
"points": 30,
"percentile": 100
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 345,
"total_pages": 4
}
}
A real response, trimmed to a single item.
Returns an overview of tracks that have leaderboards.
Each entry carries track data and how many players have submitted times on that track.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/leaderboards/overview
Response
{
"data": [
{
"track_id": 1,
"track_name": "Drive Thru Danger",
"author": "Boxic",
"lap_count": 3,
"difficulty": 9,
"track_type_id": 1,
"track_type": "saphi",
"submission_count": 1966,
"player_count": 345,
"thumbnail_url": "/media/thumbnails/original/9e28f866-c645-4320-af0a-870d99499240.jpg"
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 30,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List the site records on every track.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
category_id
|
integer | No | Category to filter by: 1 (Course) or 2 (Lap) |
engine_id
|
integer | No | Only records set with this engine |
country_id
|
integer | No | Only records held by players from this country |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
Example
curl https://www.projectsaphi.com/api/v2/site-records
Response
{
"data": [
{
"id": 25523,
"track_id": 1,
"track_name": "Drive Thru Danger",
"track_author": "Boxic",
"category_id": 1,
"category_name": "Course",
"user_id": 31,
"username": "Infernal",
"display_name": null,
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"character_id": 14,
"character_name": "Penta Penguin",
"engine_id": 4,
"engine_name": "Turn",
"paintjob_id": 14,
"paintjob_name": "Penta White",
"skin_id": null,
"skin_name": null,
"wheels_id": 1,
"wheels_name": "Standard",
"default_cam": 0,
"time": 9223,
"time_formatted": "1:32.23",
"submitted_at": "2026-02-10 21:49:39",
"standard_id": 2,
"standard_name": "God"
}
],
"meta": {
"current_page": 1,
"per_page": 61,
"total": 61,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Trace how the site record for a track changed over time.
Historical site records are returned oldest first, each with the improvement over the previous record. Timeline events affecting the track are in meta.events rather than mixed into the list.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
track_id
|
integer | Yes | Track to trace |
category_id
|
integer | No | Category to filter by: 1 (Course) or 2 (Lap) |
start_date
|
integer | No | Unix timestamp; ignore records before this |
end_date
|
integer | No | Unix timestamp; ignore records after this |
Example
curl https://www.projectsaphi.com/api/v2/site-records/progression?track_id=1
Response
{
"data": [
{
"index": 1,
"user_id": 5,
"username": "TheUbMunster",
"name": "TheUbMunster",
"country_id": 192,
"country_name": "United States of America",
"time": 17203,
"time_formatted": "2:52.03",
"improvement": null,
"improvement_formatted": null,
"character_id": 1,
"character_name": "Crash Bandicoot",
"engine_id": 1,
"engine_name": "Balanced",
"standard_tier_id": null,
"standard_tier_name": null,
"submit_date": "2025-10-15 00:19:58",
"submission_id": 1
}
],
"meta": {
"current_page": 1,
"per_page": 21,
"total": 21,
"total_pages": 1,
"track_id": 1,
"track_name": "Drive Thru Danger",
"category_id": 1,
"has_records": true,
"events": [
{
"name": "Blounard Skip",
"description": null,
"type": "Shortcut Discovery",
"color": "#8c1818",
"date": "2025-11-06 23:00:00"
}
]
}
}
A real response, trimmed to a single item.
Metrics 1 endpoints
List every metric the client can record.
The catalogue behind /metrics/{userId}, which only reports the metrics a player actually has values for. Read this to know what may appear and how to render it: type is how repeated sessions are merged, format is how the value should be shown, and divisor has already been applied to the value that endpoint returns.
Example
curl https://www.projectsaphi.com/api/v2/metrics
Response
{
"data": [
{
"key": "total_distance_driven",
"label": "Total Distance Driven",
"type": "cumulative",
"format": "float",
"icon": "fa-road",
"divisor": 7680
}
],
"meta": {
"current_page": 1,
"per_page": 30,
"total": 30,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Player 8 endpoints
Compare two players head to head.
Returns each player's time per track alongside the gap between them.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
player1_id
|
integer | Yes |
First player
Example: 5
|
player2_id
|
integer | Yes |
Second player
Example: 13
|
categories
|
string | No |
Comma-separated category IDs (default 1,2)
Example: 1,2
|
Example
curl -H "Saphi-Api-Key: your-api-key" https://www.projectsaphi.com/api/v2/matchups?player1_id=5&player2_id=13&categories=1,2
Response
{
"data": [
{
"player1": {
"id": 5,
"username": "TheUbMunster",
"display_name": null,
"name": "TheUbMunster",
"country_id": 192,
"country_name": "United States of America",
"wins": 0
},
"player2": {
"id": 13,
"username": "Garma",
"display_name": null,
"name": "Garma",
"country_id": 65,
"country_name": "Germany",
"wins": 60
},
"ties": 0,
"total_tracks": 60,
"overall_winner": 2,
"comparisons": [
{
"track_id": 1,
"track_name": "Drive Thru Danger",
"track_author": "Boxic",
"category_id": 1,
"category_name": "Course",
"player1_time": 14236,
"player2_time": 10649,
"player1_time_formatted": "2:22.36",
"player2_time_formatted": "1:46.49",
"player1_character_id": 7,
"player1_character_name": "Polar",
"player2_character_id": 14,
"player2_character_name": "Penta Penguin",
"difference": 3587,
"difference_formatted": "35.87",
"winner": 2
}
]
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Fetch a player's recorded race metrics.
Play time, distance driven and similar counters the Saphi client reports.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
userId
path
|
integer | Yes |
Player to report on
Example: 5
|
track_id
|
integer | No | Only metrics for this track |
Example
curl -H "Saphi-Api-Key: your-api-key" https://www.projectsaphi.com/api/v2/metrics/5
Response
{
"data": [
{
"trackId": 1,
"key": "average_airtime",
"value": 0.18039159663866,
"formattedValue": "0.18s",
"label": "Average Airtime",
"icon": "fa-cloud",
"updatedAt": "2026-05-08 07:57:13"
}
],
"meta": {
"current_page": 1,
"per_page": 444,
"total": 444,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Fetch a player's personal bests.
Give either user_id or username.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
user_id
|
integer | No |
Player to read
Example: 5
|
username
|
string | No | Player to read, by username |
track_id
|
integer | No | Only this track |
category_id
|
integer | No | Category to filter by: 1 (Course) or 2 (Lap) |
engine_id
|
integer | No | Only times set with this engine |
per_engine
|
boolean | No | Also return the best time per engine, tagged with engine_id |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl -H "Saphi-Api-Key: your-api-key" https://www.projectsaphi.com/api/v2/pbs?user_id=5
Response
{
"data": [
{
"id": 423,
"track_id": 1,
"track_name": "Drive Thru Danger",
"category_id": 1,
"category_name": "Course",
"character_id": 7,
"character_name": "Polar",
"engine_id": 4,
"engine_name": "Turn",
"paintjob_id": 7,
"paintjob_name": "Polar Cyan",
"skin_id": null,
"skin_name": null,
"wheels_id": 1,
"wheels_name": "Standard",
"default_cam": 0,
"time": 14236,
"time_formatted": "2:22.36",
"submitted_at": "2025-10-16 02:04:13",
"rank": 63,
"standard_id": 4,
"standard_name": "Champion",
"sr_pr": 0.6478645686990727,
"points": 0,
"percentile": 81.97674418604652
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 60,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Search players.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
|
integer | No | Return only this player |
username
|
string | No | Filter by username, matching anywhere in it |
country_id
|
integer | No | Only players from this country |
status_id
|
integer | No | Only players with this account status. Unverified and banned accounts are never listed |
discord_id
|
string | No | Find the player linked to this Discord account |
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl -H "Saphi-Api-Key: your-api-key" https://www.projectsaphi.com/api/v2/players
Response
{
"data": [
{
"id": 1,
"username": "Jasu14",
"display_name": "Jasu14",
"name": "Jasu14",
"created_at": "2023-08-10 08:54:31",
"country_id": 165,
"country_name": "Spain",
"status": 5
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 974,
"total_pages": 10
}
}
A real response, trimmed to a single item.
Fetch a player profile along with their statistics.
Give either user_id or username.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
user_id
|
integer | No |
Player to fetch
Example: 5
|
username
|
string | No | Player to fetch, by username |
Example
curl -H "Saphi-Api-Key: your-api-key" https://www.projectsaphi.com/api/v2/profiles?user_id=5
Response
{
"data": [
{
"id": 5,
"username": "TheUbMunster",
"display_name": null,
"name": "TheUbMunster",
"avatar_url": null,
"country": {
"id": 192,
"name": "United States of America"
},
"stats": {
"total_points": 0,
"course_points": 0,
"lap_points": 0,
"total_time": 502473,
"total_time_formatted": "83:44.73",
"average_finish": 132.85,
"average_standard": 3.808,
"average_sr_pr": 0.7684,
"first_places": 0,
"podium_finishes": 0,
"tracks_submitted": 123,
"completed_tracks": 30,
"total_tracks": 30,
"first_submission_at": "2025-10-15 00:19:58",
"last_submission_at": "2026-05-27 21:25:02",
"most_played_track": {
"id": 9,
"name": "Redrock Ravine",
"submission_count": 10
},
"most_played_character": {
"id": 1,
"name": "Crash Bandicoot",
"submission_count": 95
},
"most_played_engine": {
"id": 1,
"name": "Balanced",
"submission_count": 91
}
}
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Fetch a player profile by ID.
The same payload as /profiles, addressable by path so any user_id in a response can be followed directly.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Player to fetch |
Example
curl -H "Saphi-Api-Key: your-api-key" https://www.projectsaphi.com/api/v2/profiles/1
Response
{
"data": [
{
"id": 1,
"username": "Jasu14",
"display_name": "Jasu14",
"name": "Jasu14",
"avatar_url": null,
"country": {
"id": 165,
"name": "Spain"
},
"stats": {
"total_points": 6,
"course_points": 0,
"lap_points": 6,
"total_time": 551348,
"total_time_formatted": "91:53.48",
"average_finish": 112.717,
"average_standard": 3.404,
"average_sr_pr": 0.7349,
"first_places": 0,
"podium_finishes": 0,
"tracks_submitted": 125,
"completed_tracks": 20,
"total_tracks": 30,
"first_submission_at": "2025-10-23 00:04:22",
"last_submission_at": "2026-02-11 11:08:04",
"most_played_track": {
"id": 6,
"name": "Green Hill Raceway",
"submission_count": 13
},
"most_played_character": {
"id": 1,
"name": "Crash Bandicoot",
"submission_count": 31
},
"most_played_engine": {
"id": 4,
"name": "Turn",
"submission_count": 74
}
}
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Fetch what a player has unlocked.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Player to report on |
show_locked
|
boolean | No | Also list unlockables the player has not earned |
Example
curl -H "Saphi-Api-Key: your-api-key" https://www.projectsaphi.com/api/v2/profiles/1/unlockables
Response
{
"data": [
{
"id": 23,
"key": "oxide_character",
"name": "Oxide",
"description": null,
"type": "character",
"type_label": "Character",
"hidden": true,
"progress": true,
"unlocked": true,
"unlocked_at": "2026-04-30 23:11:32"
}
],
"meta": {
"current_page": 1,
"per_page": 2,
"total": 2,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Fetch which standards a player has reached.
The standard times of every track together with the best standard the given player has achieved.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
player_id
|
integer | Yes |
Player to report on
Example: 5
|
Example
curl -H "Saphi-Api-Key: your-api-key" https://www.projectsaphi.com/api/v2/standards/achievements?player_id=5
Response
{
"data": [
{
"track_id": 1,
"track_name": "Drive Thru Danger",
"track_author": "Boxic",
"standards": {
"1": {
"2": {
"time": 12400,
"time_formatted": "2:04.00",
"achieved": false
},
"3": {
"time": 12800,
"time_formatted": "2:08.00",
"achieved": false
},
"4": {
"time": 14400,
"time_formatted": "2:24.00",
"achieved": true
},
"5": {
"time": 16000,
"time_formatted": "2:40.00",
"achieved": false
}
},
"2": {
"2": {
"time": 3950,
"time_formatted": "39.50",
"achieved": false
},
"3": {
"time": 4100,
"time_formatted": "41.00",
"achieved": false
},
"4": {
"time": 4400,
"time_formatted": "44.00",
"achieved": false
},
"5": {
"time": 5000,
"time_formatted": "50.00",
"achieved": true
}
}
}
}
],
"meta": {
"current_page": 1,
"per_page": 26,
"total": 26,
"total_pages": 1,
"player_id": 5,
"categories": [
{
"id": 1,
"name": "Course"
}
],
"tiers": [
{
"id": 2,
"name": "God"
}
]
}
}
A real response, trimmed to a single item.
Ranking 7 endpoints
Players ranked by their average finishing position.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
category
|
string | No | Category to rank within: course, lap or combined |
type
|
string | No |
Either single player or country
One of:
player, country
|
country
|
integer | No | Only include players from this country |
engine
|
integer | No | Only include times set with this engine |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/rankings/average-finish
Response
{
"data": [
{
"id": 31,
"username": "Infernal",
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"rank": 1,
"tracks_submitted": 60,
"total_tracks": 60,
"average_finish": 3.01667,
"average_by_category": {
"1": 3.1,
"2": 2.93333
}
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 704,
"total_pages": 8,
"ranking_type": "player",
"category": null,
"track_counts": {
"total": 60,
"by_category": {
"1": 30,
"2": 30
}
}
}
}
A real response, trimmed to a single item.
Players ranked by their average standard.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
category
|
string | No | Category to rank within: course, lap or combined |
type
|
string | No |
Either single player or country
One of:
player, country
|
country
|
integer | No | Only include players from this country |
engine
|
integer | No | Only include times set with this engine |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/rankings/average-rank
Response
{
"data": [
{
"id": 31,
"username": "Infernal",
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"rank": 1,
"tracks_submitted": 52,
"total_tracks": 52,
"average_rank": 0,
"average_by_category": {
"1": 0,
"2": 0
},
"standard_id": 2,
"standard_name": "God",
"standard_by_category": {
"1": 2,
"2": 2
},
"standard_name_by_category": {
"1": "God",
"2": "God"
},
"lowest_standard_id": 2,
"lowest_standard_name": "God",
"lowest_standard_by_category": {
"1": 2,
"2": 2
},
"lowest_standard_name_by_category": {
"1": "God",
"2": "God"
}
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 704,
"total_pages": 8,
"ranking_type": "player",
"category": "total",
"track_counts": {
"total": 52,
"by_category": {
"1": 26,
"2": 26
}
}
}
}
A real response, trimmed to a single item.
Players by based on their finishing positions.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
category
|
string | No | Category to rank within: course, lap or combined |
type
|
string | No |
Either single player or country
One of:
player, country
|
country
|
integer | No | Only include players from this country |
engine
|
integer | No | Only include times set with this engine |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/rankings/medal-table
Response
{
"data": [
{
"id": 31,
"username": "Infernal",
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"rank": 1,
"tracks_submitted": 60,
"total_tracks": 60,
"total_medals": {
"1": 19,
"2": 16,
"3": 8,
"4_5": 10,
"6_10": 6,
"11_20": 1,
"21_50": 0,
"51_100": 0,
"101_200": 0
},
"medals_by_category": {
"1": {
"1": 8,
"2": 9,
"3": 4,
"4_5": 5,
"6_10": 4,
"11_20": 0,
"21_50": 0,
"51_100": 0,
"101_200": 0
},
"2": {
"1": 11,
"2": 7,
"3": 4,
"4_5": 5,
"6_10": 2,
"11_20": 1,
"21_50": 0,
"51_100": 0,
"101_200": 0
}
}
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 704,
"total_pages": 8,
"ranking_type": "player",
"category": null,
"track_counts": {
"total": 60,
"by_category": {
"1": 30,
"2": 30
}
}
}
}
A real response, trimmed to a single item.
Players ranked by their average percentile.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
category
|
string | No | Category to rank within: course, lap or combined |
type
|
string | No |
Either single player or country
One of:
player, country
|
country
|
integer | No | Only include players from this country |
engine
|
integer | No | Only include times set with this engine |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/rankings/percentile
Response
{
"data": [
{
"id": 31,
"username": "Infernal",
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"rank": 1,
"tracks_submitted": 60,
"total_tracks": 60,
"percentile": 99.221089166,
"percentile_by_category": {
"1": 99.181701666,
"2": 99.260476666
}
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 704,
"total_pages": 8,
"ranking_type": "player",
"category": null,
"track_counts": {
"total": 60,
"by_category": {
"1": 30,
"2": 30
}
}
}
}
A real response, trimmed to a single item.
Players ranked by points earned.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
category
|
string | No | Category to rank within: course, lap or combined |
type
|
string | No |
Either single player or country
One of:
player, country
|
country
|
integer | No | Only include players from this country |
engine
|
integer | No | Only include times set with this engine |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/rankings/points
Response
{
"data": [
{
"id": 31,
"username": "Infernal",
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"rank": 1,
"tracks_submitted": 60,
"total_tracks": 60,
"points": 1679,
"points_by_category": {
"1": 837,
"2": 842
}
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 129,
"total_pages": 2,
"ranking_type": "player",
"category": null,
"track_counts": {
"total": 60,
"by_category": {
"1": 30,
"2": 30
}
}
}
}
A real response, trimmed to a single item.
Players by how close they are to the site record.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
category
|
string | No | Category to rank within: course, lap or combined |
type
|
string | No |
Either single player or country
One of:
player, country
|
country
|
integer | No | Only include players from this country |
engine
|
integer | No | Only include times set with this engine |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/rankings/sr-pr
Response
{
"data": [
{
"id": 31,
"username": "Infernal",
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"rank": 1,
"tracks_submitted": 60,
"total_tracks": 60,
"sr_pr": 0.981188666,
"sr_pr_by_category": {
"1": 0.988939,
"2": 0.973438333
}
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 704,
"total_pages": 8,
"ranking_type": "player",
"category": null,
"track_counts": {
"total": 60,
"by_category": {
"1": 30,
"2": 30
}
}
}
}
A real response, trimmed to a single item.
Players ranked by the sum of their best times.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
category
|
string | No | Category to rank within: course, lap or combined |
type
|
string | No |
Either single player or country
One of:
player, country
|
country
|
integer | No | Only include players from this country |
engine
|
integer | No | Only include times set with this engine |
scope
|
string | No |
Track pool to draw from
One of:
saphi, community, original
|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/rankings/total-time
Response
{
"data": [
{
"id": 31,
"username": "Infernal",
"name": "Infernal",
"country_id": 83,
"country_name": "Italy",
"rank": 1,
"tracks_submitted": 60,
"total_tracks": 60,
"total_time": 391558,
"total_time_formatted": "65:15.58",
"time_by_category": {
"1": {
"total": 299884,
"total_formatted": "49:58.84",
"tracks": 30,
"submitted": 30
},
"2": {
"total": 91674,
"total_formatted": "15:16.74",
"tracks": 30,
"submitted": 30
}
}
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 704,
"total_pages": 8,
"ranking_type": "player",
"category": null,
"track_counts": {
"total": 60,
"by_category": {
"1": 30,
"2": 30
}
}
}
}
A real response, trimmed to a single item.
Reference 12 endpoints
List time trial categories.
Example
curl https://www.projectsaphi.com/api/v2/categories
Response
{
"data": [
{
"id": 1,
"name": "Course"
}
],
"meta": {
"current_page": 1,
"per_page": 2,
"total": 2,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List playable characters.
Example
curl https://www.projectsaphi.com/api/v2/characters
Response
{
"data": [
{
"id": 1,
"game_id": 0,
"name": "Crash Bandicoot",
"default_engine_id": 1,
"default_paintjob_id": 1,
"default_skin_id": null,
"default_wheels_id": 1,
"engine_name": "Balanced",
"default_paintjob_name": "Crash Blue",
"default_skin_name": null,
"default_wheels_name": "Standard",
"submission_count": 2404
}
],
"meta": {
"current_page": 1,
"per_page": 16,
"total": 16,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List the countries players can represent.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/countries
Response
{
"data": [
{
"id": 1,
"name": "Afghanistan"
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 200,
"total_pages": 2
}
}
A real response, trimmed to a single item.
List available engine styles.
Example
curl https://www.projectsaphi.com/api/v2/engines
Response
{
"data": [
{
"id": 1,
"game_id": 0,
"name": "Balanced",
"submission_count": 5544
}
],
"meta": {
"current_page": 1,
"per_page": 4,
"total": 4,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List game modes.
A game mode says what a track media version can be played for. Currently it is only used for tagging track media.
Example
curl https://www.projectsaphi.com/api/v2/game-modes
Response
{
"data": [
{
"id": 1,
"slug": "time_trial",
"name": "Time Trial"
}
],
"meta": {
"current_page": 1,
"per_page": 3,
"total": 3,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List available character paintjobs.
Example
curl https://www.projectsaphi.com/api/v2/paintjobs
Response
{
"data": [
{
"id": 1,
"game_id": 0,
"name": "Crash Blue"
}
],
"meta": {
"current_page": 1,
"per_page": 15,
"total": 15,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Fetch every reference data list in one request.
Characters, engines, categories, standards, paintjobs, skins, wheels, game modes, track types and user statuses together, so an external application can warm its lookups in a single call instead of ten.
Example
curl https://www.projectsaphi.com/api/v2/reference-data
Response
{
"data": [
{
"characters": [
{
"id": 1,
"game_id": 0,
"name": "Crash Bandicoot",
"default_engine_id": 1,
"default_paintjob_id": 1,
"default_skin_id": null,
"default_wheels_id": 1,
"engine_name": "Balanced",
"default_paintjob_name": "Crash Blue",
"default_skin_name": null,
"default_wheels_name": "Standard",
"submission_count": 2404
}
],
"engines": [
{
"id": 1,
"game_id": 0,
"name": "Balanced",
"submission_count": 5544
}
],
"categories": [
{
"id": 1,
"name": "Course"
}
],
"standards": [
{
"id": 1,
"name": "Faker",
"display_order": 1,
"numeric_value": null,
"include_in_average": false
}
],
"paintjobs": [
{
"id": 1,
"game_id": 0,
"name": "Crash Blue"
}
],
"skins": [],
"wheels": [
{
"id": 1,
"game_id": 0,
"name": "Standard"
}
],
"game_modes": [
{
"id": 1,
"slug": "time_trial",
"name": "Time Trial"
}
],
"track_types": [
{
"id": 1,
"slug": "saphi",
"name": "Saphi"
}
],
"user_statuses": [
{
"id": 3,
"name": "Verified"
}
]
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List available character skins.
Example
curl https://www.projectsaphi.com/api/v2/skins
Response
{
"data": [],
"meta": {
"current_page": 1,
"per_page": 0,
"total": 0,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List available standards.
Example
curl https://www.projectsaphi.com/api/v2/standards
Response
{
"data": [
{
"id": 1,
"name": "Faker",
"display_order": 1,
"numeric_value": null,
"include_in_average": false
}
],
"meta": {
"current_page": 1,
"per_page": 5,
"total": 5,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List the pools a track can belong to.
Resolves the track_type_id carried by /tracks. Saphi tracks are the curated pool the standards and rankings are built on, community tracks are everything else players have uploaded, and original tracks are the ones that shipped with the game. Each pool is a separate leaderboard; times are never compared across them. Only saphi and community tracks are downloadable, so a submission for an original track carries no track_media_id.
Example
curl https://www.projectsaphi.com/api/v2/track-types
Response
{
"data": [
{
"id": 1,
"slug": "saphi",
"name": "Saphi"
}
],
"meta": {
"current_page": 1,
"per_page": 3,
"total": 3,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List the account statuses a player can have.
Resolves the status field carried by /players and /profiles. Only verified accounts and above appear in the player list, so a consumer of those endpoints will not see the lower statuses in practice.
Example
curl https://www.projectsaphi.com/api/v2/user-statuses
Response
{
"data": [
{
"id": 3,
"name": "Verified"
}
],
"meta": {
"current_page": 1,
"per_page": 5,
"total": 5,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List available kart wheels.
Example
curl https://www.projectsaphi.com/api/v2/wheels
Response
{
"data": [
{
"id": 1,
"game_id": 0,
"name": "Standard"
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Statistics 1 endpoints
Fetch the global statistics
Totals, recent activity, track and world record leaders, character and engine usage, standard achievement counts and completion distributions. totals.tracks and completion are keyed by track pool slug, one entry per pool from /track-types, so a pool added later does not change the payload shape; a pool with no active tracks has a null completion entry. Recomputed at most every 15 minutes, and expensive to request: read the ETag and send If-None-Match rather than polling this endpoint.
Example
curl https://www.projectsaphi.com/api/v2/statistics
Response
{
"data": [
{
"totals": {
"tracks": { "saphi": 30, "community": 21, "original": 18, "total": 69 },
"categories": 2,
"countries": 69,
"characters": 16,
"players": 974,
"unlockables": 13,
"submissions": { "total": 44901, "course": 23271, "lap": 21630 }
},
"activity": {
"submissions_last_24h": 12,
"submissions_last_7d": 286,
"submissions_last_30d": 1319,
"average_per_day": 44,
"most_active_weekday": { "day_name": "Thursday", "submission_count": 7442 },
"peak_day": { "peak_date": "2026-01-01", "submission_count": 966 },
"most_active_players": [{ "id": 189, "name": "RandomFun", "submission_count": 1204 }],
"newest_active_players": [{ "id": 812, "name": "Newcomer", "created_at": "2026-07-02 10:11:00" }]
},
"tracks": {
"most_submissions": { "course": [], "lap": [] },
"most_players": { "course": [], "lap": [] },
"most_record_changes": { "course": [], "lap": [] },
"tightest_competition": { "course": [], "lap": [] },
"most_playtime": [],
"most_popular": []
},
"world_records": {
"oldest": { "course": {}, "lap": {} },
"newest": { "course": {}, "lap": {} },
"top_players": { "course": [], "lap": [] },
"top_countries": { "course": [], "lap": [] },
"top_characters": { "course": [], "lap": [] }
},
"players": {
"most_podiums": { "course": [], "lap": [] },
"countries_with_most_players": [],
"countries_with_most_submissions": []
},
"usage": {
"characters": { "course": [], "lap": [] },
"engines": { "course": [], "lap": [] }
},
"standards": { "standards": [], "no_standards": 0 },
"unlockables": { "most_common": [], "least_common": [] },
"completion": { "saphi": {}, "community": {}, "original": null }
}
]
}
A real response, trimmed to a single item.
Submission 4 endpoints
List recently submitted times.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
time_filter
|
string | No |
How far back to look: a count of hours, or a value like 24h, 7d or 30d (default 24h)
Example: 30d
|
track_id
|
integer | No | Only this track |
category_id
|
integer | No | Category to filter by: 1 (Course) or 2 (Lap) |
user_id
|
integer | No | Only this player |
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/submissions/recent?time_filter=30d
Response
{
"data": [
{
"id": 44982,
"user_id": 249,
"username": "jmz",
"display_name": null,
"name": "jmz",
"country_id": 32,
"country_name": "Canada",
"track_id": 13,
"track_name": "Papu's SG Temple",
"category_id": 2,
"category_name": "Lap",
"character_id": 11,
"character_name": "Ripper Roo",
"engine_id": 4,
"engine_name": "Turn",
"paintjob_id": 14,
"paintjob_name": "Penta White",
"skin_id": null,
"skin_name": null,
"wheels_id": 1,
"wheels_name": "Standard",
"default_cam": 1,
"time": 2916,
"time_formatted": "29.16",
"submitted_at": "2026-07-23 16:55:08",
"is_world_record": false
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 1253,
"total_pages": 13
}
}
A real response, trimmed to a single item.
Fetch a single submission.
The same shape as an entry of /submissions/recent.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Submission to fetch |
Example
curl https://www.projectsaphi.com/api/v2/submissions/1
Response
{
"data": [
{
"id": 1,
"user_id": 5,
"username": "TheUbMunster",
"display_name": null,
"name": "TheUbMunster",
"country_id": 192,
"country_name": "United States of America",
"track_id": 1,
"track_name": "Drive Thru Danger",
"category_id": 1,
"category_name": "Course",
"character_id": 1,
"character_name": "Crash Bandicoot",
"engine_id": 1,
"engine_name": "Balanced",
"paintjob_id": 1,
"paintjob_name": "Crash Blue",
"skin_id": null,
"skin_name": null,
"wheels_id": 1,
"wheels_name": "Standard",
"default_cam": 0,
"time": 17203,
"time_formatted": "2:52.03",
"submitted_at": "2025-10-15 00:19:58",
"is_world_record": false
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Download a submission's ghost file.
Returns the file itself, not JSON. A few kilobytes.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Submission the ghost belongs to |
Example
curl https://www.projectsaphi.com/api/v2/submissions/1/ghost
Download a submission's replay file.
Returns the file itself, not JSON. A few kilobytes.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Submission the replay belongs to |
Example
curl https://www.projectsaphi.com/api/v2/submissions/1/replay
System 2 endpoints
Check that the REST API is reachable.
Example
curl https://www.projectsaphi.com/api/v2/heartbeat
Response
{
"data": [
{
"status": "ok",
"timestamp": 1785172828,
"processing_time_ms": 2.86,
"client": {
"ip": "127.0.0.1",
"user_agent": null
}
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Fetch the OpenAPI description of the REST API.
An OpenAPI 3.1 document covering every endpoint an outside consumer can call. Generated from the API's own routing attributes, so it always matches the deployed code. Returned unwrapped, not inside the usual data envelope.
Example
curl https://www.projectsaphi.com/api/v2/openapi.json
Response
{
"openapi": "3.1.0",
"info": {
"title": "Saphi REST API",
"version": "2.0.0",
"description": "..."
},
"servers": [{ "url": "https://example.com" }],
"tags": [{ "name": "Track" }],
"paths": {
"/api/v2/tracks": {
"get": {
"operationId": "api.v2.tracks",
"summary": "List tracks",
"x-rate-limit-cost": 2,
"x-rate-limit-bucket": "api",
"parameters": [],
"responses": {}
}
}
},
"components": {
"securitySchemes": { "ApiKey": { "type": "apiKey", "in": "header", "name": "Saphi-Api-Key" } },
"schemas": {}
}
}
A real response, trimmed to a single item.
Track 6 endpoints
List the standard times of every track.
The same rows the standards array of /tracks carries, listed on their own so they can be fetched without the tracks around them and filtered by tier or category. Use /standards/achievements instead if you need to know which of them a player has reached.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
track_id
|
integer | No | Only standards of this track |
tier_id
|
integer | No | Only standards of this tier, as listed by /standards |
category_id
|
integer | No | Only standards of this category: 1 (Course) or 2 (Lap) |
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/track-standards
Response
{
"data": [
{
"id": 26,
"track_id": 1,
"tier_id": 2,
"tier_name": "God",
"tier_numeric_value": 0,
"category_id": 1,
"category_name": "Course",
"time": 12400,
"time_formatted": "2:04.00",
"track_name": "Drive Thru Danger"
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 408,
"total_pages": 5
}
}
A real response, trimmed to a single item.
List active tracks across every pool.
The three relation arrays are all included unless turned off. Set the include_* flags to 0 for a lighter payload - each one you drop also drops the lookups behind it.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
|
integer | No | Return only this track |
name
|
string | No | Filter by name or description |
author_id
|
integer | No | Only tracks by this author |
is_active
|
boolean | No | Filter by whether the track is currently playable |
track_type
|
string | No |
Restrict to one pool
One of:
saphi, community, original
|
include_standards
|
boolean | No | Include the standards array (default true) |
include_downloads
|
boolean | No | Include the downloads array (default true) |
include_events
|
boolean | No | Include the events array - timeline events attached to the track (default true) |
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Items per page. Capped at 100 without an API key, 500 with one. |
Example
curl https://www.projectsaphi.com/api/v2/tracks
Response
{
"data": [
{
"id": 1,
"name": "Drive Thru Danger",
"author": "Boxic",
"lap_count": 3,
"difficulty": 9,
"is_active": true,
"track_type_id": 1,
"track_type": "saphi",
"submission_count": 1966,
"average_rating": 8.6,
"rating_count": 5,
"created_at": "2025-10-14 18:35:30",
"standards": [
{
"id": 26,
"track_id": 1,
"tier_id": 2,
"tier_name": "God",
"tier_numeric_value": 0,
"category_id": 1,
"category_name": "Course",
"time": 12400,
"time_formatted": "2:04.00"
}
],
"downloads": [
{
"id": 339,
"track_media_id": 282,
"type": "sca",
"crc32_hash": 4081018681,
"version": "1.0.0",
"modes": [
"time_trial"
],
"file_size": 385228,
"file_size_formatted": "376.2 KB",
"download_url": "/api/v2/tracks/1/downloads/339",
"created_at": "2026-05-21 00:33:34"
}
],
"events": [
{
"id": 1,
"event_type_id": 1,
"event_type_name": "Shortcut Discovery",
"event_type_color": "#8c1818",
"event_type_display_in_sr_history": true,
"track_id": 1,
"track_name": "Drive Thru Danger",
"category_id": null,
"category_name": null,
"name": "MisterM Skip",
"description": null,
"event_date": "2026-02-02 23:00:00",
"created_at": "2026-02-03 18:16:16"
}
]
}
],
"meta": {
"current_page": 1,
"per_page": 100,
"total": 51,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Fetch a single track.
Identical to /tracks filtered to one id, addressable by path.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Track to fetch |
include_standards
|
boolean | No | Include the standards array (default true) |
include_downloads
|
boolean | No | Include the downloads array (default true) |
include_events
|
boolean | No | Include the events array - timeline events attached to the track (default true) |
Example
curl https://www.projectsaphi.com/api/v2/tracks/1
Response
{
"data": [
{
"id": 1,
"name": "Drive Thru Danger",
"author": "Boxic",
"lap_count": 3,
"difficulty": 9,
"is_active": true,
"track_type_id": 1,
"track_type": "saphi",
"submission_count": 1966,
"average_rating": 8.6,
"rating_count": 5,
"created_at": "2025-10-14 18:35:30",
"standards": [
{
"id": 26,
"track_id": 1,
"tier_id": 2,
"tier_name": "God",
"tier_numeric_value": 0,
"category_id": 1,
"category_name": "Course",
"time": 12400,
"time_formatted": "2:04.00"
}
],
"downloads": [
{
"id": 339,
"track_media_id": 282,
"type": "sca",
"crc32_hash": 4081018681,
"version": "1.0.0",
"modes": [
"time_trial"
],
"file_size": 385228,
"file_size_formatted": "376.2 KB",
"download_url": "/api/v2/tracks/1/downloads/339",
"created_at": "2026-05-21 00:33:34"
}
],
"events": [
{
"id": 1,
"event_type_id": 1,
"event_type_name": "Shortcut Discovery",
"event_type_color": "#8c1818",
"event_type_display_in_sr_history": true,
"track_id": 1,
"track_name": "Drive Thru Danger",
"category_id": null,
"category_name": null,
"name": "MisterM Skip",
"description": null,
"event_date": "2026-02-02 23:00:00",
"created_at": "2026-02-03 18:16:16"
}
]
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List a track's downloadable files.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Track to list files for |
type
|
string | No | Filter to one media type, such as lev, vrm or sca |
Example
curl https://www.projectsaphi.com/api/v2/tracks/1/downloads
Response
{
"data": [
{
"id": 339,
"track_media_id": 282,
"type": "sca",
"crc32_hash": 4081018681,
"version": "1.0.0",
"modes": [
"time_trial"
],
"file_size": 385228,
"file_size_formatted": "376.2 KB",
"download_url": "/api/v2/tracks/1/downloads/339",
"created_at": "2026-05-21 00:33:34"
}
],
"meta": {
"current_page": 1,
"per_page": 5,
"total": 5,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Download one track file.
Returns the file itself, not JSON. A whole track is three files and up to 2.5 MB, so this endpoint draws from the separate download budget.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Track the file belongs to |
download_id
path
|
integer | Yes |
File to download, taken from the downloads list
Example: 10
|
Example
curl https://www.projectsaphi.com/api/v2/tracks/1/downloads/10
Fetch a track's community rating summary.
Rating a track is a website action; the API reports the result only.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id
path
|
integer | Yes | Track to fetch ratings for |
Example
curl https://www.projectsaphi.com/api/v2/tracks/1/ratings
Response
{
"data": [
{
"track_id": 1,
"average": 8.6,
"count": 5,
"min": 1,
"max": 10
}
],
"meta": {
"current_page": 1,
"per_page": 1,
"total": 1,
"total_pages": 1
}
}
A real response, trimmed to a single item.
Track Author 1 endpoints
Unlockable 2 endpoints
List the kinds of unlockables that exist.
Example
curl https://www.projectsaphi.com/api/v2/unlockable-types
Response
{
"data": [
{
"id": 3,
"name": "character",
"label": "Character"
}
],
"meta": {
"current_page": 1,
"per_page": 4,
"total": 4,
"total_pages": 1
}
}
A real response, trimmed to a single item.
List available unlockables.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
type
|
string | No | Restrict to one unlockable type |
hidden
|
boolean | No | Include unlockables normally hidden until earned |
progress
|
boolean | No | Include the progress requirements of each unlockable |
Example
curl https://www.projectsaphi.com/api/v2/unlockables
Response
{
"data": [
{
"id": 21,
"key": "digital_n_tropy_skin",
"name": "Digital N. Tropy Skin",
"description": null,
"type": "skin",
"type_label": "Skin",
"hidden": true,
"progress": true
}
],
"meta": {
"current_page": 1,
"per_page": 13,
"total": 13,
"total_pages": 1
}
}
A real response, trimmed to a single item.