curl --request GET \
--url https://api.dexploit.dev/wallets/{w}/pnl/basic \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.dexploit.dev/wallets/{w}/pnl/basic"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.dexploit.dev/wallets/{w}/pnl/basic', 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://api.dexploit.dev/wallets/{w}/pnl/basic",
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>"
],
]);
$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://api.dexploit.dev/wallets/{w}/pnl/basic"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<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://api.dexploit.dev/wallets/{w}/pnl/basic")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dexploit.dev/wallets/{w}/pnl/basic")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"wallet": "<string>",
"window": "<string>",
"realized_sol": 123,
"unrealized_sol": 123,
"total_volume_sol": 123,
"win_rate": 123,
"sell_count": 123,
"warning": "<string>",
"realized_usd": 123,
"unrealized_usd": 123,
"oracle": {},
"unrealized_price_basis": "spot",
"priced_position_count": 123,
"unpriced_position_count": 123,
"unpriced_cost_basis_sol": 123,
"unpriced_cost_basis_usd": 123,
"spot_price_max_age_secs": 123,
"coverage_position_set": "onchain_held",
"unrealized_floor_sol": 123,
"unrealized_floor_usd": 123,
"reconciled": true,
"reconciliation_source": "<string>"
}{
"success": false,
"error": {
"code": "INVALID_PARAM",
"message": "use pair_address; see /api/v1/pairs?token_address=X to discover pools"
}
}{
"success": false,
"error": {
"code": "INVALID_PARAM",
"message": "use pair_address; see /api/v1/pairs?token_address=X to discover pools"
}
}{
"success": false,
"error": {
"code": "INVALID_PARAM",
"message": "use pair_address; see /api/v1/pairs?token_address=X to discover pools"
}
}FIFO realized and unrealized PnL
Returns FIFO realized + unrealized PnL for the wallet over the specified window. Lot-based matching: buy lots pushed in time order, each sell matches against the earliest open lots and realizes proceeds − cost_matched.
The response always includes a warning field:
basic FIFO realized PnL; full cost-basis (cross-wallet, tax-lot, transfer-aware) ships in Phase 8
This is intentional — FIFO is a good approximation for single-wallet trading but is not suitable for tax calculations or cross-wallet strategies. Full PnL ships in Phase 8.
Response cached 60 seconds.
See Wallet portfolio.
curl --request GET \
--url https://api.dexploit.dev/wallets/{w}/pnl/basic \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.dexploit.dev/wallets/{w}/pnl/basic"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.dexploit.dev/wallets/{w}/pnl/basic', 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://api.dexploit.dev/wallets/{w}/pnl/basic",
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>"
],
]);
$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://api.dexploit.dev/wallets/{w}/pnl/basic"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<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://api.dexploit.dev/wallets/{w}/pnl/basic")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dexploit.dev/wallets/{w}/pnl/basic")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"wallet": "<string>",
"window": "<string>",
"realized_sol": 123,
"unrealized_sol": 123,
"total_volume_sol": 123,
"win_rate": 123,
"sell_count": 123,
"warning": "<string>",
"realized_usd": 123,
"unrealized_usd": 123,
"oracle": {},
"unrealized_price_basis": "spot",
"priced_position_count": 123,
"unpriced_position_count": 123,
"unpriced_cost_basis_sol": 123,
"unpriced_cost_basis_usd": 123,
"spot_price_max_age_secs": 123,
"coverage_position_set": "onchain_held",
"unrealized_floor_sol": 123,
"unrealized_floor_usd": 123,
"reconciled": true,
"reconciliation_source": "<string>"
}{
"success": false,
"error": {
"code": "INVALID_PARAM",
"message": "use pair_address; see /api/v1/pairs?token_address=X to discover pools"
}
}{
"success": false,
"error": {
"code": "INVALID_PARAM",
"message": "use pair_address; see /api/v1/pairs?token_address=X to discover pools"
}
}{
"success": false,
"error": {
"code": "INVALID_PARAM",
"message": "use pair_address; see /api/v1/pairs?token_address=X to discover pools"
}
}Authorizations
Preferred for swaps-api endpoints (/swaps/*, /stats/*, /trending, /pool-events).
Path Parameters
Wallet address (base58, 32–44 characters).
Query Parameters
Time window to compute PnL over.
30d, 90d, 1y, all Filter to a single DEX by integer ID. See DexId for the mapping.
Response
FIFO PnL summary
FIFO realized + unrealized PnL for a wallet. Includes a warning pointing to Phase 8 for full multi-lot PnL.
The time window used (all, 30d, 90d, 1y).
Total FIFO realized PnL in SOL across all closed positions.
Unrealized PnL in SOL on the remaining open lots the wallet is confirmed to still HOLD on chain, summed over those with a live spot price. The FIFO replay set is reconciled against on-chain holdings first, so bags exited outside the indexed venues do not contribute; coverage_position_set says whether that reconciliation succeeded, and unpriced_* say what the price window excluded.
Total SOL volume traded.
Fraction of sells that were profitable (0.0–1.0).
Number of sell transactions included.
Always present. Explains that this is basic FIFO PnL and that full cost-basis ships in Phase 8.
Realized PnL in USD via Dexploit's SOL/USD rate. null when the rate is unavailable.
Unrealized PnL in USD via Dexploit's SOL/USD rate. null when the rate is unavailable.
SOL/USD rate snapshot used for USD conversion.
How much of the open book unrealized_sol covers. spot = every counted position had a live spot price; partial = some were excluded; none = nothing was priced, so unrealized_sol is 0 for lack of data, NOT because the wallet is flat. Distinct from unrealized_basis, which answers whether the whole position LIST was looked at.
spot, partial, none Open positions that had a live spot price and are therefore included in unrealized_sol.
Open positions with NO live spot price inside spot_price_max_age_secs. They are EXCLUDED from unrealized_sol — not valued at zero and not assumed break-even, both of which would be wrong in a direction the caller cannot detect.
Total cost basis, in SOL, of the excluded positions — the magnitude of what unrealized_sol does not cover.
unpriced_cost_basis_sol in USD via Dexploit's SOL/USD rate. null when the rate is unavailable.
Maximum age of a trade for it to count as a live spot price (seconds).
WHICH set the coverage counts describe. onchain_held = every counted position was confirmed still held on chain. replay_unreconciled = on-chain truth was unavailable, so the counts come from the raw replay/ledger set and include bags the wallet may already have exited; treat them as an UPPER bound on the open book. This route serves only these two values; onchain_held_basis_drift (see PnlV2Summary) is a /v2 value.
onchain_held, replay_unreconciled Lower bound on the wallet's true unrealized PnL in SOL: unrealized_sol with every unpriced bag valued at zero. Non-null only when coverage_position_set is onchain_held; with replay_unreconciled the priced side can contain phantom gains, so no bound holds and this is null.
unrealized_floor_sol in USD via Dexploit's SOL/USD rate. null when there is no floor or the rate is unavailable.
Whether the open-position set was checked against on-chain holdings. false ⇒ the raw FIFO replay set was served and coverage_position_set is replay_unreconciled.
What supplied on-chain truth (shyft), or unavailable when the response is not reconciled.

