Kubera Data API v3

Read and write your Kubera portfolio programmatically — authenticate with an API key/secret, list and export portfolios, create and update assets and debts, manage cash flow entries, archive items, and generate recap reports.

  • Base URL: https://api.kubera.com  
  • Path prefix: /api/v3/data  
  • Content type: application/json   on every request
  • Auth: HMAC-SHA256 signed headers (see Authentication)

On this page

Getting started

Portfolio endpoints

Item endpoints

Cash flow endpoints

Recap report endpoints

Reference


Endpoint index

Every endpoint requires the three authentication headers. All paths are relative to https://api.kubera.com  .

# Purpose Method Path Body Returns
1 List portfolios GET /api/v3/data/portfolio portfolio[]
2 Get portfolio data GET /api/v3/data/portfolio/{portfolioId} Portfolio with assets, debts, totals
3 Create item POST /api/v3/data/item Yes New item ids
4 Update item POST /api/v3/data/item/{itemId} Yes Success envelope
5 Archive item POST /api/v3/data/item/{itemId}/archive Empty data
6 Get cash flow GET /api/v3/data/item/{itemId}/cashFlow cashFlow[]
7 Insert/update cash flow POST /api/v3/data/item/{itemId}/cashFlow Yes Success envelope
8 Submit recap report GET /api/v3/data/portfolio/{portfolioId}/recap/report 202 + reportId
9 Poll recap report GET /api/v3/data/portfolio/{portfolioId}/recap/report/{reportId} Status, then results

{itemId}   is an asset id or a debt id — the same endpoints serve both.


Authentication

Generate your keys

Generate your API key and secret from Kubera Settings > API.

  • IP restrictions are optional but highly recommended.
  • Keep your API key and secret confidential at all times.
  • In case of accidental disclosure, delete the compromised keys.
  • When creating a key, choose the appropriate permission level. The default setting is “Read portfolio data”.
  • Remove any API keys that are no longer in use.

Required headers

Every request must include these three headers, plus Content-Type: application/json  .

Header Value
x-api-token Your API key
x-timestamp Current time in seconds (Unix epoch)
x-signature HMAC-SHA256 signature (see below)

Signature generation

Build the signing string by concatenating five values in this exact order, with no separators:

{apiKey}{timestamp}{HTTP_METHOD}{requestPath}{body}
Part Notes
apiKey Same value as the x-api-token header
timestamp Unix epoch in seconds, same value as the x-timestamp header
HTTP_METHOD Uppercase — GET or POST
requestPath Path only, starting with /api/v3/.... No host, no query string
body The request body serialized with compact encoding (no spaces between keys/values). Empty string for requests with no body, such as GET

Sign that string with HMAC-SHA256 using your API secret. The hex digest is your x-signature  .

The body you sign and the body you send must be byte-identical. If your HTTP client re-serializes the body (adding spaces or reordering keys), the signature will not match. Serialize once, sign that string, send that same string.

JavaScript

const crypto = require('crypto-js');

const apiKey = 'Your API Key';
const secret = 'Your API Secret';
const timestamp = Math.floor(Date.now() / 1000);        // x-timestamp
const bodyData = JSON.stringify(request.body);          // compact encoding
const data = `${apiKey}${timestamp}POST${request.path}${bodyData}`;
const signature = crypto.HmacSHA256(data, secret).toString(crypto.enc.Hex); // x-signature

Python

import time
import math
import hashlib
import hmac
import json

api_key = "Your API Key"
secret = "Your API Secret"
item_id = "Item ID"
request_body = {"value": 400}

timestamp = str(math.floor(time.time()))                # x-timestamp
body_data = json.dumps(request_body, separators=(',', ':')) if request_body else ""
request_type = "POST"
request_path = f"/api/v3/data/item/{item_id}"

data = f"{api_key}{timestamp}{request_type}{request_path}{body_data}"
signature = hmac.new(
    secret.encode('utf-8'),
    data.encode('utf-8'),
    hashlib.sha256
).hexdigest()                                           # x-signature

Worked signature examples

Both examples use this sample key and secret:

apiKey = a20d0129-c121-433c-90e3-97068458584d
secret = s-397a324204a34921a72c9ec7a41c22f2

