Get merged PnL data for a user across one or more wallets/providers
Fetches and merges PnL records/summary across the wallets supplied for this user via the general-purpose PnL path — not the real-time pipeline used by /pnl/hover and /pnl/wallet-totals. At least one of polymarket_wallet, kalshi_wallet, or hyperliquid_wallet is required — the request is rejected with 400 if none are supplied. polymarket_wallet and hyperliquid_wallet must be valid Ethereum addresses (normalized/lower-cased); kalshi_wallet is treated as an opaque user-id string (Kalshi is a CEX with internal, non-address identifiers). provider (repeatable query param, up to 16 items) filters the merged result down to specific provider(s); when omitted, all providers implied by the supplied wallets are used. Kalshi and Hyperliquid PnL are served only through this aggregate endpoint; neither is available from /pnl/hover or /pnl/wallet-totals. Hyperliquid open-position and unrealized PnL use HIP-4 balances and mark prices, while realized PnL and fee fields are currently zero. The caller-supplied {user_id} MUST match the authenticated principal’s own id (JWT sub, or user_id for API-key auth) — mismatches are rejected with 403; this endpoint can only ever return the caller’s own PnL. limit/offset paginate the merged record list; internally up to min(limit + offset, 1000) records per provider are fetched, so total/has_more in the response describe the page relative to that ceiling, not an authoritative full-history count. For API-key callers, each requested provider must additionally have its API access enabled; JWT/admin callers are exempt.
curl --request GET \
--url https://data.kairos.trade/pnl/{user_id} \
--header 'X-Api-Key: <api-key>' \
--header 'X-Api-Secret: <api-key>' \
--header 'X-Client-Id: <api-key>'import requests
url = "https://data.kairos.trade/pnl/{user_id}"
headers = {
"X-Client-Id": "<api-key>",
"X-Api-Key": "<api-key>",
"X-Api-Secret": "<api-key>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {
'X-Client-Id': '<api-key>',
'X-Api-Key': '<api-key>',
'X-Api-Secret': '<api-key>'
}
};
fetch('https://data.kairos.trade/pnl/{user_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://data.kairos.trade/pnl/{user_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-Api-Key: <api-key>",
"X-Api-Secret: <api-key>",
"X-Client-Id: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://data.kairos.trade/pnl/{user_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Client-Id", "<api-key>")
req.Header.Add("X-Api-Key", "<api-key>")
req.Header.Add("X-Api-Secret", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://data.kairos.trade/pnl/{user_id}")
.header("X-Client-Id", "<api-key>")
.header("X-Api-Key", "<api-key>")
.header("X-Api-Secret", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://data.kairos.trade/pnl/{user_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Client-Id"] = '<api-key>'
request["X-Api-Key"] = '<api-key>'
request["X-Api-Secret"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"user_id": "usr_9f3c2a1b",
"summary": {
"user_id": "usr_9f3c2a1b",
"total_realized_pnl": 1875.1,
"total_fees": 18.2,
"total_cost_basis": 12400,
"total_winning_positions": 25,
"total_losing_positions": 11,
"by_exchange": {}
},
"records": [
{
"provider": "polymarket",
"market_id": "0xabc123condition",
"timestamp": "2026-07-20T09:14:02+00:00",
"realized_pnl": 24.15,
"fees": 0.35
}
],
"filters_applied": {},
"total": 143,
"has_more": false
}{
"detail": "Not authenticated"
}{
"detail": [
{}
]
}{
"error": "Rate limit exceeded: 100 per 1 minute"
}{
"detail": "Invalid provider"
}Authorizations
Credential client id (kairos_ck_...). Must be sent together with X-Api-Key and X-Api-Secret.
64-char hex API key.
64-char hex API secret.
Path Parameters
Kairos internal user id. Must match the authenticated caller's own id.
128Query Parameters
Polymarket (Ethereum) wallet address.
128Kalshi user ID (opaque internal identifier, not a wallet address).
128Hyperliquid EVM address (HIP-4 positions key off this same deposit/trade address).
128Filter merged PnL to these provider(s). Repeatable query param.
16Records per page.
1 <= x <= 500Records to skip.
0 <= x <= 10000Response
Merged PnL summary and records for the user.
Full merged PnL response for a user, across the requested wallets/providers.
"usr_9f3c2a1b"
Merged PnL summary across all providers/wallets supplied for a user.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Echoes the effective filters used to build this response (wallets, providers, etc.).
Count of merged records the aggregator returned before pagination slicing, bounded by the per-provider fetch ceiling (min(limit+offset, 1000)) — not an authoritative full-history count.
143
True only when the aggregator definitively saw more records than this page returned.
Was this page helpful?
curl --request GET \
--url https://data.kairos.trade/pnl/{user_id} \
--header 'X-Api-Key: <api-key>' \
--header 'X-Api-Secret: <api-key>' \
--header 'X-Client-Id: <api-key>'import requests
url = "https://data.kairos.trade/pnl/{user_id}"
headers = {
"X-Client-Id": "<api-key>",
"X-Api-Key": "<api-key>",
"X-Api-Secret": "<api-key>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {
'X-Client-Id': '<api-key>',
'X-Api-Key': '<api-key>',
'X-Api-Secret': '<api-key>'
}
};
fetch('https://data.kairos.trade/pnl/{user_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://data.kairos.trade/pnl/{user_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-Api-Key: <api-key>",
"X-Api-Secret: <api-key>",
"X-Client-Id: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://data.kairos.trade/pnl/{user_id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Client-Id", "<api-key>")
req.Header.Add("X-Api-Key", "<api-key>")
req.Header.Add("X-Api-Secret", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://data.kairos.trade/pnl/{user_id}")
.header("X-Client-Id", "<api-key>")
.header("X-Api-Key", "<api-key>")
.header("X-Api-Secret", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://data.kairos.trade/pnl/{user_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Client-Id"] = '<api-key>'
request["X-Api-Key"] = '<api-key>'
request["X-Api-Secret"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"user_id": "usr_9f3c2a1b",
"summary": {
"user_id": "usr_9f3c2a1b",
"total_realized_pnl": 1875.1,
"total_fees": 18.2,
"total_cost_basis": 12400,
"total_winning_positions": 25,
"total_losing_positions": 11,
"by_exchange": {}
},
"records": [
{
"provider": "polymarket",
"market_id": "0xabc123condition",
"timestamp": "2026-07-20T09:14:02+00:00",
"realized_pnl": 24.15,
"fees": 0.35
}
],
"filters_applied": {},
"total": 143,
"has_more": false
}{
"detail": "Not authenticated"
}{
"detail": [
{}
]
}{
"error": "Rate limit exceeded: 100 per 1 minute"
}{
"detail": "Invalid provider"
}
