rlapi
rlapi copied to clipboard
Reverse engineered Rocket League internal HTTP & WebSocket API
rlapi
ITEM SHOP DEMO
rlapi is a reverse engineered collection of Rocket League's internal APIs with a Go SDK. It provides a full end-to-end flow, from authentication to accessing the item shop, player stats, inventory, match history, replays, and more. This repository also contains resources for reverse engineering and analyzing Rocket League network traffic, serving as a foundation for further exploration. Not all endpoints are fully documented—do not ask about specific ones, as I probably don't know.
Contributions
All contributions are welcome! If you discover new endpoints, extend the Go SDK, or add additional functionality, please submit a PR.
Getting Started
Refer to the godoc for detailed documentation on the Go SDK.
Comprehensive examples are available in the examples directory.
Usage
go get github.com/dank/rlapi
Authentication
Rocket League authentication always goes through Epic Online Services (EOS), either via the Epic Games Store (EGS) or by exchanging a Steam session ticket for an EOS token.
This library provides full end-to-end authentication via EGS. Steam login and ticket generation are out of scope, but a method is provided to exchange a Steam session ticket for an EOS token, and users can leverage external libraries such as steam-user or Steamworks to obtain the ticket.
Intercepting Requests
Traditional proxy tools like Fiddler don't work with Rocket League due to certificate pinning.
To intercept traffic, we use Frida dynamic instrumentation to hook curl functions at runtime, disabling SSL verification and redirecting API traffic to a local MITM server.
MITM Server
The MITM proxy forwards both HTTP and WebSocket traffic to the official servers while intercepting and logging requests and responses. Authentication responses are rewritten so the WebSocket URL points to the local server.
[!NOTE] Refer to the respective READMEs in
tools/frida/andtools/mitm/directories for more details and usage instructions.
Reconstructing Requests
Authentication
Rocket League initially establishes a connection with the HTTP API before transitioning to a WebSocket connection. The client sends an EOS access token via HTTP and receives session credentials, the WebSocket endpoint URL, and any tokens required for further communication. The client then connects to the WebSocket using these tokens, allowing all subsequent API calls to occur over a persistent WebSocket connection.
Signing
All API requests and responses must include a PsySig header containing a Base64-encoded HMAC-SHA256 signature. The signing keys were reverse engineered from the game binary and are XOR'd with a 4-byte pattern. To decrypt:
# Raw IDA dump
data = [0x36, 0xEA, 0x37, 0x0C, ...] # 36 bytes total
key_bytes = [data[i] ^ data[(i % 4) + 32] for i in range(32)]
-
Request Key:
c338bd36fb8c42b1a431d30add939fc7- Format:
HMAC-SHA256(key, "-" + <request body>)
- Format:
-
Response Key:
3b932153785842ac927744b292e40e52- Format:
HMAC-SHA256(key, PsyTime + "-" + <response body>)
- Format:
Request Protocol
WebSocket Schema
WebSocket messages require a custom HTTP-like schema with headers and JSON body:
PsyService: Matchmaking/StartMatchmaking v2
PsyRequestID: PsyNetMessage_X_1
PsyToken: authentication-token
PsySessionID: session-identifier
PsySig: request-signature
PsyBuildID: 151471783
User-Agent: RL Win/250811.43331.492665 gzip
PsyEnvironment: Prod
{"playlist_id": 10, "region": "USE"}
The message format is: headers (each ending with \r\n) followed by \r\n\r\n separator, then JSON body.
Required Headers
(Values may be outdated)
| Name | HTTP | WS | Value | |
|---|---|---|---|---|
| PsyService | ✅ | WS event name | ||
| PsyRequestID | ✅ | ✅ | PsyNetMessage_X_0 | Incrementing idempotency key (also for request/response matching |
| PsyToken | ✅ | WS auth token | ||
| PsySessionID | ✅ | WS session identifier | ||
| PsySig | ✅ | ✅ | Base64-encoded HMAC signature of the body | |
| PsyBuildID | ✅ | ✅ | 151471783 | Varies by build |
| PsyEnvironment | ✅ | ✅ | Prod | Varies by build |
| FeatureSet | ✅ | PrimeUpdate55_1 | Varies by build | |
| User-Agent | ✅ | RL Win/250811.43331.492665 gzip | Varies by build | |
| User-Agent | ✅ | RL Win/250811.43331.492665 gzip (x86_64-pc-win32) curl-7.67.0 Schannel | Varies by build | |
| Content-Type | ✅ | application/x-www-form-urlencoded | JSON body but form type |
HTTP Endpoints
The HTTP API is used only for authentication and to bootstrap the WebSocket connection. Unlike before, there is no way to "downgrade" the WebSocket connection to HTTP (AFAIK).
The base URL for HTTP requests is: https://api.rlpp.psynet.gg/rpc/.
POST Auth/AuthPlayer/v2
[!NOTE] When the API refers to a "Player ID", it typically expects the following format:
<platform>|<platform-specific account ID>|0For example, on Steam:
Steam|76561197960287930|0. The final 0 is always included, though its purpose is unknown.
Request
PsyRequestID: PsyNetMessage_X_0
PsyBuildID: 151471783
PsyEnvironment: Prod
User-Agent: User-Agent: RL Win/250811.43331.492665 gzip (x86_64-pc-win32) curl-7.67.0 Schannel
PsySig: <HMAC signature>
Content-Type: application/x-www-form-urlencoded
{
"Platform": "<platform>",
"PlayerName": "<player name>",
"PlayerID": "<platform account ID>",
"Language": "INT",
"AuthTicket": "<EOS access token>",
"BuildRegion": "",
"FeatureSet": "PrimeUpdate55_1", // varies by build
"Device": "PC",
"LocalFirstPlayerID": "<player ID>",
"bSkipAuth": false,
"bSetAsPrimaryAccount": true,
"EpicAuthTicket": "<EOS auth ticket>",
"EpicAccountID": "<Epic account ID>"
}
Response
{
"Result": {
"IsLastChanceAuthBan": false,
"VerifiedPlayerName": "<player name>",
"UseWebSocket": true, // forcing this to false doesn't do anything
"PerConURL": "wss://...", // unused / legacy?
"PerConURLv2": "wss://...", // url for ws connection
"PsyToken": "<auth token>", // used by subsequent ws requests
"SessionID": "<session id>", // used by subsequent ws requests
"CountryRestrictions": []
}
}
WebSocket Events
[!NOTE] For complete request/response schema definitions, refer to the godoc documentation instead.
The following is an incomplete list of WebSocket events, some events may be undocumented or partially understood.
The WebSocket endpoint URL is returned by the authentication endpoint, it is currently: wss://ws.rlpp.psynet.gg/ws/gc2.
- Challenges
- Challenges/CollectReward v1
- Challenges/FTECheckpointComplete v1
- Challenges/FTEGroupComplete v1
- Challenges/GetActiveChallenges v1
- Challenges/PlayerProgress v1
- Clubs
- Clubs/AcceptClubInvite v2
- Clubs/CreateClub v1
- Clubs/GetClubDetails v1
- Clubs/GetClubInvites v1
- Clubs/GetClubTitleInstances v1
- Clubs/GetPlayerClubDetails v2
- Clubs/GetStats v1
- Clubs/InviteToClub v4
- Clubs/LeaveClub v1
- Clubs/RejectClubInvite v1
- Clubs/UpdateClub v2
- Drop
- Drop/GetTradeInFilters v1
- GameServer
- GameServer/GetClubPrivateMatches v1
- GameServer/GetGameServerPingList v2
- Matches
- Matches/GetMatchHistory v1
- Matchmaking
- Matchmaking/PlayerCancelMatchmaking v1
- Matchmaking/PlayerSearchPrivateMatch v1
- Matchmaking/StartMatchmaking v2
- Microtransaction
- Microtransaction/ClaimEntitlements v2
- Microtransaction/GetCatalog v1
- Microtransaction/StartPurchase v1
- Party
- Party/ChangePartyOwner v1
- Party/CreateParty v1
- Party/GetPlayerPartyInfo v1
- Party/JoinParty v1
- Party/KickPartyMembers v1
- Party/LeaveParty v1
- Party/SendPartyChatMessage v1
- Party/SendPartyInvite v2
- Party/SendPartyJoinRequest v1
- Party/SendPartyMessage v1
- Players
- Players/GetBanStatus v3
- Players/GetCreatorCode v1
- Players/GetProfile v1
- Players/GetXP v1
- Players/Report v4
- Playlists
- Playlists/GetActivePlaylists v1
- Population
- Population/GetPopulation v1
- Population/UpdatePlayerPlaylist v1
- Products
- Products/CrossEntitlement/GetProductStatus v1
- Products/GetContainerDropTable v2
- Products/GetPlayerProducts v2
- Products/TradeIn v2
- Products/UnlockContainer v2
- Regions
- Regions/GetSubRegions v1
- Reservations
- Reservations/JoinMatch v1
- RocketPass
- RocketPass/GetPlayerInfo v2
- RocketPass/GetPlayerPrestigeRewards v1
- RocketPass/GetRewardContent v1
- Shops
- Shops/GetPlayerWallet v1
- Shops/GetShopCatalogue v2
- Shops/GetShopNotifications v1
- Shops/GetStandardShops v1
- Skills
- Skills/GetPlayerSkill v1
- Skills/GetPlayersSkills v1
- Skills/GetSkillLeaderboard v1
- Skills/GetSkillLeaderboardRankForUsers v1
- Skills/GetSkillLeaderboardValueForUser v1
- Stats
- Stats/GetStatLeaderboard v1
- Stats/GetStatLeaderboardRankForUsers v1
- Stats/GetStatLeaderboardValueForUser v1
- Tournaments
- Tournaments/Registration/RegisterTournament v1
- Tournaments/Registration/UnsubscribeTournament v1
- Tournaments/Search/GetPublicTournaments v1
- Tournaments/Search/GetSchedule v1
- Tournaments/Status/GetCycleData v1
- Tournaments/Status/GetScheduleRegion v1
- Tournaments/Status/GetTournamentSubscriptions v1
- Training
- Training/BrowseTrainingData v1
- Training/GetTrainingMetadata v1
- Users
- Users/CanShowAvatar v1
- Misc
- DSR/RelayToServer v1
- Filters/FilterContent v1
- Metrics/RecordMetrics v1
- Party/System
- PsyPing