get_positions() was the py_clob_client method for retrieving your open Polymarket positions in V1. CLOB V2 removed it — there is no client.get_positions() (and no client.get_balance()) in py-clob-client-v2. In V2, positions come from the public Data API (GET https://data-api.polymarket.com/positions), which needs no authentication and returns richer data — including unrealized P&L computed for you. This page shows the V2 way to track positions, calculate P&L, and gate orders on existing holdings.
⚠️
get_positions()was removed in CLOB V2 (live April 28, 2026). If you’re porting V1 code, replaceclient.get_positions()with a Data API call (below). The V1py-clob-clientpackage no longer works against production at all — see Migrating to CLOB V2. Each position represents your holdings of a specific outcome token (how many shares, at what average price).
For the complete V2 method reference, see the py-clob-client-v2 Reference. For Kalshi’s equivalent positions endpoint, see the Prediction Market API Reference.
What happened to get_positions() in V2
The V1 call simply doesn’t exist anymore:
# V1 — no longer works on production:
# positions = client.get_positions()
# V2 — fetch from the Data API (no auth needed):
import requests
positions = requests.get(
"https://data-api.polymarket.com/positions",
params={"user": "<your-wallet-address>"},
).json()
If you’re migrating a V1 bot, here’s the full mapping of what changed around position tracking:
V1 (py-clob-client) | V2 (py-clob-client-v2) |
|---|---|
client.get_positions() | Data API GET /positions (no client method) |
client.get_balance() | client.get_balance_allowance(BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)) |
client.create_or_derive_api_creds() | client.create_or_derive_api_key() |
from py_clob_client.order_builder.constants import BUY | from py_clob_client_v2 import Side → Side.BUY |
pip install py-clob-client | pip install py-clob-client-v2 |
| USDC.e collateral | pUSD collateral |
The rest of this page uses the V2 Data API path.
The Data API Positions Endpoint
GET https://data-api.polymarket.com/positions?user=<wallet-address>
No authentication required — you only need a wallet address. The user is the funder address that holds the positions (your proxy / Gnosis Safe / deposit wallet, or the EOA itself), not necessarily the signing key.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user | address | Yes | The wallet address holding the positions |
market | string | No | Comma-separated condition IDs to filter by (mutually exclusive with eventId) |
eventId | string | No | Comma-separated event IDs (mutually exclusive with market) |
sizeThreshold | number | No | Minimum size to include (default 1) |
redeemable | bool | No | Only positions in resolved markets awaiting redemption (default false) |
mergeable | bool | No | Only positions that can be merged (default false) |
limit | int | No | Page size, 0–500 (default 100) |
offset | int | No | Pagination offset, max 10000 (default 0) |
sortBy | string | No | TOKENS (default), CURRENT, INITIAL, CASHPNL, PERCENTPNL, PRICE, AVGPRICE, TITLE, RESOLVING |
sortDirection | string | No | ASC or DESC (default DESC) |
title | string | No | Filter by market title substring |
Response Fields (per position)
| Field | Description |
|---|---|
asset | The outcome token ID (string) |
conditionId | The on-chain condition (market) ID |
size | Shares held |
avgPrice | Average entry price per share |
curPrice | Current market price |
initialValue / currentValue | Cost basis and current mark-to-market value |
cashPnl / percentPnl | Unrealized P&L (dollars and percent) — computed for you |
realizedPnl / percentRealizedPnl | Realized P&L |
redeemable | true when the market resolved and the position can be redeemed |
title / slug / eventSlug / icon | Market metadata |
outcome / outcomeIndex | The outcome label (e.g. Yes) and its index |
oppositeOutcome / oppositeAsset | The other side’s label and token ID |
negativeRisk | true for neg-risk markets |
proxyWallet / endDate | The holding wallet and market end date |
Note
assetis the token ID string itself — there’s no nestedasset.token_idobject (that was a V1 client shape).
List your open positions
import requests
WALLET = "<your-wallet-address>" # the funder address that holds your positions
positions = requests.get(
"https://data-api.polymarket.com/positions",
params={"user": WALLET},
).json()
if not positions:
print("No open positions")
else:
for pos in positions:
print(f"Market: {pos.get('title', 'Unknown')} ({pos['outcome']})")
print(f"Token: {pos['asset'][:20]}...")
print(f"Shares: {float(pos['size']):.2f}")
print(f"Avg Price: ${float(pos['avgPrice']):.4f}")
print(f"Cur Price: ${float(pos['curPrice']):.4f}")
print("---")
Calculate Portfolio P&L
In V2 you don’t need to fetch prices and do the math yourself — the Data API already returns currentValue, initialValue, cashPnl (unrealized dollar P&L), and percentPnl per position. Just read and sum them:
import requests
WALLET = "<your-wallet-address>"
positions = requests.get(
"https://data-api.polymarket.com/positions",
params={"user": WALLET, "sortBy": "CASHPNL"},
).json()
total_pnl = 0.0
total_cost = 0.0
for pos in positions:
cash_pnl = float(pos["cashPnl"])
total_pnl += cash_pnl
total_cost += float(pos["initialValue"])
print(f"{pos.get('title', 'Unknown')} ({pos['outcome']})")
print(f" Entry: ${float(pos['avgPrice']):.4f} → Current: ${float(pos['curPrice']):.4f}")
print(f" P&L: ${cash_pnl:+.2f} ({float(pos['percentPnl']):+.1f}%)")
print(f"\nTotal Cost Basis: ${total_cost:.2f}")
print(f"Total Unrealized P&L: ${total_pnl:+.2f}")
For realized P&L on closed-out positions, read realizedPnl / percentRealizedPnl (or query the closed-positions endpoint). If you prefer to compute marks yourself, unrealized = (curPrice − avgPrice) × size reproduces cashPnl.
Check for Existing Position Before Trading
Prevent accidentally doubling your exposure when a bot runs repeatedly. Look up positions from the Data API by wallet, then filter by the asset token ID:
import requests
from py_clob_client_v2 import OrderArgs, OrderType, PartialCreateOrderOptions, Side
def get_position_size(wallet, target_token_id):
"""Current position size for a token, or 0 if no position."""
positions = requests.get(
"https://data-api.polymarket.com/positions",
params={"user": wallet},
).json()
for pos in positions:
if pos["asset"] == target_token_id:
return float(pos["size"])
return 0.0
def has_position(wallet, target_token_id):
"""True if we already hold a non-zero position in this outcome."""
return get_position_size(wallet, target_token_id) > 0
# Example: only buy if we don't already have a position
WALLET = "<your-wallet-address>"
token_id = "<token-id>"
if not has_position(WALLET, token_id):
client.create_and_post_order(
order_args=OrderArgs(token_id=token_id, price=0.50, size=10.0, side=Side.BUY),
options=PartialCreateOrderOptions(tick_size="0.01"),
order_type=OrderType.GTC,
)
print("Order placed")
else:
current_size = get_position_size(WALLET, token_id)
print(f"Already holding {current_size:.2f} shares — skipping")
Position Sizing Based on Current Holdings
Scale orders based on your existing exposure (Data API) and available collateral. Note V2 has no get_balance() — read your pUSD balance via get_balance_allowance():
from py_clob_client_v2 import BalanceAllowanceParams, AssetType
def calculate_order_size(client, wallet, token_id, max_position=1000, target_price=0.50):
"""Calculate order size respecting a maximum position limit."""
current_size = get_position_size(wallet, token_id) # Data API (see above)
remaining_capacity = max_position - current_size
if remaining_capacity <= 0:
print(f"Position limit reached: {current_size:.0f}/{max_position} shares")
return 0
# Check available pUSD balance (V2: get_balance_allowance, not get_balance)
ba = client.get_balance_allowance(
BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)
)
balance_pusd = int(ba["balance"]) / 1e6
max_affordable = balance_pusd / target_price
order_size = min(remaining_capacity, max_affordable)
print(f"Current: {current_size:.0f} | Capacity: {remaining_capacity:.0f} | "
f"Affordable: {max_affordable:.0f} | Order: {order_size:.0f}")
return order_size
curl & Filtering Examples
Because the Data API is public, you can hit it from anything — curl, a dashboard, a serverless function — for any wallet, not just your own:
# All positions for a wallet
curl "https://data-api.polymarket.com/positions?user=0xYOUR_WALLET_ADDRESS"
# Only positions in one market (condition ID), biggest P&L first
curl "https://data-api.polymarket.com/positions?user=0xYOUR_WALLET_ADDRESS&market=0xCONDITION_ID&sortBy=CASHPNL"
# Only resolved positions you can redeem
curl "https://data-api.polymarket.com/positions?user=0xYOUR_WALLET_ADDRESS&redeemable=true"
Division of labor in V2: the Data API owns positions and P&L (this page); the CLOB client (py-clob-client-v2) owns balances/allowances (get_balance_allowance) and orders (create_order, post_order, …). There’s no longer a positions method on the client to choose between — positions are always a Data API call.
Common Errors
| Error | Cause | Fix |
|---|---|---|
AttributeError: get_positions | Calling the removed V1 method on py-clob-client-v2 | There is no client positions method in V2 — use the Data API GET /positions |
| Empty array returned | Querying the signing EOA instead of the funder, or no holdings | Use the funder address (proxy / Safe / deposit wallet) that actually holds the positions, and confirm it has open positions |
KeyError: 'token_id' | Reading the V1 client shape on a Data API response | The token ID is the top-level asset field (a string), not asset['token_id'] |
ConnectionError | API unreachable | Retry with exponential backoff |
| Stale position data | Positions may lag recent fills by seconds | For real-time fills, use the WebSocket user channel |
| Missing resolved positions | Default query hides resolved markets | Pass redeemable=true to list positions in resolved markets awaiting redemption |
See Also
- py-clob-client-v2 Complete Reference — Every CLOB V2 method documented
- py_clob_client create_order() — Place new orders
- py_clob_client get_balance_allowance() — Check pUSD funds available
- py_clob_client get_order_book() — Read market liquidity
- Prediction Market API Reference — Polymarket vs Kalshi comparison
- Build a Polymarket Trading Bot — Full bot tutorial
- Agent Betting Glossary — Key terms defined
- Sports Betting vs Prediction Markets — Position management across betting platforms
{{ partial “marketplace-cta.html” . }}
This reference is maintained by AgentBets.ai. Found an error or SDK change we missed? Let us know on Twitter.
Not financial advice. Built for builders.
