Copy PnL Batch
curl --request POST \
--url https://api.polynode.dev/v3/copy-pnl/batch \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"wallets": [
"<string>"
],
"max_trades": 50000,
"history": "recent",
"from": 126701150399,
"to": 126701150399
}
'import requests
url = "https://api.polynode.dev/v3/copy-pnl/batch"
payload = {
"wallets": ["<string>"],
"max_trades": 50000,
"history": "recent",
"from": 126701150399,
"to": 126701150399
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
wallets: ['<string>'],
max_trades: 50000,
history: 'recent',
from: 126701150399,
to: 126701150399
})
};
fetch('https://api.polynode.dev/v3/copy-pnl/batch', 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.polynode.dev/v3/copy-pnl/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'wallets' => [
'<string>'
],
'max_trades' => 50000,
'history' => 'recent',
'from' => 126701150399,
'to' => 126701150399
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"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"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.polynode.dev/v3/copy-pnl/batch"
payload := strings.NewReader("{\n \"wallets\": [\n \"<string>\"\n ],\n \"max_trades\": 50000,\n \"history\": \"recent\",\n \"from\": 126701150399,\n \"to\": 126701150399\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.polynode.dev/v3/copy-pnl/batch")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"wallets\": [\n \"<string>\"\n ],\n \"max_trades\": 50000,\n \"history\": \"recent\",\n \"from\": 126701150399,\n \"to\": 126701150399\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.polynode.dev/v3/copy-pnl/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"wallets\": [\n \"<string>\"\n ],\n \"max_trades\": 50000,\n \"history\": \"recent\",\n \"from\": 126701150399,\n \"to\": 126701150399\n}"
response = http.request(request)
puts response.read_body{
"results": [
{
"wallet": "<string>",
"status": "ok",
"data": {
"wallet": "<string>",
"actual_pnl_usdc": "<string>",
"backtest_copy_pnl_usdc": "<string>",
"slippage_amount_usdc": "<string>",
"slippage_cost_rate_pct": "<string>",
"toxic_for_copying": true,
"trade_count": 250000,
"pnl_definition": "<string>",
"fees_included": true,
"opening_inventory_valued": true,
"open_positions_marked_to_market": true,
"slippage_bps": {
"buy": 123,
"sell": 123,
"buy_price_capped_at": "<string>"
},
"cashflows": {
"buy_cost_usdc": "<string>",
"sell_revenue_usdc": "<string>",
"settlement_in_usdc": "<string>",
"settlement_out_usdc": "<string>"
},
"event_counts": {
"buys": 123,
"sells": 123,
"splits": 123,
"merges": 123,
"redemptions": 123,
"neg_risk_conversions": 123
},
"coverage": {
"selection": "<string>",
"max_trades": 25000,
"history_truncated": true,
"window_start": {
"type": "beginning_of_indexed_history"
},
"first_included_event_timestamp": 123,
"last_included_event_timestamp": 123,
"snapshot_at": "2023-11-07T05:31:56Z",
"requested_history": "recent",
"served_history": "recent",
"fallback_reason": null,
"lifetime_history_included": true,
"window_end": {
"type": "exclusive_timestamp",
"timestamp": 123
}
},
"query_ms": 123,
"realized_pnl_context": {
"available": true,
"scope": "lifetime_wallet",
"date_filters_applied": false,
"source": "api.wallet_pnl_current",
"pnl_definition": "realized",
"total_realized_pnl_usdc": "<string>",
"position_count": 123,
"open_positions": 123,
"updated_block": "<string>",
"updated_at": "2023-11-07T05:31:56Z",
"refresh_age_seconds": 1,
"latest_indexed_fill_block": "<string>",
"freshness": "behind_indexed_fills"
},
"applied_filters": {
"period": "<string>",
"from": 123,
"to": 123
}
},
"cache": {
"status": "hit",
"age_ms": 1
}
}
],
"requested": 123,
"succeeded": 123,
"failed": 123,
"max_trades": 123,
"elapsed_ms": 123
}{
"error": {
"code": "<string>",
"message": "<string>"
}
}Wallet Activity
Copy PnL Batch
Calculate copying-slippage cash flow for up to 100 wallets in one request.
POST
/
v3
/
copy-pnl
/
batch
Copy PnL Batch
curl --request POST \
--url https://api.polynode.dev/v3/copy-pnl/batch \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"wallets": [
"<string>"
],
"max_trades": 50000,
"history": "recent",
"from": 126701150399,
"to": 126701150399
}
'import requests
url = "https://api.polynode.dev/v3/copy-pnl/batch"
payload = {
"wallets": ["<string>"],
"max_trades": 50000,
"history": "recent",
"from": 126701150399,
"to": 126701150399
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
wallets: ['<string>'],
max_trades: 50000,
history: 'recent',
from: 126701150399,
to: 126701150399
})
};
fetch('https://api.polynode.dev/v3/copy-pnl/batch', 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.polynode.dev/v3/copy-pnl/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'wallets' => [
'<string>'
],
'max_trades' => 50000,
'history' => 'recent',
'from' => 126701150399,
'to' => 126701150399
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"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"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.polynode.dev/v3/copy-pnl/batch"
payload := strings.NewReader("{\n \"wallets\": [\n \"<string>\"\n ],\n \"max_trades\": 50000,\n \"history\": \"recent\",\n \"from\": 126701150399,\n \"to\": 126701150399\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.polynode.dev/v3/copy-pnl/batch")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"wallets\": [\n \"<string>\"\n ],\n \"max_trades\": 50000,\n \"history\": \"recent\",\n \"from\": 126701150399,\n \"to\": 126701150399\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.polynode.dev/v3/copy-pnl/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"wallets\": [\n \"<string>\"\n ],\n \"max_trades\": 50000,\n \"history\": \"recent\",\n \"from\": 126701150399,\n \"to\": 126701150399\n}"
response = http.request(request)
puts response.read_body{
"results": [
{
"wallet": "<string>",
"status": "ok",
"data": {
"wallet": "<string>",
"actual_pnl_usdc": "<string>",
"backtest_copy_pnl_usdc": "<string>",
"slippage_amount_usdc": "<string>",
"slippage_cost_rate_pct": "<string>",
"toxic_for_copying": true,
"trade_count": 250000,
"pnl_definition": "<string>",
"fees_included": true,
"opening_inventory_valued": true,
"open_positions_marked_to_market": true,
"slippage_bps": {
"buy": 123,
"sell": 123,
"buy_price_capped_at": "<string>"
},
"cashflows": {
"buy_cost_usdc": "<string>",
"sell_revenue_usdc": "<string>",
"settlement_in_usdc": "<string>",
"settlement_out_usdc": "<string>"
},
"event_counts": {
"buys": 123,
"sells": 123,
"splits": 123,
"merges": 123,
"redemptions": 123,
"neg_risk_conversions": 123
},
"coverage": {
"selection": "<string>",
"max_trades": 25000,
"history_truncated": true,
"window_start": {
"type": "beginning_of_indexed_history"
},
"first_included_event_timestamp": 123,
"last_included_event_timestamp": 123,
"snapshot_at": "2023-11-07T05:31:56Z",
"requested_history": "recent",
"served_history": "recent",
"fallback_reason": null,
"lifetime_history_included": true,
"window_end": {
"type": "exclusive_timestamp",
"timestamp": 123
}
},
"query_ms": 123,
"realized_pnl_context": {
"available": true,
"scope": "lifetime_wallet",
"date_filters_applied": false,
"source": "api.wallet_pnl_current",
"pnl_definition": "realized",
"total_realized_pnl_usdc": "<string>",
"position_count": 123,
"open_positions": 123,
"updated_block": "<string>",
"updated_at": "2023-11-07T05:31:56Z",
"refresh_age_seconds": 1,
"latest_indexed_fill_block": "<string>",
"freshness": "behind_indexed_fills"
},
"applied_filters": {
"period": "<string>",
"from": 123,
"to": 123
}
},
"cache": {
"status": "hit",
"age_ms": 1
}
}
],
"requested": 123,
"succeeded": 123,
"failed": 123,
"max_trades": 123,
"elapsed_ms": 123
}{
"error": {
"code": "<string>",
"message": "<string>"
}
}Uses the same Copy PnL calculation, coverage, and response fields for each wallet. This replaces the deprecated V2 on-demand batch.
Send parameters in a JSON object, with a body no larger than 16 KiB. Unknown fields, invalid options, and invalid batch sizes return
A valid batch returns HTTP
Work is scheduled with bounded concurrency. Each calculation has a ten-second database budget, and the batch has a 25-second queue/result deadline inside the public request timeout. A maximum-size batch is accepted, but 100 uncached heavy wallets are not guaranteed to finish in that deadline. Start with batches of 5–10 for interactive use. Keep successful results and retry only transient failures in smaller batches; changing
Request
curl -X POST "https://api.polynode.dev/v3/copy-pnl/batch" \
-H "x-api-key: pn_live_YOUR_KEY" \
-H "Content-Type: application/json" \
--data '{"wallets":["0xab03f377164c3726d498e56f7a9398cd8eb74494","0x3708cef9f92678df15a0473e36c45d68a16cdaf9"],"max_trades":50000}'
| Body field | Required | Description |
|---|---|---|
wallets | Yes | Array of 1–100 wallet addresses. |
max_trades | No | Integer from 1 to 50,000, applied per wallet. Default 50,000. Recent/fallback cap; initial scan size in full mode. |
history | No | recent (default), auto, or full, applied independently to each wallet. |
period | No | 7d, 14d, 30d, 60d, 90d, or 180d. No default date window. |
from, to | No | Inclusive start and exclusive end. Unix seconds or UTC YYYY-MM-DD dates. from overrides period. |
400. An invalid wallet within an otherwise valid batch produces an error for that row.
Results preserve input order and duplicate entries. Duplicate normalized wallets with the same options share calculation work. Wallets use different snapshots; relative periods are anchored to each calculation, so use explicit from and to to fix the same dates across the batch. The whole HTTP batch counts as one API request under normal V3 account limits; the old V2 five-second throttle does not apply.
Every successful row includes separately scoped lifetime realized_pnl_context. Date filters apply to the copying simulation, not to that stored lifetime context. In auto, some wallets may return full history and others the completed recent-window fallback. Inspect data.coverage.served_history, history_truncated, and fallback_reason for every row. In full, incomplete histories become individual errors. See history modes and work limits.
Response and partial failures
The envelope containsresults, requested, succeeded, failed, max_trades, and elapsed_ms.
| Result field | Successful row | Failed row |
|---|---|---|
wallet | Normalized wallet address | Original input |
status | ok | error |
data | Full single-wallet summary | Absent |
cache | status and age_ms | Absent |
error | Absent | Object with code and message |
status_code | Absent | Per-wallet HTTP-equivalent status, such as 400, 422, 503, or 504 |
200 even if some or all wallets fail. Always inspect status on every result. For example, an invalid wallet row is:
{
"wallet": "invalid-address",
"status": "error",
"error": {
"code": "invalid_wallet",
"message": "Wallet must be a 0x-prefixed, 40-digit hexadecimal address."
},
"status_code": 400
}
max_trades changes the analysis window.
Automatic expansion has up to two additional seconds per wallet within the existing ten-second calculation budget. It never silently lowers the requested fallback cap or returns a partly calculated financial total. The endpoint does not create a background job or update V2 BYOB wallets.Authorizations
Body
application/json
Invalid addresses produce per-row errors.
Required array length:
1 - 100 elementsRecent/fallback fill cap per wallet; initial scan size in full mode. Auto/full may return more fills, up to the 500000-fill work limit.
Required range:
1 <= x <= 50000recent keeps the cap; auto attempts full history with an explicit recent fallback; full requires complete matching history or returns an error.
Available options:
recent, auto, full Optional window ending at to or the calculation snapshot. No default period. Explicit from overrides period.
Available options:
7d, 14d, 30d, 60d, 90d, 180d Inclusive start: Unix seconds or YYYY-MM-DD at UTC midnight. Applies to fills and settlement events, not lifetime realized-PnL context.
Required range:
0 <= x <= 253402300799Exclusive end: Unix seconds or YYYY-MM-DD at UTC midnight. Defaults to the calculation snapshot.
Required range:
0 <= x <= 253402300799
