{
  "openapi": "3.1.0",
  "info": {
    "title": "Skill Rating API — OpenSkill, Glicko-2 & Elo",
    "version": "1.1.0",
    "summary": "Match results in, skill ratings, leaderboards and win probabilities out. Stateless.",
    "description": "Skill Rating API is a hosted REST API that turns a match log (who played, who won, optional timestamps) into skill ratings and a leaderboard using OpenSkill, Glicko-2 or Elo, and predicts the win probability of any matchup. It handles singles, doubles, teams, free-for-alls and draws, and it is stateless: nothing is stored, you keep the ratings and send them back with your next batch.\n\n**Use it for** game leaderboards and matchmaking, sports clubs and leagues, esports ladders, chess and table-tennis clubs, and pairwise AI-model or A/B comparisons.\n\n**Limits:** 5,000 matches, 2,000 players and 2 MB of JSON per request; 2 to 8 teams of 1 to 8 players per match. Paths have no trailing slash.\n\n**Pricing:** RapidAPI: free for 200 requests a month, PRO $12/month for 10,000 requests, ULTRA $39/month for 100,000 requests; each request can rate up to 5,000 matches. Apify: pay per event, $0.20 per 1,000 rated matches."
  },
  "externalDocs": {
    "description": "Docs, examples and FAQ",
    "url": "https://ranking-api.fly.dev/"
  },
  "servers": [
    {
      "url": "https://ranking-api.fly.dev/api/public/v1",
      "description": "Production (Fly.io, London)"
    }
  ],
  "tags": [
    {
      "name": "Ratings",
      "description": "Rate a match log"
    },
    {
      "name": "Predictions",
      "description": "Win probability and match quality"
    },
    {
      "name": "Reference",
      "description": "Algorithms and parameters"
    }
  ],
  "paths": {
    "/rate": {
      "post": {
        "tags": [
          "Ratings"
        ],
        "operationId": "rateMatches",
        "summary": "Rate a match log with OpenSkill, Glicko-2 or Elo",
        "description": "Replays up to 5,000 matches in order (or by timestamp) and returns every player's rating, a ranked leaderboard and, optionally, each match's expected win probability and rating changes. To continue later, send the returned players object with the next batch: the result is identical to rating the whole history in one call.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RateRequest"
              },
              "example": {
                "algorithm": "openskill",
                "matches": [
                  {
                    "teams": [
                      [
                        "ana"
                      ],
                      [
                        "ben"
                      ]
                    ],
                    "winner": 0,
                    "timestamp": "2026-10-01T18:00:00Z"
                  },
                  {
                    "teams": [
                      [
                        "cat"
                      ],
                      [
                        "dan"
                      ]
                    ],
                    "winner": 1,
                    "timestamp": "2026-10-01T18:30:00Z"
                  },
                  {
                    "teams": [
                      [
                        "ana",
                        "dan"
                      ],
                      [
                        "ben",
                        "cat"
                      ]
                    ],
                    "winner": 0,
                    "timestamp": "2026-10-02T19:00:00Z"
                  },
                  {
                    "teams": [
                      [
                        "ana"
                      ],
                      [
                        "dan"
                      ]
                    ],
                    "ranks": [
                      0,
                      0
                    ],
                    "timestamp": "2026-10-03T18:00:00Z"
                  },
                  {
                    "teams": [
                      [
                        "ben"
                      ],
                      [
                        "cat"
                      ]
                    ],
                    "winner": 0,
                    "weight": 0.5,
                    "timestamp": "2026-10-03T18:45:00Z"
                  },
                  {
                    "teams": [
                      [
                        "ana"
                      ],
                      [
                        "cat"
                      ]
                    ],
                    "winner": 0,
                    "timestamp": "2026-10-04T18:00:00Z"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ratings, leaderboard and per-match history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/RateResult"
                    }
                  }
                },
                "example": {
                  "data": {
                    "algorithm": "openskill",
                    "params": {
                      "mu": 25,
                      "sigma": 8.333333333333334,
                      "beta": 4.166666666666667,
                      "tau": 0.08333333333333333,
                      "idleTau": 0,
                      "bias": 0,
                      "kappa": 0.0001
                    },
                    "players": {
                      "ana": {
                        "mu": 30.5018,
                        "sigma": 7.4836,
                        "games": 4,
                        "display": 8.0511
                      },
                      "ben": {
                        "mu": 22.2753,
                        "sigma": 7.8085,
                        "games": 3,
                        "display": -1.1501
                      },
                      "cat": {
                        "mu": 18.2068,
                        "sigma": 7.5946,
                        "games": 4,
                        "display": -4.5769
                      },
                      "dan": {
                        "mu": 28.9659,
                        "sigma": 7.684,
                        "games": 3,
                        "display": 5.9139
                      }
                    },
                    "leaderboard": [
                      {
                        "rank": 1,
                        "id": "ana",
                        "display": 8.0511,
                        "wins": 3,
                        "losses": 0,
                        "draws": 1
                      },
                      {
                        "rank": 2,
                        "id": "dan",
                        "display": 5.9139,
                        "wins": 2,
                        "losses": 0,
                        "draws": 1
                      },
                      {
                        "rank": 3,
                        "id": "ben",
                        "display": -1.1501,
                        "wins": 1,
                        "losses": 2,
                        "draws": 0
                      },
                      {
                        "rank": 4,
                        "id": "cat",
                        "display": -4.5769,
                        "wins": 0,
                        "losses": 4,
                        "draws": 0
                      }
                    ],
                    "history": [
                      {
                        "match": 0,
                        "expected_win_probability": [
                          0.5,
                          0.5
                        ],
                        "display_delta": {
                          "ana": 3.4377,
                          "ben": -1.8331
                        }
                      },
                      {
                        "match": 1,
                        "expected_win_probability": [
                          0.5,
                          0.5
                        ],
                        "display_delta": {
                          "cat": -1.8331,
                          "dan": 3.4377
                        }
                      },
                      {
                        "match": 2,
                        "expected_win_probability": [
                          0.6488,
                          0.3512
                        ],
                        "display_delta": {
                          "ana": 1.7365,
                          "dan": 1.7365,
                          "ben": -0.9244,
                          "cat": -0.9244
                        }
                      },
                      {
                        "match": 3,
                        "expected_win_probability": [
                          0.5,
                          0.5
                        ],
                        "display_delta": {
                          "ana": 0.7397,
                          "dan": 0.7397
                        }
                      },
                      {
                        "match": 4,
                        "expected_win_probability": [
                          0.5,
                          0.5
                        ],
                        "display_delta": {
                          "ben": 1.6074,
                          "cat": -0.8749
                        }
                      },
                      {
                        "match": 5,
                        "expected_win_probability": [
                          0.6764,
                          0.3236
                        ],
                        "display_delta": {
                          "ana": 2.1372,
                          "cat": -0.9445
                        }
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_teams",
                    "message": "matches[0]: teams must be 2–8 arrays of 1–8 player ids."
                  }
                }
              }
            }
          },
          "401": {
            "description": "No RapidAPI proxy secret (call through RapidAPI)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Call this API through RapidAPI (X-RapidAPI-Proxy-Secret required)."
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid RapidAPI proxy secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "forbidden",
                    "message": "Invalid RapidAPI proxy secret."
                  }
                }
              }
            }
          },
          "413": {
            "description": "Body over 2 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "too_large",
                    "message": "Request body over 2 MB."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/predict": {
      "post": {
        "tags": [
          "Predictions"
        ],
        "operationId": "predictMatchup",
        "summary": "Predict the win probability and quality of a matchup",
        "description": "Returns each team's chance of winning and a 0–1 match-quality score from the players' current ratings. Players you do not pass start at the algorithm default. Use it to pick balanced pairings.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PredictRequest"
              },
              "example": {
                "players": {
                  "ana": {
                    "mu": 29.2,
                    "sigma": 6.1
                  },
                  "cat": {
                    "mu": 23.4,
                    "sigma": 6.5
                  }
                },
                "teams": [
                  [
                    "ana"
                  ],
                  [
                    "cat"
                  ]
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Win probability per team and match quality",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PredictResult"
                    }
                  }
                },
                "example": {
                  "data": {
                    "algorithm": "openskill",
                    "win_probability": [
                      0.6325,
                      0.3675
                    ],
                    "quality": 0.4759
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_teams",
                    "message": "teams: a player appears twice."
                  }
                }
              }
            }
          },
          "401": {
            "description": "No RapidAPI proxy secret (call through RapidAPI)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Call this API through RapidAPI (X-RapidAPI-Proxy-Secret required)."
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid RapidAPI proxy secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "forbidden",
                    "message": "Invalid RapidAPI proxy secret."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/algorithms": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "listAlgorithms",
        "summary": "List rating algorithms and their tunable parameters",
        "responses": {
          "200": {
            "description": "Algorithms with default parameters and allowed ranges",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AlgorithmInfo"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No RapidAPI proxy secret (call through RapidAPI)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Call this API through RapidAPI (X-RapidAPI-Proxy-Secret required)."
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid RapidAPI proxy secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "forbidden",
                    "message": "Invalid RapidAPI proxy secret."
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Algorithm": {
        "type": "string",
        "enum": [
          "openskill",
          "glicko2",
          "elo",
          "custom"
        ],
        "default": "openskill",
        "description": "openskill (default): Weng-Lin Bayesian ratings for teams and free-for-alls, the patent-free TrueSkill alternative. glicko2: rating, deviation and volatility. elo: classic K-factor Elo. custom: Club Points."
      },
      "Params": {
        "type": "object",
        "additionalProperties": {
          "type": "number"
        },
        "description": "Override algorithm parameters, e.g. {\"k\": 24} for Elo. GET /algorithms lists every key with its range."
      },
      "PlayerState": {
        "type": "object",
        "required": [
          "mu",
          "sigma"
        ],
        "description": "A rating you already hold, exactly as a previous /rate response returned it.",
        "properties": {
          "mu": {
            "type": "number",
            "description": "Skill estimate (Elo/Glicko-2: the rating itself)."
          },
          "sigma": {
            "type": "number",
            "minimum": 0,
            "description": "Uncertainty (Glicko-2: rating deviation)."
          },
          "games": {
            "type": "integer",
            "minimum": 0
          },
          "extra": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Algorithm state such as Glicko-2 volatility."
          }
        }
      },
      "Players": {
        "type": "object",
        "additionalProperties": {
          "$ref": "#/components/schemas/PlayerState"
        },
        "description": "Ratings from a previous call, keyed by player id (max 2000). Unknown players start at the algorithm default."
      },
      "Match": {
        "type": "object",
        "required": [
          "teams"
        ],
        "description": "One result. With neither ranks nor winner, team 0 won. A draw is ranks [0, 0].",
        "properties": {
          "teams": {
            "type": "array",
            "minItems": 2,
            "maxItems": 8,
            "description": "2 to 8 teams; each team is a list of 1 to 8 player ids. Singles: one id per team.",
            "items": {
              "type": "array",
              "minItems": 1,
              "maxItems": 8,
              "items": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100
              }
            },
            "examples": [
              [
                [
                  "ana",
                  "ben"
                ],
                [
                  "cat",
                  "dan"
                ]
              ]
            ]
          },
          "winner": {
            "type": "integer",
            "minimum": 0,
            "description": "Index of the winning team (alternative to ranks). Default 0."
          },
          "ranks": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Finishing position per team, lower is better; equal ranks are a draw. For free-for-alls and placings."
          },
          "weight": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 3,
            "default": 1,
            "description": "How much this match counts, e.g. 0.5 for a friendly."
          },
          "timestamp": {
            "type": [
              "string",
              "integer"
            ],
            "description": "When it was played: ISO 8601 or epoch milliseconds. If every match has one, matches are replayed in time order; give it on all matches or none.",
            "examples": [
              "2026-10-01T18:00:00Z"
            ]
          }
        }
      },
      "RateRequest": {
        "type": "object",
        "required": [
          "matches"
        ],
        "properties": {
          "algorithm": {
            "$ref": "#/components/schemas/Algorithm"
          },
          "params": {
            "$ref": "#/components/schemas/Params"
          },
          "players": {
            "$ref": "#/components/schemas/Players"
          },
          "matches": {
            "type": "array",
            "minItems": 1,
            "maxItems": 5000,
            "items": {
              "$ref": "#/components/schemas/Match"
            }
          },
          "history": {
            "type": "boolean",
            "default": true,
            "description": "Include per-match expected win probability and rating change."
          }
        }
      },
      "PlayerResult": {
        "type": "object",
        "required": [
          "mu",
          "sigma",
          "games",
          "display"
        ],
        "properties": {
          "mu": {
            "type": "number"
          },
          "sigma": {
            "type": "number"
          },
          "extra": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "games": {
            "type": "integer"
          },
          "display": {
            "type": "number",
            "description": "The rating to show users (OpenSkill: the conservative μ − 3σ; Elo and Glicko-2: the rating itself)."
          }
        }
      },
      "LeaderboardRow": {
        "type": "object",
        "required": [
          "rank",
          "id",
          "display",
          "wins",
          "losses",
          "draws"
        ],
        "properties": {
          "rank": {
            "type": "integer",
            "minimum": 1
          },
          "id": {
            "type": "string"
          },
          "display": {
            "type": "number"
          },
          "wins": {
            "type": "integer"
          },
          "losses": {
            "type": "integer"
          },
          "draws": {
            "type": "integer"
          }
        }
      },
      "HistoryEntry": {
        "type": "object",
        "required": [
          "match",
          "expected_win_probability",
          "display_delta"
        ],
        "properties": {
          "match": {
            "type": "integer",
            "description": "Index of the match in your request."
          },
          "expected_win_probability": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "description": "Per team, before the match was played."
          },
          "display_delta": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Rating change per player."
          }
        }
      },
      "RateResult": {
        "type": "object",
        "required": [
          "algorithm",
          "params",
          "players",
          "leaderboard"
        ],
        "properties": {
          "algorithm": {
            "$ref": "#/components/schemas/Algorithm"
          },
          "params": {
            "$ref": "#/components/schemas/Params"
          },
          "players": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/PlayerResult"
            },
            "description": "Final ratings. Store this and send it back as players with your next batch."
          },
          "leaderboard": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LeaderboardRow"
            }
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HistoryEntry"
            }
          }
        }
      },
      "PredictRequest": {
        "type": "object",
        "required": [
          "teams"
        ],
        "properties": {
          "algorithm": {
            "$ref": "#/components/schemas/Algorithm"
          },
          "params": {
            "$ref": "#/components/schemas/Params"
          },
          "players": {
            "$ref": "#/components/schemas/Players"
          },
          "teams": {
            "type": "array",
            "minItems": 2,
            "maxItems": 8,
            "description": "2 to 8 teams; each team is a list of 1 to 8 player ids. Singles: one id per team.",
            "items": {
              "type": "array",
              "minItems": 1,
              "maxItems": 8,
              "items": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100
              }
            },
            "examples": [
              [
                [
                  "ana",
                  "ben"
                ],
                [
                  "cat",
                  "dan"
                ]
              ]
            ]
          }
        }
      },
      "PredictResult": {
        "type": "object",
        "required": [
          "algorithm",
          "win_probability",
          "quality"
        ],
        "properties": {
          "algorithm": {
            "$ref": "#/components/schemas/Algorithm"
          },
          "win_probability": {
            "type": "array",
            "items": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            },
            "description": "Per team; sums to 1."
          },
          "quality": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Match quality: 1 = perfectly balanced."
          }
        }
      },
      "AlgorithmInfo": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Algorithm"
          },
          "name": {
            "type": "string"
          },
          "tagline": {
            "type": "string"
          },
          "provenance": {
            "type": "string"
          },
          "default_params": {
            "$ref": "#/components/schemas/Params"
          },
          "params": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "help": {
                  "type": "string"
                },
                "min": {
                  "type": "number"
                },
                "max": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "examples": [
                  "invalid_teams"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}