Case 1 — GET (no body)

timestamp = 1726554618
body      = (empty string)
path      = /api/v3/data/portfolio

signing string:
a20d0129-c121-433c-90e3-97068458584d1726554618GET/api/v3/data/portfolio

signature:
8a24943d24d6def02b38fd6522a4a899883243f30e9f3bda715ddbb6345e0abf

Case 2 — POST (with body)

timestamp = 1726554715
body      = {"value":400}
path      = /api/v3/data/item/27f59377-44e8-4a94-ac3b-f1e1c5080a48

signing string:
a20d0129-c121-433c-90e3-97068458584d1726554715POST/api/v3/data/item/27f59377-44e8-4a94-ac3b-f1e1c5080a48{"value":400}

signature:
584977d5822c3bf697ce929cc86ef4ac41331edba4f3d3c55d0d67361b76542e

Use these to verify your signing implementation before making live calls — if you reproduce both digests exactly, your signing is correct.


Response format

Every response uses the same envelope:

{
  "data": ...,
  "errorCode": 0
}
Field Description
data The payload. Shape depends on the endpoint; {} when the endpoint returns nothing
errorCode 0 on success

Conventions

Convention Detail
Ids UUID strings. Portfolio, item, section and sheet ids are all opaque — pass them back exactly as received
Dates YYYY-MM-DD strings
Timestamps x-timestamp is in seconds. Recap createdAt / updatedAt are in milliseconds
Monetary fields Returned as a Value object{ "amount": number, "currency": string } — not a bare number
Currency ISO codes, e.g. USD
Item ids An asset id and a debt id are interchangeable wherever {itemId} appears

API limits

There are different types of limits, all of which are subject to change at any time.

Limit Threshold
Rate 600 requests per minute
Kubera Essential 6000 requests per day (UTC)
Kubera Black 24000 requests per day (UTC)
Kubera for Business 24000 requests per day (UTC)

List portfolios

GET /api/v3/data/portfolio  

Fetch the list of portfolios. Start here — every other endpoint needs a portfolioId   or an itemId   that originates from this call.

curl --location --request GET 'https://api.kubera.com/api/v3/data/portfolio' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>'

Response

{
  "data": [
    {
      "id": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
      "name": "Mike",
      "currency": "USD"
    }
  ],
  "errorCode": 0
}

data   is an array of Portfolio objects.


Get portfolio data

GET /api/v3/data/portfolio/{portfolioId}  

Fetch a particular portfolio’s full data.

Path parameters

Parameter Required Description
portfolioId Yes From the List portfolios response
curl --location --request GET 'https://api.kubera.com/api/v3/data/portfolio/<PORTFOLIO_ID>' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>'

Response

{
  "data": {
    "id": "Portfolio id",
    "name": "Portfolio name",
    "ticker": "Portfolio currency",
    "timestamp": "Time",
    "asset": [],
    "debt": [],
    "document": [],
    "insurance": [],
    "totalAssets": { "amount": 0, "currency": 0 },
    "totalDebts": { "amount": 0, "currency": 0 },
    "netWorth": { "amount": 0, "currency": 0 },
    "costBasis": 0,
    "unrealizedGain": 0,
    "allocationByAssetClass": {
      "Cash": 0,
      "Crypto": 0,
      "Stock": 0,
      "Fund": 0,
      "Derivative": 0,
      "Investment": 0
    }
  },
  "errorCode": 0
}
Field Type Description
asset array Asset objects
debt array Debt objects — same shape as assets
insurance array Insurance objects — same shape as assets
document array Document objects
totalAssets, totalDebts, netWorth object Value objects
costBasis, unrealizedGain number
allocationByAssetClass object Allocation per asset class

Additional fields may be present — parse defensively and ignore unknown keys.

The id   values inside asset   and debt   are the {itemId}   used by the item and cash flow endpoints.


Create item

POST /api/v3/data/item  

Create a new manual asset or debt in a portfolio. The item is added to the portfolio’s first Asset section (or first Debt section when isDebt   is true  ), unless sheetName   / sectionName   match an existing section.

Ticker-backed itemvalue   is the quantity held:

