Skip to main content

TL;DR — which PnL field do you actually want?

This endpoint returns two different PnL numbers that can look very different for the same wallet. Pick the right one for your use case: Both numbers are correct — they measure different things. See the worked example below for the math.

Path parameters

string
required
The Polymarket wallet address (Safe proxy or EOA, normalized to lowercase).

Query parameters

string
default:"30d"
Convenience window preset. One of: 7d, 14d, 30d, 60d, 90d, 180d. Anchored to “now”. If you pass period, you don’t need from.
string | number
Start of the window. Accepts YYYY-MM-DD (UTC midnight) or unix seconds. Overrides period if both are passed.
string | number
End of the window. Same format as from. Defaults to current time when omitted.
boolean
default:"false"
When 1 or true, response includes a trades array with one entry per fill (ts, side, price, shares, actual_usd, backtest_usd). Useful for transparency and debugging.

Response fields

Understanding cashflow PnL vs realized PnL

The most common source of confusion with this endpoint is “why don’t actual_pnl_usdc and total_realized_pnl_usdc agree?” — they’re answering different questions. Worked example. A wallet’s first 30 days:
Final state explained:
  • actual_pnl_usdc = -$16 — the wallet paid out $16 more USDC than it received over the 30 days.
  • total_realized_pnl_usdc = +$17.20 — actual trading profit on the 130 shares the wallet closed.
  • The wallet still HOLDS 70 shares (cost basis $32.20). Cashflow counts that as money “spent”. Realized ignores it.
Reconciliation: cashflow_pnl + cost_basis_of_open_positions ≈ realized_pnl. In this example: -$16 + $32.20 = $16.20 ≈ $17.20 (small rounding from WAVG cost basis updates). Which to use:
  • For “did the wallet make money trading?” → total_realized_pnl_usdc. This is what trading platforms typically call “PnL” and what users intuitively expect.
  • For “how much net cash flowed through this wallet?” → actual_pnl_usdc. Useful for liquidity analysis and the slippage formula, but not what most people mean by “PnL”.
  • For copy-trading slippage analysis → use slippage_* fields (which are derived from cashflow). The slippage formula needs apples-to-apples cashflow comparison; this is why actual_pnl_usdc is defined as cashflow.
One-line summary: realized = closed-trade profit; cashflow = USDC delta in/out of the wallet. They reconcile when the wallet holds zero open inventory.

Example: default 30-day window

Request:
Response (200 OK):
Reading this response: the wallet’s realized trading P/L is **-2,412(lost 2,412** (lost ~2.4K on 142 positions that closed in the window). The cashflow says **-5,697theextra5,697** — the extra 3,285 is the cost basis of positions still open at window-end (inventory the wallet bought but hasn’t sold yet). For “did this wallet make money?” the answer is total_realized_pnl_usdc — and the answer is no.

Example: 7-day window via preset

Request:
Response (200 OK):

Example: explicit date range

Request — score the window from April 15 to April 25 inclusive:
Response (200 OK):

Example: per-fill drill-down

Request:
Response (200 OK, truncated):

Errors

400 Invalid period:
401 No API key:
403 Free tier:
429 Rate limited:
The Retry-After header (in seconds, rounded up) is also set on 429 responses. The rate limit is shared across all /v2/copy-pnl/* calls per API key — calling the endpoint with different wallets or different params still counts toward the same 1 req per 5 seconds budget.

Notes

  • Wallet address is case-insensitive. Internally lowercased.
  • Time precision: when from is YYYY-MM-DD, it resolves to that date at 00:00 UTC. Pass unix seconds for sub-day precision.
  • Settlement events (redemption, merge, split, neg_risk_conversion) are processed at face value on both actual_pnl and backtest_copy_pnl, so they cancel out in slippage_amount but remain in the absolute PnL numbers.
  • High-volume wallets: the underlying walk is paginated by time bucket and runs in parallel. Wallets with up to ~1.6M fills in the window have been validated. Beyond that, pass a tighter window or expect partial: true.