Skip to content

List a player's rating history

GET
/players/{player_id}/rating-history
curl --request GET \
--url 'https://api.krkplaystennis.example/v1/players/plr_2b7xq9mhk4/rating-history?limit=20&cursor=bWF0XzJ2YjZucDRrdGY' \
--header 'X-API-Key: <X-API-Key>'

Returns every change to a player’s rating or confidence, newest first. Each entry includes the reason for the change. Entries caused by a match also include the inputs to the calculation, so you can reproduce the change by hand.

player_id
required

A player ID. Starts with plr_.

string
/^plr_[a-z0-9]{10}$/

The player ID.

limit
integer
default: 20 >= 1 <= 100

Maximum number of items to return.

cursor
string

The next_cursor value from the previous page. Omit it to get the first page.

Example

bWF0XzJ2YjZucDRrdGY

A page of rating history entries, newest first.

Media typeapplication/json
object
data
required
Array<object>
object
id
required

A rating history entry ID. Starts with rtg_.

string
/^rtg_[a-z0-9]{10}$/
player_id
required

A player ID. Starts with plr_.

string
/^plr_[a-z0-9]{10}$/
occurred_at
required

When the change was applied.

string format: date-time
reason
required
object
code
required

Why the rating or confidence changed:

  • initial_assessment: the starting rating, set from the self-assessed NTRP level.
  • match_result: a rated match. The calculation object has the inputs.
  • inactivity: confidence dropped after a period without rated matches. The rating value doesn’t change.
string
Allowed values: initial_assessment match_result inactivity
match_id
required
One of:

A match ID. Starts with mat_.

string
/^mat_[a-z0-9]{10}$/
explanation
required

A one-sentence explanation of the change, for display to players.

string
value_before
required
One of:

A rating. New players start between 1000 and 1800, based on their self-assessed NTRP level.

integer
value_after
required

A rating. New players start between 1000 and 1800, based on their self-assessed NTRP level.

integer
change
required

value_after minus value_before. null for initial_assessment.

integer | null
confidence_before
required
One of:

How settled a rating is, from 0 to 100. New players start at 20. Each rated match adds 10, up to 95. After 60 days without a rated match, confidence drops by 5 every 30 days, down to 20.

integer
<= 100
confidence_after
required

How settled a rating is, from 0 to 100. New players start at 20. Each rated match adds 10, up to 95. After 60 days without a rated match, confidence drops by 5 every 30 days, down to 20.

integer
<= 100
calculation
required
One of:

The inputs to a match rating change. The change is k_factor × (actual_score - expected_score), rounded to the nearest integer.

object
opponent_id
required

A player ID. Starts with plr_.

string
/^plr_[a-z0-9]{10}$/
opponent_rating
required

A rating. New players start between 1000 and 1800, based on their self-assessed NTRP level.

integer
expected_score
required

The player’s chance of winning before the match, from 0 to 1. It’s 1 / (1 + 10^((opponent_rating - rating) / 400)), rounded to 3 decimal places.

number
<= 1
actual_score
required

1 for a win and 0 for a loss. For a retired match, the share of games the player won, rounded to 3 decimal places. A completed match tiebreak counts as one game.

number
<= 1
k_factor
required

The maximum number of points a rating can move in one match. It’s 48 when confidence is below 40, 32 when confidence is from 40 to 69, and 16 when confidence is 70 or higher.

integer
Allowed values: 48 32 16
next_cursor
required

Pass this value as cursor to get the next page. null means this is the last page.

string | null

Examples

Examplehistory

Ola's history after one rated match

{
"data": [
{
"id": "rtg_6ym2kd9qrz",
"player_id": "plr_2b7xq9mhk4",
"occurred_at": "2026-10-10T20:43:12Z",
"reason": {
"code": "match_result",
"match_id": "mat_4nc8xq2vbt",
"explanation": "Won against Kasia W. (1520). Expected score 0.334, actual score 1. K-factor 48 because confidence was 20."
},
"value_before": 1400,
"value_after": 1432,
"change": 32,
"confidence_before": 20,
"confidence_after": 30,
"calculation": {
"opponent_id": "plr_9fz3tr6wn1",
"opponent_rating": 1520,
"expected_score": 0.334,
"actual_score": 1,
"k_factor": 48
}
},
{
"id": "rtg_1pt8xv4hcn",
"player_id": "plr_2b7xq9mhk4",
"occurred_at": "2026-09-02T08:15:00Z",
"reason": {
"code": "initial_assessment",
"match_id": null,
"explanation": "Starting rating from a self-assessed NTRP level of 3.5."
},
"value_before": null,
"value_after": 1400,
"change": null,
"confidence_before": null,
"confidence_after": 20,
"calculation": null
}
],
"next_cursor": null
}

The request is malformed, for example a query parameter has the wrong type.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

ExampleinvalidRequest

Limit out of range

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The request has 1 invalid parameter.",
"instance": "/v1/players",
"errors": [
{
"pointer": "#/limit",
"detail": "limit must be between 1 and 100."
}
]
}

The credentials are missing, invalid, or expired.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

Exampleunauthenticated

Missing API key

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#unauthenticated",
"title": "Authentication required",
"status": 401,
"detail": "Send a valid API key in the X-API-Key header.",
"instance": "/v1/players/plr_2b7xq9mhk4"
}

No resource exists with this ID.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

ExamplenotFound

Unknown player ID

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#not-found",
"title": "Not found",
"status": 404,
"detail": "No player exists with the ID plr_0000000000.",
"instance": "/v1/players/plr_0000000000"
}

You sent too many requests. Wait for the number of seconds in Retry-After, then retry.

Media typeapplication/problem+json

An error, as RFC 9457 problem details.

object
type
required

A URL that identifies the error type and links to its documentation.

string format: uri
title
required

A short summary of the error type. It’s the same for every error of this type.

string
status
required

The HTTP status code.

integer
>= 400 <= 599
detail
required

An explanation of this occurrence of the error.

string
instance

The request path that caused the error.

string
errors

One item per invalid field or parameter. Only for invalid-request, invalid-score, and validation-failed.

Array<object>
object
pointer
required

A JSON Pointer to the invalid field in the request body, or #/ followed by the name of an invalid query parameter.

string
detail
required

What’s wrong with the field and how to fix it.

string

Examples

ExamplerateLimited

Rate limit reached

{
"type": "https://saverio-cirillo.com/krk-plays-tennis-api/errors/#rate-limited",
"title": "Rate limit reached",
"status": 429,
"detail": "This API key is limited to 60 requests per minute. Retry after 30 seconds.",
"instance": "/v1/matches"
}
Retry-After
integer
>= 1

Number of seconds to wait before you retry.

Example
30

© 2026 Saverio Cirillo · KRKplaysTennis is a fictional API · Built with Starlight