curl --location --request POST 'https://api.kubera.com/api/v3/data/item' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "portfolioId": "<PORTFOLIO_ID>",
  "name": "Apple",
  "ticker": "AAPL",
  "value": 10,
  "cost": 1500
}'

Cash / manually-valued item — pass currency   instead of ticker   (or neither, to use the portfolio currency):

curl --location --request POST 'https://api.kubera.com/api/v3/data/item' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "portfolioId": "<PORTFOLIO_ID>",
  "name": "Savings",
  "currency": "USD",
  "value": 5000
}'

value   is overloaded — read this before sending

Item type What value means
ticker is set — stock, ETF, fund, crypto, precious metal The quantity held (shares/units), not a currency amount. Kubera derives market value as quantity × price
No ticker — cash or manually-valued item The monetary amount in the item’s currency

cost   is always a monetary amount (a total, not a per-unit price).

Sending {"ticker": "AAPL", "value": 1500}   creates 1,500 shares of Apple, not $1,500 of Apple.

Body properties

Property Type Required Description
portfolioId string Yes Portfolio to add the item to
name string Yes Item name
value number Yes Quantity (ticker items) or amount (cash items)
ticker string No Ticker symbol, e.g. AAPL, BTC. When set, value is the quantity
currency string No Currency code for a cash item, e.g. USD. Ignored when ticker is set. Defaults to the portfolio currency
cost number No Cost basis as a monetary amount
description string No
isDebt boolean No true to create a debt. Defaults to false (asset)
sheetName string No Target an existing section by sheet name (use with sectionName)
sectionName string No Target an existing section by name

symbol   is accepted as a legacy alias for ticker  . Prefer ticker  .

Response

{
  "data": {
    "success": true,
    "itemId": "cust-abc",
    "portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
    "sectionId": "9fcbee08-3524-470f-9352-ba99f0f6cf37"
  },
  "errorCode": 0
}

Update item

POST /api/v3/data/item/{itemId}  

Update a particular asset or debt.

Path parameters

Parameter Required Description
itemId Yes Asset id or debt id from the Get portfolio data response

Update the current value

curl --location --request POST 'https://api.kubera.com/api/v3/data/item/<ASSET_ID/DEBT_ID>' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "value": 400
}'

Update a historical value — include date  :

curl --location --request POST 'https://api.kubera.com/api/v3/data/item/<ASSET_ID/DEBT_ID>' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "value": 350,
  "date": "2026-01-01"
}'

Body properties

Property Type Description
name string
description string
value number Follows the same quantity-vs-amount rule as Create item
cost number Monetary amount
date string YYYY-MM-DD. Writes the value at that historical date instead of today

Archive item

POST /api/v3/data/item/{itemId}/archive  

Archive a single manual asset or debt. No request body is required.

Path parameters

Parameter Required Description
itemId Yes Asset id or debt id from the Get portfolio data response

Behavior depends on the item

Item Result
A holding (belongs to a parent account) Its value is zeroed. The parent and sibling holdings are untouched
A top-level account The account is archived
A connected/linked account (bank, brokerage, crypto, etc.) Rejected. Only manually-added items can be archived
curl --location --request POST 'https://api.kubera.com/api/v3/data/item/<ASSET_ID/DEBT_ID>/archive' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>'

Response

{
  "data": {},
  "errorCode": 0
}

Get cash flow entries

GET /api/v3/data/item/{itemId}/cashFlow  

Fetch the list of cash flow entries for an item.

curl --location --request GET 'https://api.kubera.com/api/v3/data/item/<ASSET_ID>/cashFlow' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>'

Response

{
  "data": [
    {
      "cashIn": 300,
      "cashOut": null,
      "currency": "USD",
      "date": "2025-02-02",
      "note": "Initial investment"
    }
  ],
  "errorCode": 0
}

data   is an array of cash flow objects.


Insert/update cash flow entry

POST /api/v3/data/item/{itemId}/cashFlow  

Add a cash flow entry to an item, or update the existing entry for that date.

curl --location --request POST 'https://api.kubera.com/api/v3/data/item/<ASSET_ID>/cashFlow' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "cashIn": 100.0,
  "cashOut": 200.0,
  "date": "2025-04-02",
  "currency": "USD",
  "note": "3rd investment"
}'

Body properties

