AArtery
On this page

Batch account value

POST /v1/accounts/batch resolves the complete, mode-aware account value for a bounded list of wallets in a single call — the same number /v1/portfolio and /v1/me/stats report per wallet, just fanned out. It exists so a fund / copy-trade aggregator can read "total amount + total unrealized PnL across N wallets" without making N round-trips.

Artery is the stateless per-address provider here: it computes current state on demand. Snapshots, time series, and cross-wallet aggregation are owned by the caller — Artery does not store a "fund" concept.

When to use this

  • Fund / copy-trade dashboards — sum current NAV across a follower set.
  • Risk monitoring — watch unrealized PnL across many wallets at once.
  • For a single wallet, prefer /v1/portfolio (it also returns the raw clearinghouseState + Polymarket positions).

Request

bashcurl -X POST -H "Authorization: Bearer art_live_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{"users":["0x1111…","0x2222…"]}' \
  "https://api.artery.questflow.ai/v1/accounts/batch"

Body

FieldTypeRequiredDescription
usersstring[]yesEVM addresses, 0x + 40 hex each. Max 50 per call — page client-side for larger sets. Must be unique.

Each address is lowercased server-side. More than 50 addresses, an empty array, a malformed address, or a duplicate address each return 400 (duplicates are rejected so the fund total can't silently double-count).

Response

jsonc{
  "results": [
    {
      "address": "0x1111111111111111111111111111111111111111",
      "valueUsd": 12450.78,
      "buckets": [
        { "kind": "perp",    "usd": 12450.78 },
        { "kind": "spot",    "usd": 0 },
        { "kind": "vault",   "usd": 0 },
        { "kind": "staking", "usd": 0 }
      ],
      "unrealizedPnlUsd": 350.78,
      "accountMode": "unifiedAccount"
    },
    {
      "address": "0x2222222222222222222222222222222222222222",
      "valueUsd": null,
      "unrealizedPnlUsd": null,
      "note": "account state unavailable"
    }
  ],
  "summary": {
    "totalValueUsd": 12450.78,
    "addressesReporting": 1,
    "addressesRequested": 2
  },
  "fetchedAt": "2026-06-16T07:00:00.000Z"
}

Semantics

  • results[i] is in the same order as the request users — one row per requested address (which are unique).
  • valueUsd is the complete account value — spot_available + Σ dex accountValue + vault + staking, with unrealized PnL counted exactly once. Mode-aware exactly like /v1/portfolio: on unifiedAccount / portfolioMargin wallets the USDC margin hold is excluded from the spot bucket (it already lives inside each dex's accountValue).
  • buckets / accountMode mirror the portfolio endpoint's Hyperliquid slice.
  • unrealizedPnlUsd is mark-to-market open-position PnL summed across every perp dex. It is already inside valueUsd — surfaced separately for PnL consumers.
  • Per-address failure isolation — when an address can't be resolved upstream, its row has valueUsd: null and a note; the rest of the batch still returns 200. Failed addresses are excluded from summary.totalValueUsd but still counted in addressesRequested.

Deriving fund totals

total amount    = Σ results[i].valueUsd        (across all pages)
total unrealized = Σ results[i].unrealizedPnlUsd

For lifetime total PnL, the correct derivation is Σ valueUsd − Σ netDeposits (deposits − withdrawals). v1 does not return netDepositsUsd / realizedPnlUsd: the node-local sources are windowed (hl_fills ~14-day retention) or only cover the node's indexing history, so a lifetime figure can't be guaranteed from them yet. Those fields are planned for a later revision once a lifetime history source is settled.

Error responses

HTTPWhen
400users missing / not an array / empty, more than 50 addresses, a malformed address, or a duplicate address.
401Missing / invalid Authorization header.
403Key lacks the read scope.
429Over the portfolio.read SKU quota (this endpoint shares it).

Quota & performance

Metered under the portfolio.read SKU (1 unit per call). Each address triggers one full account fan-out (~1s, ~12 node info calls); the batch runs them with bounded internal concurrency and reuses the shared 15s coalescing cache, so overlapping batches for the same wallets are cheap. Keep batches at or below 50 so a synchronous call stays responsive — loop pages for larger sets.

Edit this page on GitHubLast updated
Batch account value · Artery API Docs