curl --request GET \
--url https://api.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart', 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.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body[
{
"data_points": 123,
"timestamp": 123,
"spot_pnl": 123,
"spot_value": 123,
"vault_equity": 123
}
]Get portfolio chart data
Returns time series data for PnL or account value over specified time range. PnL = Cumulative Realized PnL from completed trades. Account Value = Initial Deposits - Withdrawals + Realized PnL (excludes unrealized PnL).
curl --request GET \
--url https://api.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart', 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.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.mainnet.aptoslabs.com/decibel/api/v1/portfolio_chart")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body[
{
"data_points": 123,
"timestamp": 123,
"spot_pnl": 123,
"spot_value": 123,
"vault_equity": 123
}
]Authorizations
Bearer token from Geomi. See Authentication for setup instructions.
Query Parameters
User account address (user query alias is also accepted)
Time range for chart data
24h, 7d, 30d, 90d, all Data type: pnl or account_value
pnl, account_value Response
Portfolio chart data retrieved successfully
Spot PnL (unrealized + realized) at this snapshot. A sidecar like
spot_value, so data_points stays perp-only and the caller composes
the combined cut. 0 for snapshots predating the columns (NULL in
ClickHouse); None for the account_value data_type.
Spot holdings value (USD) at this snapshot, from spot_value_snapshots.
NOT included in data_points: data_points stays the canonical
perp Portfolio Value (perp equity + free vault equity) because vault
NAV, the leaderboard, and the DLP oracle bind to that meaning. The
"Perps + Spot" cut is data_points + spot_value, composed by the
caller under a distinct name. 0 when no spot value is recorded at
this timestamp (including all history before the spot recorder
deployed — same JOIN-miss semantics as vault_equity); None for
the PnL data_type.
Vault equity: user's proportional share of vault NAV(s). None for users with no vault deposits, or for PnL data_type.

