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