When you close an equity position, you can control exactly which tax lots are sold. This guide walks through viewing your unrealized tax lots, inspecting the lots behind a symbol, and placing a sell order that targets specific lots — with raw HTTP requests and the Python SDK side by side.
For the HTTP examples you need an access token with the portfolio scope, which the tax lot endpoints require. For the Python examples, install version 0.1.19 or later of the Public Python SDK — the release that added tax lot support:
pip install "publicdotcom-py>=0.1.19"The SDK handles token generation for you — construct the client with the API secret key from your Public account settings:
from public_api_sdk import (
ApiKeyAuthConfig,
PublicApiClient,
PublicApiClientConfiguration,
)
client = PublicApiClient(
ApiKeyAuthConfig(api_secret_key="YOUR_API_SECRET_KEY"),
config=PublicApiClientConfiguration(
default_account_number="YOUR_ACCOUNT_NUMBER"
),
)Start with an account-wide overview of your unrealized tax lots. Each entry summarizes a position along with its cost basis and how its gain or loss splits between short and long term:
curl --request GET \
--url https://api.public.com/userapigateway/trading/{YOUR_ACCOUNT_ID}/taxlots/unrealized \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'In this example the account holds 15 shares of AAPL, bought at two different times:
{
"asOf": "2026-07-21",
"lots": [
{
"accountNumber": "YOUR_ACCOUNT_NUMBER",
"symbol": "AAPL",
"cusip": "037833100",
"companyName": "Apple Inc.",
"quantity": "15",
"costBasis": "2400.00",
"unitCost": "160.00",
"currentPrice": "212.34",
"currentValue": "3185.10",
"gainLoss": "785.10",
"shortTermGainLoss": "161.70",
"longTermGainLoss": "623.40"
}
],
"shortTerm": "161.70",
"longTerm": "623.40",
"sixtyFortyTerm": "0.00",
"totalProfitLoss": "785.10"
}The same call with the Python SDK:
summary = client.get_unrealized_tax_lots()
print(f"Total P/L as of {summary.as_of}: {summary.total_profit_loss}")
for lot in summary.lots:
print(f"{lot.symbol}: {lot.quantity} shares, gain/loss {lot.gain_loss}")The overview shows the position as a whole. To pick lots to sell, fetch the per-symbol detail, which lists every lot with its open date, holding term, and — most importantly — its lotSelectionId:
curl --request GET \
--url https://api.public.com/userapigateway/trading/{YOUR_ACCOUNT_ID}/taxlots/unrealized/AAPL \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'The response breaks the 15 shares into their two lots — 10 long-term shares bought at $150.00 and 5 short-term shares bought at $180.00:
{
"asOf": "2026-07-21",
"symbol": "AAPL",
"companyName": "Apple Inc.",
"lots": [
{
"quantity": "10",
"costBasis": "1500.00",
"unitCost": "150.00",
"currentPrice": "212.34",
"currentValue": "2123.40",
"gainLoss": "623.40",
"shortTermGainLoss": "0.00",
"longTermGainLoss": "623.40",
"openDate": "2024-01-15",
"term": "LONG",
"lotSelectionId": "AAPL;2024-01-15;150.00;10"
},
{
"quantity": "5",
"costBasis": "900.00",
"unitCost": "180.00",
"currentPrice": "212.34",
"currentValue": "1061.70",
"gainLoss": "161.70",
"shortTermGainLoss": "161.70",
"longTermGainLoss": "0.00",
"openDate": "2026-03-10",
"term": "SHORT",
"lotSelectionId": "AAPL;2026-03-10;180.00;5"
}
]
}The lotSelectionId is the handle you pass back when selling. If it is null, the lot cannot be selected — for example because the symbol is undergoing a corporate action, or the lot was deduced from trade history. Separately, the outOfDateStatusfield flags when a lot's information is out of date (for example, an order or trade on the symbol today) — lot instructions are only accepted while the information is current. With the Python SDK:
detail = client.get_unrealized_tax_lots_for_symbol("AAPL")
for lot in detail.lots or []:
print(
f"Opened {lot.open_date}: {lot.quantity} shares at {lot.unit_cost} "
f"({lot.term}), lot ID: {lot.lot_selection_id}"
)Lot prices and gains are calculated once per trading session and do not change intraday. To evaluate lots against a price of your choosing, pass an explicit price — for example ?price=215.00 on the request above, or price="215.00" in the SDK call.
Now place a sell order that names its lots. Suppose you want to sell 12 of the 15 shares: all 10 long-term shares, plus 2 of the 5 short-term shares — selling part of a lot is allowed. Attach one taxLotMatchingInstructionsentry per lot, using each lot's lotSelectionId as the taxLotId:
curl --request POST \
--url https://api.public.com/userapigateway/trading/{YOUR_ACCOUNT_ID}/order \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"orderId": "550e8400-e29b-41d4-a716-446655440000",
"instrument": {
"symbol": "AAPL",
"type": "EQUITY"
},
"orderSide": "SELL",
"orderType": "LIMIT",
"limitPrice": "212.00",
"expiration": {
"timeInForce": "DAY"
},
"quantity": "12",
"openCloseIndicator": "CLOSE",
"taxLotMatchingInstructions": [
{
"taxLotId": "AAPL;2024-01-15;150.00;10",
"quantity": "10"
},
{
"taxLotId": "AAPL;2026-03-10;180.00;5",
"quantity": "2"
}
]
}'The response confirms the order was submitted:
{
"orderId": "550e8400-e29b-41d4-a716-446655440000"
}With the Python SDK, build the same order with GatewayTaxLotMatchingInstruction objects:
import uuid
from decimal import Decimal
from public_api_sdk import (
GatewayTaxLotMatchingInstruction,
InstrumentType,
OpenCloseIndicator,
OrderExpirationRequest,
OrderInstrument,
OrderRequest,
OrderSide,
OrderType,
TimeInForce,
)
order = OrderRequest(
order_id=str(uuid.uuid4()),
instrument=OrderInstrument(symbol="AAPL", type=InstrumentType.EQUITY),
order_side=OrderSide.SELL,
order_type=OrderType.LIMIT,
limit_price=Decimal("212.00"),
expiration=OrderExpirationRequest(time_in_force=TimeInForce.DAY),
quantity=Decimal("12"),
open_close_indicator=OpenCloseIndicator.CLOSE,
tax_lot_matching_instructions=[
GatewayTaxLotMatchingInstruction(
tax_lot_id="AAPL;2024-01-15;150.00;10",
quantity="10",
),
GatewayTaxLotMatchingInstruction(
tax_lot_id="AAPL;2026-03-10;180.00;5",
quantity="2",
),
],
)
new_order = client.place_order(order)
print(f"Order submitted: {new_order.order_id}")📝 Rules for tax lot instructions
Two more endpoints round out the workflow. You can export every unrealized lot as a CSV file via /userapigateway/trading/{accountId}/taxlots/csv/unrealized (returned as a base64-encoded file, or get_unrealized_tax_lots_csv() in the SDK). And the single-leg preflight endpoint accepts the same taxLotMatchingInstructions field, so you can validate a lot-specific sell before submitting it.
For full request and response schemas, see the tax lot selling endpoint reference.