Historical uploads
Historical-holdings endpoints write holdings directly. There are three families: account holdings (cash), asset holdings (securities in a depository) and fee holdings, plus a bulk reclassification of asset holdings.
Common rules
- Every attribute listed for a family is required. Numbers must be JSON numbers and dates strict
yyyy-MM-dd; otherwise400Cannot parse request body.currencyCode/reportingCurrencyCodeandclassificationIdmay benullbut must be present, and are then rejected by the validation below. - A relationship the family does not declare is
400Resource of type '{type}' does not support relationship '{name}'., checked before the type. - A
data.typeother than the family's type is409Resource type '{type}' does not match the expected type of '{expected}'. - An unknown or invisible account, asset or depository is
404'{account|asset|depository}' resource with ID '{id}' not found. - An unknown
classificationIdis checked after those: on account and asset holdings it is500An error occurred while processing your request.; on fee holdings it is the404below. - A second holding on the same natural key is
403(texts per family below). Uploads are never overwritten. - The
201echoes the attributes in the order listed, with every number as a decimal (5becomes5.0,650.00keeps its scale), the relationships, a new 24-character hexidand thetype— in that member order.
Upload account holdings
Books a historical holding on a cash account. Type historical-account-holding; relationship account.
Attributes
date, marketValue, holdings, return, externalMovement, currencyCode, classificationId.
Errors
400—'{currencyCode}' is not a valid currency code., thenAccountId must not be null or empty., thenClassificationId must not be null or empty.403—A historical account holding already exists for the given date and account.
curl -X POST https://api.aleta.ai/api/v2/historical-account-holdings \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "historical-account-holding",
"attributes": {
"date": "2025-07-01",
"marketValue": 650.00,
"holdings": 100.00,
"return": 0.05,
"externalMovement": 10.00,
"currencyCode": "DKK",
"classificationId": "68f132497c339ca9e9141234"
},
"relationships": {
"account": { "data": { "type": "account", "id": "66f132497c339ca9e9141234" } }
}
}
}'
{
"data": {
"attributes": {
"date": "2025-07-01",
"marketValue": 650.00,
"holdings": 100.00,
"return": 0.05,
"externalMovement": 10.00,
"currencyCode": "DKK",
"classificationId": "68f132497c339ca9e9141234"
},
"relationships": {
"account": {
"data": { "id": "66f132497c339ca9e9141234", "type": "account" }
}
},
"id": "66f132497c339ca9e9145678",
"type": "historical-account-holding"
}
}
Upload asset holdings
Books a historical holding of an asset in a depository. Type historical-asset-holding; relationships asset and depository.
Attributes
date, marketPriceDate, marketValue, holdings, return, externalMovement, marketPrice, currencyExchangeRate, reportingCurrencyCode, classificationId, holdingType (asset or redemption; any other value is 400 Cannot parse request body.).
Errors
400—AssetId must not be null or empty., thenDepositoryId must not be null or empty., thenClassificationId must not be null or empty., then'ReportingCurrencyCode' must be an ISO 4217 currency code.403—A historical asset holding already exists for the given date, asset, depository, and holdingType.
curl -X POST https://api.aleta.ai/api/v2/historical-asset-holdings \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "historical-asset-holding",
"attributes": {
"date": "2025-07-01",
"marketPriceDate": "2025-07-01",
"marketValue": 650.00,
"holdings": 100.00,
"return": 0.05,
"externalMovement": 0.00,
"marketPrice": 6.50,
"currencyExchangeRate": 100,
"reportingCurrencyCode": "DKK",
"classificationId": "68f132497c339ca9e9141234",
"holdingType": "asset"
},
"relationships": {
"asset": { "data": { "type": "asset", "id": "66f132497c339ca9e9141234" } },
"depository": { "data": { "type": "depository", "id": "66f132497c339ca9e9145678" } }
}
}
}'
Upload fee holdings
Books a historical fee holding for a fee asset on an account. Type historical-fee-holding; relationships asset (must be a fee asset) and account.
Attributes
date, return, externalMovement, reportingCurrencyCode, classificationId.
Errors
400—AssetId must not be null or empty., thenAccountId must not be null or empty., then'ReportingCurrencyCode' must be an ISO 4217 currency code., thenClassificationId must not be null or empty.404—No classification found with ID '{classificationId}'for an unknown classification.403—A historical fee holding already exists for the given date, fee, and account.
curl -X POST https://api.aleta.ai/api/v2/historical-fee-holdings \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "historical-fee-holding",
"attributes": {
"date": "2025-07-01",
"return": 100.00,
"externalMovement": -100.00,
"reportingCurrencyCode": "DKK",
"classificationId": "68f132497c339ca9e9141234"
},
"relationships": {
"asset": { "data": { "type": "asset", "id": "66f132497c339ca9e9141234" } },
"account": { "data": { "type": "account", "id": "66f132497c339ca9e9145678" } }
}
}
}'
Delete historical holdings
The three DELETE routes take no body; they delete every historical holding of the family in scope dated fromDate to toDate (inclusive) and return 200 with {"meta":{"recordsDeleted":N}}. Query parameter names are matched case-insensitively.
Validation, in order (the id rules apply to each id parameter of the route):
400—The ID '{id}' in '{parameter}' is not a valid ID format.(also when the id parameter is missing)400—'FromDate' must be specified exactly once./'ToDate' must be specified exactly once.400—'FromDate' must not be empty/'FromDate' must be a valid date in the format 'yyyy-MM-dd'.(same forToDate)400—'FromDate' must be less than or equal to 'ToDate'404—Account not found./Depository not found.for an account or depository you cannot see. An id that does not exist in the workspace at all is500Internal server error.
Delete account holdings
Query parameters: accountId, fromDate, toDate (all required).
curl -X DELETE -G https://api.aleta.ai/api/v2/historical-account-holdings \
-H "Authorization: Bearer {access_token}" \
-d accountId={account_id} \
-d fromDate=2024-01-01 \
-d toDate=2024-12-31
{ "meta": { "recordsDeleted": 12 } }
Delete asset holdings
Query parameters: depositoryId, fromDate, toDate (all required). Deletes the depository's asset and redemption holdings in the range.
curl -X DELETE -G https://api.aleta.ai/api/v2/historical-asset-holdings \
-H "Authorization: Bearer {access_token}" \
-d depositoryId={depository_id} \
-d fromDate=2024-01-01 \
-d toDate=2024-12-31
Delete fee holdings
Query parameters: accountId, assetId (the fee asset), fromDate, toDate (all required). An asset that is not a fee you can see is 404 Account not found.
curl -X DELETE -G https://api.aleta.ai/api/v2/historical-fee-holdings \
-H "Authorization: Bearer {access_token}" \
-d accountId={account_id} \
-d assetId={fee_asset_id} \
-d fromDate=2024-01-01 \
-d toDate=2024-12-31
Reclassify asset holdings
Moves every historical holding of one asset from one classification to another and returns 200 with the number of rows changed. Type historical-asset-holdings-reclassify; relationship asset; attributes fromClassificationId and toClassificationId (both required keys). Reclassifying to the same classification changes nothing (rowsUpdated: 0).
Errors
400—Cannot parse request body.when a classification attribute is missing.409—Resource type '{type}' does not match the expected type of 'historical-asset-holdings-reclassify'400—AssetId must not be null or empty., thenFromClassificationId must not be null or empty., thenToClassificationId must not be null or empty.404No matching historical asset holdings found for the requested reclassification.when you cannot see the asset or one of the depositories holding it; nothing is updated.404No classification found with ID '{classificationId}'.for an unknown classification (fromClassificationIdis checked first).
curl -X POST https://api.aleta.ai/api/v2/historical-asset-holdings/reclassify \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "historical-asset-holdings-reclassify",
"attributes": {
"fromClassificationId": "68f132497c339ca9e9141234",
"toClassificationId": "68f132497c339ca9e9145678"
},
"relationships": {
"asset": { "data": { "type": "asset", "id": "66f132497c339ca9e9141234" } }
}
}
}'
{
"data": {
"attributes": { "rowsUpdated": 42 },
"relationships": {
"asset": {
"data": { "id": "66f132497c339ca9e9141234", "type": "asset" }
}
},
"id": null,
"type": "historical-asset-holdings-reclassify"
}
}
Related
- Custodian holdings — the custodian-reported positions.
- Transactions — for booking historical activity rather than balance snapshots.