Property Type Description
cashIn number Money in
cashOut number Money out
date string YYYY-MM-DD
currency string ISO currency code
note string Free-text note

Recap reports

A recap report can span years of history across every holding in a portfolio, so it is too slow to compute inside a single HTTP request. The API is therefore submit-then-poll:

Step Endpoint Returns
1. Submit GET /api/v3/data/portfolio/{portfolioId}/recap/report 202 with a reportId
2. Poll GET /api/v3/data/portfolio/{portfolioId}/recap/report/{reportId} 200 with status, and the results once complete

Only one recap computation per user runs at a time.

Submit recap report

GET /api/v3/data/portfolio/{portfolioId}/recap/report  

Queues the computation and returns the id of the report to poll for.

curl --location --request GET \
'https://api.kubera.com/api/v3/data/portfolio/<PORTFOLIO_ID>/recap/report?reports=networth,asset_classes&timeRanges=monthly&reportType=totals&dataPoints=12&currency=USD' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>'

Query parameters

Parameter Required Description
reports Yes Comma-separated report IDs. Case-insensitive
timeRanges Yes Comma-separated time ranges. Case-insensitive
reportType Yes totals or percentageAllocation
dataPoints Yes Integer ≥ 1. Caps the report to the most recent N points per series. quarterly and today are derived from the raw series, so the number of points actually returned for those ranges can differ
currency No ISO currency code, e.g. USD. Every value in the report is converted to it. Defaults to the portfolio’s own currency. An unknown code returns 400

Note: the query string is not part of the signing string — sign the path only. See Signature generation.

Response — 202 Accepted  

{
  "data": {
    "reportId": "7c1f0a3d9b6e42f58ad0c4e91b73d2a6f5c8e1097b4d6a3f2e8c5b91d7a0463f",
    "status": "pending",
    "pollIntervalMs": 2000
  },
  "errorCode": 0
}

The response also carries a Retry-After: 2   header. status   is pending   for a newly queued report, or processing   / completed   when the same request was already submitted and is still cached.

Response — another report is already running

If a different recap request is already in flight, the submit returns 202   without queuing anything:

{
  "data": {
    "status": "in_progress",
    "message": "A recap computation is already in progress for this user. Please retry shortly.",
    "retryAfterMs": 2000
  },
  "errorCode": 0
}

There is no reportId   in this response — retry the submit after the suggested interval. These retries do not consume your API quota.

Agent note: branch on the presence of reportId  , not on status  . No reportId   means nothing was queued and you must resubmit.

Poll recap report

GET /api/v3/data/portfolio/{portfolioId}/recap/report/{reportId}  

Fetch the state — and, once finished, the contents — of a submitted report.

curl --location --request GET \
'https://api.kubera.com/api/v3/data/portfolio/<PORTFOLIO_ID>/recap/report/<REPORT_ID>' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>'

Statuses

Status Meaning Next action
pending Queued, not started yet Keep polling
processing Being computed Keep polling
completed Finished — params and results are present Read results
failed The computation failed. The response carries an error message Submit again

Response — still running

{
  "data": {
    "reportId": "7c1f0a3d9b6e42f58ad0c4e91b73d2a6f5c8e1097b4d6a3f2e8c5b91d7a0463f",
    "status": "processing",
    "createdAt": 1756612800000,
    "updatedAt": 1756612812000
  },
  "errorCode": 0
}

createdAt   and updatedAt   are Unix timestamps in milliseconds.

Response — completed

