Overview
The Tax Loss Harvesting API provides endpoints to identify opportunities, validate trades, and execute tax-loss harvesting transactions. The workflow consists of three main steps:
- Identify opportunities (Manual or Automated)
- Validate proposed trades
- Generate trades
Authentication
All endpoints require authentication. Invalid or missing authentication tokens will result in a 401 Unauthorized response.
Endpoints
Choose between two workflows:
- Manual TLH
- Automated TLH
1A. Manual TLH
Identifies tax harvesting opportunities based on specific gain/loss criteria you define
Required Parameters
isFullPositionHarvest (boolean): Whether to harvest full position only
- false: Allow partial position sales
- true: Only allow full position sales
ignoreDoNotTLH (boolean): Whether to include positions marked as “Do Not TLH”
- false: Exclude Do Not TLH positions
- true: Include Do Not TLH positions
isGainHarvesting (boolean): Whether to look for gains or losses
- true: Look for gains
- false: Look for losses
amount (decimal): Maximum gain/loss amount to consider
- null: No limit
- amount: Maximum gain/loss amount
term (integer): Holding period filter
- 1: Both short and long term
- 2: Short term only
- 3: Long term only
type (string): Entity type to analyze
- “Portfolio”
- “Account”
ids (integer[]): Array of portfolio or account IDs to analyze
Optional Parameters:
securityTypeIds (integer[]): Filter by security types
securityIds (integer[]): Filter by specific securities
minimumGainLoss (integer): Minimum gain/loss threshold type
- Per lot
- Per holding
minimumGainLossAmount (decimal): Minimum gain/loss amount
minimumGainLossPercent (decimal): Minimum gain/loss percentage
1B) Automated TLH
Identifies opportunities using preset thresholds. Processes up to 500 portfolios/accounts per batch.
Required Parameters
term (integer): Holding period filter (1, 2, or 3)
type (string): Entity type (“Portfolio” or “account”)
ids (integer[]): Portfolios/accounts to analyze
bucketArray (integer[][]): Max 2 buckets, 250 elements each
batchId (string): Unique identifier (obtain via createTLHTradeBatchId endpoint)
Optional Parameters:
securityTypeIds (integer[]): Filter by security types
securityIds (integer[]): Filter by specific securities
To generate a batch ID:
2. Trade Validation
Validates proposed trades for wash sales and other restrictions.
Required Parameters:
allowWashSaleOnSell (boolean): Override wash sale restrictions
isTaxHarvesting (boolean):
- true: Manual TLH
- false: Automated TLH
trades (array): Array of trade objects containing:
- accountId (integer)
- sellTickerId (integer)
- sellTickerName (string)
- percent (decimal)
- shares (decimal)
- buyTickerId (integer)
- buyTickerName (string)
- isEquivalentTicker (boolean)
- totalGLAmount (decimal)
- portfolioId (integer)
- washSaleGroup (string)
- isTargeted (integer): 0 or 1
- equivalentSecurityId (integer)
filter (object): Same format as opportunity identification
taxHarvestingFilterBatchId (string): Batch ID from previous step
3. Trade Generation
Creates validated trades
Required Parameters
All parameters from validation endpoint, plus:
instanceNotes (string): Optional description
tradeToolSelection (integer):
- Portfolio
- Account
tradeInstanceType (integer): Use 8 for TLH
tradeInstanceSubType (integer):
- 1: Automated TLH
- 7: Manual Gains
- 8: Manual Losses
Best Practices
- Always validate trades before generating them
- Include descriptive notes for audit purposes
- Check individual trade status in responses
- Handle errors appropriately:
-
- 401: Check authentication
- 400: Verify request parameters
- 500: Retry with exponential backoff
Example Payloads
1A. Manual TLH
Request:
{
"isFullPositionHarvest": false,
"ignoreDoNotTLH": false,
"isGainHarvesting": true,
"amount": null,
"term": 2,
"securityTypeIds": [5],
"securityIds": [292],
"minimumGainLoss": null,
"minimumGainLossAmount": null,
"minimumGainLossPercent": null,
"type": "portfolio",
"ids": [38598]
}
Response:
{
"batchId": "q2EOT0S-7odlHuW6Ak6Db",
"trades": [
{
"GLPercent": 100,
"LTGLAmount": 0,
"STGLAmount": 19925.02442,
"accountId": 64272,
"accountName": "Rogers 08, Steve",
"accountNumber": "SR08",
"accountType": "TAXABLE",
"accountValue": 385434.125782,
"cashValue": 4.59,
"costPrice": 86.87,
"currentPercent": 5.76,
"currentShares": 229.366,
"currentValue$": 22218.68442,
"custodian": "Schwab",
"custodianId": 4,
"securityId": 292,
"securityName": "Walt Disney Co",
"symbol": "DIS",
"tlhSecurityId": 14906,
"tlhSecurityName": "FS Bancorp Inc",
"tlhSecuritySymbol": "FSBW",
"tlhSellingPercent": 100,
"totalGLAmount": 19925.02442,
"tradeAmount": 22218.68
}
]
}
1B. Automated TLH
Request:
{
"amount": null,
"batchId": "rm8hAuojO6w3GBmDle87N",
"bucketArray": [
[38598]
],
"ids": [38598],
"ignoreDoNotTLH": null,
"isFullPositionHarvest": null,
"isGainHarvesting": null,
"minimumGainLoss": null,
"minimumGainLossAmount": null,
"minimumGainLossPercent": null,
"securityIds": [880],
"securityTypeIds": [5],
"term": 1,
"type": "portfolio"
}
Response:
{
"trades": [
{
"accountId": 64282,
"accountName": "Rebalance 11,",
"accountNumber": "REBAL11",
"symbol": "VTV",
"securityName": "Vanguard Value ETF",
"sellPrice": 164.93,
"costPrice": -35.07,
"totalGLAmount": -2875.74,
"tlhSellingPercent": 100,
"portfolioId": 38598,
"shares": 82,
"tradeAmount": 13524.26,
"STGLAmount": 0,
"LTGLAmount": -2875.74,
"GLPercent": -17.535,
"accountType": "TAXABLE",
"custodian": "Schwab"
}
],
"haveDoNotTLHSecurity": false,
"batchId": "rm8hAuojO6w3GBmDle87N"
}
2. Trade Validation
Request:
{
"allowWashSaleOnSell": false,
"isTaxHarvesting": true,
"trades": [{
"accountId": 64272,
"sellTickerId": 292,
"sellTickerName": "DIS",
"percent": 100,
"shares": 229.366,
"buyTickerId": 14906,
"buyTickerName": "FS Bancorp Inc",
"isEquivalentTicker": false,
"totalGLAmount": 19925.02442,
"portfolioId": 38598,
"washSaleGroup": "27531",
"isTargeted": 1,
"equivalentSecurityId": null
}],
"filter": {
"isFullPositionHarvest": false,
"ignoreDoNotTLH": false,
"isGainHarvesting": true,
"amount": null,
"term": 2,
"securityTypeIds": [5],
"securityIds": [292],
"type": "portfolio",
"ids": [38598],
"isViewOnly": false
},
"taxHarvestingFilterBatchId": "q2EOT0S-7odlHuW6Ak6Db",
"overridePreference": {
"enableVSP": null
}
}
Response:
{
"cashValuePostTrade": {
"64272": 90060.28
},
"trades": [
{
"accountId": 64272,
"buyTickerId": 14906,
"buyTickerName": "FSBW",
"buyTradeAmount": 22218.67,
"buyTradeShares": 633.191,
"cashValuePostTrade": 90060.273774,
"errorMessage": "",
"isValidTrade": 1,
"percent": 100,
"reason": "Successfully validated Trades",
"sellTickerId": 292,
"sellTickerName": "DIS",
"sellTradeAmount": 22218.68,
"sellTradeShares": 229.366,
"status": "success",
"warningMessage": "Sell Trade (DIS): Portfolio max gain and carry forward loss amount of 200 has been reached."
}
]
}
3. Trade Generation
Request:
{
"allowWashSaleOnSell": false,
"isTaxHarvesting": true,
"trades": [
{
"accountId": 64272,
"sellTickerId": 292,
"sellTickerName": "DIS",
"percent": 100,
"shares": 229.366,
"buyTickerId": 14906,
"buyTickerName": "FS Bancorp Inc",
"isEquivalentTicker": false,
"totalGLAmount": 19925.02442,
"portfolioId": 38598,
"washSaleGroup": "27531",
"isTargeted": 1,
"equivalentSecurityId": null
}
],
"filter": {
"isFullPositionHarvest": false,
"ignoreDoNotTLH": false,
"isGainHarvesting": true,
"amount": null,
"term": 2,
"securityTypeIds": [5],
"securityIds": [292],
"type": "portfolio",
"ids": [38598],
"isViewOnly": false
},
"instanceNotes": "TLH Instance",
"taxHarvestingFilterBatchId": "Sst1x8ujrbmGHIIcLPBYo",
"overridePreference": {
"enableVSP": null
},
"tradeToolSelection": 1,
"tradeInstanceType": 8,
"tradeInstanceSubType": 7
}
Response:
{
"trades": [
{
"sellTickerId": 292,
"sellTickerName": "DIS",
"buyTickerId": 14906,
"percent": 100,
"accountId": 64272,
"portfolioId": 38598,
"status": "success",
"reason": "Successfully Swap Trades.",
"isValidTrade": 1,
"errorMessage": ""
}
],
"instanceId": 1893,
"cashValuePostTrade": {
"64272": 90060.28
}
}
Rate Limits
- Maximum 500 portfolios/accounts per batch
- Maximum 2 buckets per automated TLH request