Solana Wallet PnL API: This repository demonstrates how to use the Vybe Solana wallet PnL APIs to fetch, explore, and analyze per-wallet profit and loss (PnL) together with related wallets ranked by realized PnL, including trades, program labels, and token metadata for SPL and Token-2022 activity. It ships as a production-ready Node.js backend and browser UI so you can search wallets (address or partial name via ilikeFilter), load wallet-scoped PnL, and browse the related-wallets leaderboard with the same parameters you would send to Vybe. Use this project as a reference implementation or starter kit for building data products such as wallet analytics dashboards, PnL leaderboards, related-wallet discovery, and more.
Try the live demo: https://solana-wallet-pnl-profit-and-loss-api.vybenetwork.com
- Node.js ≥ 20 (LTS recommended)
- npm ≥ 10 (or equivalent)
Get from clone to running app in a few commands:
git clone https://github.com/vybenetwork/solana-wallet-pnl-profit-and-loss-api.git
cd solana-wallet-pnl-profit-and-loss-api
npm install
cp .env.example .env
# Edit .env and set VYBE_API_KEY=your_api_key_here
npm startThen open http://localhost:3000, enter a wallet address or name (mapped to Vybe ilikeFilter when not a full address), tune resolution and sort controls if needed, and click Load wallet PnL to fetch wallet PnL and related wallets.
| Variable | Required | Description | Example |
|---|---|---|---|
VYBE_API_KEY |
Yes | Vybe API key used for all Vybe requests | your_api_key_here |
SOLANA_RPC_URL |
No | RPC endpoint for Metaplex symbol lookup (token-symbol fallback) | https://api.mainnet-beta.solana.com |
PORT |
No | HTTP server port | 3000 |
TUNNEL |
No | Set to 1 to run behind a Cloudflare Tunnel |
1 |
TRADES_LOG |
No | Set to 1 to append /api/trades request lines to trades-requests.log |
0 |
Get your API key at https://vybe.fyi/api-pricing.
- Wallet PnL and related-wallets proxy
- Express server that proxies Vybe:
GET /v4/wallets/{ownerAddress}/pnl(per-wallet token-level PnL for a resolution window)GET /v4/wallets/top-traders(related wallets / leaderboard formintAddressorilikeFilter)GET /v4/tokens/{mintAddress}/top-pnl-traders(token-scoped top PnL traders; used when the UI is in token-oriented flows)GET /v4/trades(supporting trade context where the UI requests it)GET /v4/programs/labeled-program-accounts(program labels)GET /v4/tokens/{mintAddress}(token metadata for optional token panels)
- Express server that proxies Vybe:
- Wallet PnL web UI
- Single-page GUI (no frameworks) built from
src/frontend/app.tsintopublic/app.js(npm startrunsprestart→npm run build:frontend). - Realtime flow: wallet search, resolution (1d / 7d / 30d), optional
mintAddressfilter on PnL, sort field and direction, pagination, and a Related wallets grid driven by top-traders.
- Single-page GUI (no frameworks) built from
- Remote filters (Vybe query params)
- Controls at the top and second row map directly to Vybe query params (
ilikeFilter,resolution,label,sortByAsc/sortByDesc,limit,page, optionalmintAddresson wallet PnL).
- Controls at the top and second row map directly to Vybe query params (
- Retries and errors
- Shared HTTP client (
src/api/client.ts) with timeouts, retries, and human-readable error messages for failed Vybe calls.
- Shared HTTP client (
- Related wallets / top traders (
GET /v4/wallets/top-traders): - Wallet PnL (
GET /v4/wallets/{ownerAddress}/pnl):- https://docs.vybenetwork.com/reference (wallet PnL lives alongside wallet endpoints in the Vybe reference)
- Trade data (
GET /v4/trades): - Token details (
GET /v4/tokens/{mintAddress}): - Token top PnL traders (
GET /v4/tokens/{mintAddress}/top-pnl-traders): - Labeled programs (
GET /v4/programs/labeled-program-accounts):
Wallet-level PnL and related-wallet discovery are useful for:
- Trader intelligence: see realized and unrealized PnL, trade counts, and win rate for a wallet over a chosen window.
- Network effects: surface wallets with similar behavior or peer performance via top traders for the same filter and resolution.
- Product UX: ship a credible wallet dashboard without operating your own indexer—Vybe aggregates trades and PnL across Solana venues.
This repo shows how to wire a practical wallet explorer on top of Vybe’s wallet PnL and top-traders endpoints.
The wallet UI is implemented in src/frontend/app.ts and compiled to public/app.js via npm run build:frontend (also run automatically before npm start).
-
Remote filters (wallet + endpoint row)
- Endpoint mode: Realtime vs Historical switch (Historical is disabled in the current UI until the timeseries section ships).
- Wallet address or name (
ilikeFilter/ owner resolution), Resolution (1d, 7d, 30d), Wallet PnL sort and related controls that map to Vybe query params.
-
Wallet PnL summary and token rows
- After Load wallet PnL, the UI renders wallet PnL metadata, per-token metrics (where returned), charts/cards depending on payload, and links or copy-friendly addresses.
-
Related wallets (top traders)
- Grid of wallets returned by
GET /v4/wallets/top-tradersfor the same scope, sorted by realized PnL for the selected resolution.
- Grid of wallets returned by
-
Second-row controls (pagination and filters)
- Wallet traders label (
label), optional Wallet PnL mint (mintAddress), Sort direction, limit, page, and loading indicator beside Load wallet PnL.
- Wallet traders label (
-
Token stats & token-mode panels (when enabled in the build)
- Optional sections for token metadata, supply charts, and token top PnL traders—wired to the same proxy routes when those panels are visible.
The top of the UI controls the requests sent to the API:
- Wallet search — full address or partial name; forwarded as
mintAddressorilikeFilterper Vybe rules for top-traders and as the owner for wallet PnL. - Resolution —
1d,7d, or30dfor both wallet PnL and related wallets where applicable. - Wallet PnL sort —
sortByDesc/sortByAscfield for per-wallet token rows (e.g.realizedPnlUsd). - Label —
label=all|kolstyle filter for top-traders when exposed in the UI. - limit / page — pagination for wallet PnL and related wallet queries.
These map to the proxy routes in src/server.ts and are forwarded to Vybe with your VYBE_API_KEY.
After the primary row, optional controls refine the same Vybe calls:
- Wallet traders label — forwarded as
labelon top-traders when Vybe expects it (UI omits invalid combinations such aslabel=allwhere the API rejects it). - Wallet PnL mint — optional
mintAddressfilter onGET /v4/wallets/{owner}/pnl. - Sort direction, limit, page — standard pagination and sort order for wallet PnL and related lists.
Changing these and clicking Load wallet PnL refetches from the proxy.
The UI may show a Historical path for wallet PnL timeseries in the future. In the current repository, Historical is disabled in the browser while that section is implemented; the Express server focuses on realtime wallet PnL (GET /v4/wallets/{ownerAddress}/pnl) and related wallets (GET /v4/wallets/top-traders). When Historical ships, this README will document the matching proxy route (for example GET /api/wallets/:owner/pnl-ts → Vybe pnl-ts) alongside the UI.
The Express server in src/server.ts exposes:
GET /api/wallets/top-traders- Proxies to Vybe
GET /v4/wallets/top-traderswith query params:mintAddressorilikeFilter,resolution,label,sortByAsc/sortByDesc,limit,page.
- Proxies to Vybe
GET /api/wallets/:ownerAddress/pnl- Proxies to Vybe
GET /v4/wallets/{ownerAddress}/pnlwithresolution, optionalmintAddress, sort fields,limit,page.
- Proxies to Vybe
GET /api/tokens/:mint/top-pnl-traders- Proxies to Vybe
GET /v4/tokens/{mintAddress}/top-pnl-traderswith resolution and sort pagination.
- Proxies to Vybe
GET /api/tokens/:mint- Proxies to Vybe
GET /v4/tokens/{mintAddress}for token metadata.
- Proxies to Vybe
GET /api/tokens/:mint/top-holders- Proxies to Vybe top holders for the mint (optional UI).
GET /api/trades- Proxies to Vybe
GET /v4/tradeswithmintAddress,limit,page,sortByDesc, optionaltimeStart/timeEnd.
- Proxies to Vybe
GET /api/programs/labeled-program-account?programAddress=…- Proxies to Vybe labeled program lookup; responses cached on disk (
data/).
- Proxies to Vybe labeled program lookup; responses cached on disk (
POST /api/programs/labeled-program-accounts- Batch labeled programs; merges with on-disk cache.
GET /api/token-symbol/:mintandPOST /api/token-symbols- Symbol resolution (Metaplex / Vybe fallback); cached under
data/.
- Symbol resolution (Metaplex / Vybe fallback); cached under
All Vybe requests use a shared client (src/api/index.ts) with timeouts, retries, and toHumanReadableError. Symbol and program-label caches are JSON files in the data/ directory (created on demand).
git clone https://github.com/vybenetwork/solana-wallet-pnl-profit-and-loss-api.git
cd solana-wallet-pnl-profit-and-loss-apinpm installcp .env.example .env
# Add your VYBE_API_KEY to .envnpm startThen open http://localhost:3000. The UI loads wallet PnL and related wallets for the search and resolution you choose; use Load wallet PnL after changing remote filters.
To expose the app on a public URL (e.g. for sharing or testing from another device), enable a tunnel (requires cloudflared installed):
TUNNEL=1 npm startOr use the npm script:
npm run dev:tunnelThe console will print a Cloudflare Tunnel URL when cloudflared starts successfully.
solana-wallet-pnl-profit-and-loss-api/
├── .env.example # Copy to .env, fill in VYBE_API_KEY (and optional SOLANA_RPC_URL, PORT, TUNNEL, TRADES_LOG)
├── tsconfig.json # TypeScript config for backend (tsx runs src/server.ts)
├── tsconfig.frontend.json # TypeScript config for frontend (builds public/app.js)
├── package.json # Scripts and pinned dependencies
├── README.md
├── screenshots/ # Screenshots referenced in this README (same filenames as template; you replace images)
├── public/ # Web GUI (HTML, CSS, built JS)
│ ├── index.html
│ ├── app.js # Generated by `npm run build:frontend` from src/frontend/app.ts
│ └── app.css
└── src/
├── server.ts # Express server; proxies Vybe API and serves public/
├── config.ts # Env loading, API base URL, timeouts, PUBLIC_DIR
├── cache.ts # On-disk caches (symbol, program-label) in data/
├── types/
│ └── api.ts # Interfaces matching Vybe API response shapes
├── utils/
│ └── formatters.ts # Shared formatting helpers
├── api/
│ ├── index.ts # createClient(apiKey) — wires all API methods
│ ├── client.ts # Axios wrapper, retries, human-readable errors
│ ├── tokens.ts # GET /v4/tokens/{mintAddress}
│ ├── holders.ts # GET /v4/tokens/{mint}/top-holders
│ ├── trades.ts # Trades, top-traders, wallet Pnl, token top PnL traders
│ └── token-symbol.ts # Token symbol fallback (Metaplex, WSOL/USDC hardcoded)
└── frontend/
└── app.ts # Wallet PnL UI → builds to public/app.js
If you want to bypass the UI and call Vybe directly for wallet PnL and related wallets:
import axios from 'axios';
const API = 'https://api.vybenetwork.xyz';
const headers = { 'X-API-KEY': process.env.VYBE_API_KEY!, Accept: 'application/json' };
async function fetchWalletPnl(ownerAddress: string, resolution = '7d') {
const { data } = await axios.get(`${API}/v4/wallets/${encodeURIComponent(ownerAddress)}/pnl`, {
params: { resolution, limit: 100, sortByDesc: 'realizedPnlUsd' },
headers,
});
return data;
}
async function fetchRelatedWallets(ilikeFilter: string, resolution = '7d') {
const { data } = await axios.get(`${API}/v4/wallets/top-traders`, {
params: { ilikeFilter, resolution, limit: 100, sortByDesc: 'realizedPnlUsd' },
headers,
});
return data;
}
const owner = 'CyaE1VxvBrahnPWkqm5VsdCvyS2QmNht2UFrKJHga54o';
Promise.all([fetchWalletPnl(owner), fetchRelatedWallets(owner)])
.then(([pnl, related]) => {
console.log('Wallet PnL payload keys:', pnl && typeof pnl === 'object' ? Object.keys(pnl) : pnl);
console.log('Related wallets rows:', Array.isArray((related as { data?: unknown[] })?.data) ? (related as { data: unknown[] }).data.length : related);
})
.catch((err) => console.error(err.response?.data ?? err.message));| Issue | What to do |
|---|---|
| 403 Forbidden | Verify VYBE_API_KEY in .env is correct and has access to wallet and top-traders endpoints. If the key works locally but not on a server, it may be IP-restricted — contact Vybe to allow your server IP. |
| Slow responses / timeouts | The app uses a 60s timeout for Vybe requests with retries (VYBE_TIMEOUT_MS, VYBE_MAX_RETRIES, VYBE_RETRY_DELAY_MS in src/config.ts). Retry later if Vybe is under load. |
| Missing env vars | Copy .env.example to .env and set VYBE_API_KEY. On start you should see VYBE_API_KEY loaded in the server logs. |
- Telegram: Vybe community
- Support ticket: Submit a ticket via vybenetwork.xyz