{
  "data": {
    "reportId": "7c1f0a3d9b6e42f58ad0c4e91b73d2a6f5c8e1097b4d6a3f2e8c5b91d7a0463f",
    "status": "completed",
    "createdAt": 1756612800000,
    "updatedAt": 1756612830000,
    "params": {
      "portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
      "currency": "USD",
      "reportType": "totals",
      "reports": ["asset_classes"],
      "timeRanges": ["weekly"],
      "dataPoints": 2
    },
    "results": [
      {
        "timeRange": "weekly",
        "report": "asset_classes",
        "currency": "USD",
        "valueType": "number",
        "rows": [
          {
            "label": "Stocks",
            "category": "asset",
            "dataPoints": [
              { "date": "2026-08-22", "value": 4050112.88 },
              { "date": "2026-08-29", "value": 4126069.16 }
            ],
            "children": [
              {
                "label": "Apple Inc",
                "category": "asset",
                "itemId": "27f59377-44e8-4a94-ac3b-f1e1c5080a48",
                "sectionId": "0f0b3a2e-6d51-4e42-9f0e-6d51c1a2b3c4",
                "portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
                "isArchived": false,
                "dataPoints": [
                  { "date": "2026-08-22", "value": 878220.45 },
                  { "date": "2026-08-29", "value": 892401.00 }
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  "errorCode": 0
}

Reading results  

results   is flat: one entry per report × time range, so its length equals reports   × timeRanges  . A combination that produced no data still appears, with "rows": []  .

Each entry is a Recap Series object; each row is a Recap Row object.

Report IDs

Valid values for the reports   query parameter.

Report ID What it shows
networth Total net worth over time
sheets_and_sections Breakdown by your portfolio sheets and sections
asset_classes Assets by class (stocks, real estate, crypto, …)
investable Investable assets
investable_without_cash Investable assets excluding cash
investable_by_sheets_and_sections Investable assets within each sheet and section
investable_without_cash_by_sheets_and_sections Same, excluding cash
cash_on_hand Cash holdings over time
assets_and_currency Fiat assets by currency
stocks_and_geography Stocks by geography
stocks_and_sector Stocks by sector
stocks_and_marketcap Stocks by market cap tier
crypto Crypto holdings
brokerages Holdings by brokerage / institution
taxable_assets Assets by tax treatment (taxable, tax-deferred, tax-free)

Time ranges

Valid values for the timeRanges   query parameter: today  , daily  , weekly  , monthly  , quarterly  , yearly  .

Each range determines which historical snapshot represents a period — weekly keeps the Saturday point, monthly the last point of the calendar month, yearly the last point of the year.

Report types

Valid values for the reportType   query parameter.

reportType Result
totals valueType: "number" — each value is an amount in currency
percentageAllocation valueType: "percentage" — each value is a rounded share of the total, with the unrounded figure in preciseValue

Object reference

Primitives first, then the composite objects that use them.

Value object

Field Description
amount Numeric amount
currency Currency code

Rate object

Field Description
currency Currency
price Price

Connection object

Field Description
accountId Account id
aggregator Aggregator name
id Connection id
lastUpdatedTimestamp Last updated timestamp
providerName Institution name

Document object

Field Description
fileType MIME type
id Document id
name Name
size Size in bytes

Portfolio object

Returned by List portfolios.

Field Description
id Portfolio id — use as {portfolioId}
name Portfolio name
currency Portfolio currency

Asset / Debt / Insurance object

The three share one shape; category   tells them apart.

Field Description
id Asset / debt / insurance id — use as {itemId}
name Name
category Type — asset / debt / insurance
subType More detailed item type
description Item description
note Item note
value Value object
cost Cost object
quantity Quantity
rate Rate object
ownership Ownership percentage
irr IRR
cashIn Value object
cashOut Value object
committedCapital Value object
unfunded Value object
investable Investable type — non_investable / investable_easy_convert / investable_cash
ticker Ticker symbol
tickerId Ticker id
tickerSector Sector name
tickerSubType Asset class
isin ISIN
exchange Exchange name
accountNumber Account number
holdingsCount Number of holdings
parent Parent account object
sectionId Containing section id
sectionName Section name
sheetId Containing sheet id
sheetName Sheet name
costBasisForTax Value object
taxability taxable / tax-deferred / tax-free
taxRate Tax percentage
taxOnUnrealizedGain Value object
connection Connection object

Example — holding

A holding belongs to a parent account and carries a parent   object.

{
  "id": "9f4445b3-0b25-49fb-b1d9-4387e081639d_8E4L9XLl6MudjEpwPAAgivmdZRdBPJuvMPlPb",
  "name": "Nflx Feb 01'18 $355 Call",
  "sectionId": "9fcbee08-3524-470f-9352-ba99f0f6cf37",
  "sectionName": "Chase - Plaid IRA - 5555",
  "sheetId": "627a1796-f28e-4646-a796-d983e8576d46",
  "sheetName": "Sheet 6",
  "category": "asset",
  "value": { "amount": 110, "currency": "USD" },
  "ticker": "USD",
  "tickerId": 150,
  "tickerSubType": null,
  "tickerSector": "Other",
  "quantity": 110,
  "irr": 1099900,
  "investable": "investable_easy_convert",
  "ownership": 100,
  "description": null,
  "note": null,
  "isin": null,
  "subType": "derivative",
  "holdingsCount": 0,
  "cost": { "amount": 0.01, "currency": "USD" },
  "costBasisForTax": { "amount": 0.01, "currency": "USD" },
  "taxRate": 30,
  "taxability": "taxable",
  "taxOnUnrealizedGain": { "amount": 32.997, "currency": "USD" },
  "connection": {
    "aggregator": "plaid",
    "providerName": "Chase",
    "lastUpdatedTimestamp": 1723452761,
    "id": "9DorR9zEmNsqA6xvyM1JtmolBWNWNjfRzBpv4",
    "accountId": "P6LzkBlD1Xhn3B649KLvcxE14Jm37Vuo47Wzk"
  },
  "parent": {
    "id": "9f4445b3-0b25-49fb-b1d9-4387e081639d",
    "name": "Chase - Plaid IRA - 5555"
  }
}

Example — account

A top-level account has no parent   and reports holdingsCount   > 0.

{
  "id": "9f4445b3-0b25-49fb-b1d9-4387e081639d",
  "name": "Chase - Plaid IRA - 5555",
  "sectionId": "9fcbee08-3524-470f-9352-ba99f0f6cf37",
  "sectionName": "Chase - Plaid IRA - 5555",
  "sheetId": "627a1796-f28e-4646-a796-d983e8576d46",
  "sheetName": "Sheet 6",
  "category": "asset",
  "value": { "amount": 249.20000000000002, "currency": "USD" },
  "ticker": "USD",
  "tickerId": 150,
  "tickerSubType": null,
  "tickerSector": "Other",
  "quantity": 249.20000000000002,
  "irr": 522.8442889277682,
  "investable": "investable_easy_convert",
  "ownership": 100,
  "description": null,
  "note": null,
  "isin": null,
  "subType": "investment",
  "holdingsCount": 2,
  "cost": { "amount": 40.01, "currency": "USD" },
  "costBasisForTax": { "amount": 40.01, "currency": "USD" },
  "taxRate": 30,
  "taxability": "taxable",
  "taxOnUnrealizedGain": { "amount": 62.757000000000005, "currency": "USD" },
  "connection": {
    "aggregator": "plaid",
    "providerName": "Chase",
    "lastUpdatedTimestamp": null,
    "id": "9DorR9zEmNsqA6xvyM1JtmolBWNWNjfRzBpv4",
    "accountId": "P6LzkBlD1Xhn3B649KLvcxE14Jm37Vuo47Wzk"
  }
}

Cash flow object

Field Type Description
cashIn number | null Money in
cashOut number | null Money out
currency string Currency code
date string YYYY-MM-DD
note string Free-text note

Recap Series object

One entry in results  .

{
  "timeRange": "weekly",
  "report": "asset_classes",
  "currency": "USD",
  "valueType": "number",
  "rows": []
}
Field Description
timeRange One of the requested time ranges
report One of the requested report IDs
currency Always present
valueType number or percentage — see report types
rows Array of Recap Row objects

Recap Row object

Field Present on Description
label All rows Display name of the group or holding
dataPoints All rows The row’s time series, ordered oldest → newest. Each point is { "date": "YYYY-MM-DD", "value": number }
children Group rows Nested rows. Omitted on leaf rows — never an empty array
category Most rows asset or debt
itemId Leaf rows The asset/debt id — use it with the item endpoints
sectionId, sheetId Where applicable Ids of the containing section and sheet
portfolioId Leaf rows Owning portfolio — differs from the requested one for linked portfolios
isArchived Leaf rows Present and true when the item is archived
isLinkedPortfolio Leaf rows Present and true when the row comes from a linked portfolio

To distinguish a group row from a leaf row, test for the presence of children   — leaf rows omit the key entirely.


Unofficial SDKs

These are not maintained or verified by the Kubera team. Use at your own risk.