For developers
Everything a record page shows is available as JSON: the curves, the proof level, the score and the inputs it was computed from. No key and no account. The data is public and read-only.
One record
GET https://provepnl.com/api/record/address
The address is what follows /w/ in a record's link. Example: /api/record/0x176a657e1d013b6f97f6b…
| status | verified (owner proven and identity confirmed), owner-proven, unclaimed, or preview (a wallet someone looked up: not listed, not ranked) |
| owner | The trader page this record belongs to, when someone has proven it is theirs: handle, link, whether it carries the ✓, the channel the identity was confirmed on, and the kind of proof. |
| source | Where the numbers come from: on-chain data, an exchange's public copy-trading board, or a read-only key. |
| as_of | When the numbers were last computed (Unix seconds). |
| score, tier, rank, of, top | The 0 to 100 score, its tier, and where the record stands among all scored records. |
| score_parts | The three periods the score blends: 30 days (30%), 1 year (40%), all time (30%). A period the record has not earned yet counts as 50. |
| score_inputs | Per period: return, worst fall, consistency and track length, exactly as they went into the score. |
| windows | For 7d, 30d, 1y and max: return, worst fall (max_drawdown), score, and points: the cumulative return in percent from the start of the window. t0 and t1 are the first and last point's time; t lists every point's time when they are not evenly spaced. |
| windows_hidden | Periods we do not publish for this record, with the reason. |
| months | Return and worst fall for each calendar month on record. |
| capital_band, usd | The capital band, unless the owner hides it. Dollar figures only where the record's page shows them. |
| flags, stale, paused | Warnings shown on the page, whether the numbers are too old for the board, and whether a read-only key has stopped answering. |
One trader page
GET https://provepnl.com/api/trader/handle
Status, the channel the identity was confirmed on, every proven account with its kind of proof, and which record is the headline.
The board
GET https://provepnl.com/api/leaderboard?limit=100&offset=0&win=30d&venue=hyperliquid
win is 7d, 30d, 1y or max. venue is optional: hyperliquid, binance, okx, kucoin, bybit, gate, htx, mexc, gmx, gmx-avax. Up to 5,000 rows a call.
Recompute a score yourself
A score is a ranking against a fixed population. That population is frozen once a day and published, so a score only moves when the record moves.
GET https://provepnl.com/api/reference
With a record's score_inputs and this reference you can reproduce each period's score. This runs as it is:
import bisect, gzip, json, urllib.request
BASE = "https://provepnl.com"
def get(path):
req = urllib.request.Request(BASE + path, headers={"Accept-Encoding": "gzip", "User-Agent": "recompute-example"})
with urllib.request.urlopen(req) as r:
body = r.read()
return json.loads(gzip.decompress(body) if r.headers.get("Content-Encoding") == "gzip" else body)
rec = get("/api/record/0x176a657e1d013b6f97f6beef3f5fbbfb07193fb4")
ref = get("/api/reference")
def pct(sorted_values, v): # mid-rank percentile
n = max(len(sorted_values), 1)
return (bisect.bisect_left(sorted_values, v) + bisect.bisect_right(sorted_values, v)) / 2 / n
def period_score(period):
i, r = rec["score_inputs"][period], ref["windows"][period]
w_ret, w_risk, w_con, w_trk = ref["weights"][period]
cal = i["ret"] / max(i["mdd"], ref["drawdown_floor"].get(i["res"], 0.05))
raw = (w_ret * pct(r["ret"], i["ret"]) + w_risk * pct(r["cal"], cal)
+ w_con * pct(r["con"], i["consist"]) + w_trk * pct(r["trk"], i["track"]))
return min(round(100 * pct(r["raw"], raw), 1), 99.9)
for part in rec["score_parts"]:
if part["score"] is not None:
print(part["window"], "published", part["score"], "recomputed", period_score(part["window"]))
print("page score", rec["score"], "blend of the parts", round(sum(p["weight"] * p["counts_as"] for p in rec["score_parts"]), 1))
Two caps can make the page score lower than the blend, and both are shown on the page as a warning: an account that lost 95% or more at some point, or whose longer history can't be checked, is capped at 69.9, and one whose lifetime result is a loss is capped at 84.9. The full scoring code is on the scoring method page.
A live card for your site
A small card that always shows the current numbers and links to the full record. It says exactly what the page says: only a verified trader's card carries the ✓.
<iframe src="https://provepnl.com/embed/w/address" width="340" height="190" style="border:0;border-radius:14px" loading="lazy" title="Trading record on ProvePnL"></iframe> <iframe src="https://provepnl.com/embed/t/handle" width="340" height="190" style="border:0;border-radius:14px" loading="lazy" title="Trading record on ProvePnL"></iframe>
Add ?theme=light or ?theme=dark to fix the colours; otherwise the card follows the visitor's system setting.
Fair use
- Up to 240 record or trader requests an hour from one address, 60 for the reference. Cache what you fetch: the numbers change once an hour.
- Say where it comes from: "Data: ProvePnL" with a link to the record.
- Never present an unverified record as verified. The status field is the truth.
- This is a record of what happened. It is not advice and comes with no warranty.