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

Profile

Portfolio endpoints

Sheet endpoints

Section endpoints

Item endpoints

Cash flow endpoints

Fund schedule endpoints

Recap report endpoints

Reference


Endpoint index

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

# Group Purpose Method Path Body Returns
1 Profile Get profile GET /api/v3/data/profile — Account holder details
2 Look up a ticker GET /api/v3/data/ticker?name={q} — ticker[]
3 Portfolios List portfolios GET /api/v3/data/portfolio — portfolio[]
4 Get portfolio data GET /api/v3/data/portfolio/{portfolioId} — Portfolio with assets, debts, totals
5 Create portfolio POST /api/v3/data/portfolio Yes New portfolio
6 Update portfolio POST /api/v3/data/portfolio/{portfolioId} Yes Updated portfolio
7 Delete portfolio DELETE /api/v3/data/portfolio/{portfolioId} — Success envelope. Permanent
8 Portfolio CAGR GET /api/v3/data/portfolio/{portfolioId}/cagr — CAGR per window + benchmarks
9 Net worth history GET /api/v3/data/portfolio/{portfolioId}/history — Daily net worth series
10 Top movers GET /api/v3/data/portfolio/{portfolioId}/topMovers — Biggest movers by day and month
11 Sheets Create sheet POST /api/v3/data/portfolio/{portfolioId}/sheet Yes New sheet + seeded section
12 Update sheet POST /api/v3/data/sheet/{sheetId} Yes Updated sheet
13 Archive sheet POST /api/v3/data/sheet/{sheetId}/archive — Success envelope
14 Sections Create section POST /api/v3/data/sheet/{sheetId}/section Yes New section
15 Update section POST /api/v3/data/section/{sectionId} Yes Updated section
16 Archive section POST /api/v3/data/section/{sectionId}/archive — Success envelope
17 Items Create item POST /api/v3/data/item Yes New item ids
18 Update item POST /api/v3/data/item/{itemId} Yes Success envelope
19 Archive item POST /api/v3/data/item/{itemId}/archive — Empty data
20 Item value history GET /api/v3/data/item/{itemId}/history — Value series for one item
21 Cash flow Get cash flow GET /api/v3/data/item/{itemId}/cashFlow — cashFlow[]
22 Insert/update cash flow POST /api/v3/data/item/{itemId}/cashFlow Yes Success envelope
23 Delete cash flow entry DELETE /api/v3/data/item/{itemId}/cashFlow/{date} — Deleted entry. Permanent
24 Fund schedule Get fund schedule GET /api/v3/data/item/{itemId}/fundSchedule — fundSchedule[]
25 Insert/update fund schedule POST /api/v3/data/item/{itemId}/fundSchedule Yes Created/updated entry
26 Delete fund schedule entry DELETE /api/v3/data/item/{itemId}/fundSchedule/{type}/{date} — Deleted entry. Permanent
27 Recap Request recap report GET /api/v3/data/portfolio/{portfolioId}/recap/report — 202 + reportId
28 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)

Endpoints added for parity with Kubera's AI tools — Profile, ticker lookup, the portfolio create/update/delete and analytics endpoints, and everything under Sheets, Sections and Fund schedule — are counted against a separate daily allowance of the same size, rather than sharing the one above. The per-minute rate limit is shared.


Get profile

GET /api/v3/data/profile    

Returns the account holder's own details. Useful for confirming which account an API key belongs to, and for reading the time zone Kubera uses when it rolls up daily values.

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

Response

{
  "data": {
    "name": "Ada Lovelace",
    "utcOffset": -330,
    "tz": "Asia/Kolkata",
    "dateOfBirth": ""
  },
  "errorCode": 0
}

utcOffset     is in minutes. dateOfBirth     is an empty string when it has not been set.


Look up a ticker

GET /api/v3/data/ticker    

Resolves a name or symbol to Kubera's tickers. Call this before sending a ticker     to Create item — the same symbol exists on several exchanges and as different instrument types, so pick the match whose type     / subType     , market     / code     and currency     fit the holding you mean.

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

Query parameters

Parameter Type Required Description
name string Yes Name or symbol to search for

Response

{
  "data": [
    {
      "id": 4321,
      "ticker": "AAPL",
      "name": "Apple Inc",
      "type": "stock",
      "subType": "Common Stock",
      "code": "AAPL",
      "market": "NASDAQ",
      "currency": "USD",
      "countryCode": "US"
    }
  ],
  "errorCode": 0
}

Exact matches come first, and at most 10 matches are returned. An empty array simply means nothing matched. Each entry is a Ticker object; pass its ticker     value back when creating an item.


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 portfolio

POST /api/v3/data/portfolio    

Creates an empty portfolio, pre-seeded with Kubera's default sheets and sections (Investments, Real Estate, Others, Credit Cards, Loans, and so on). Call Get portfolio data on the new portfolio to see what it was given before adding sheets of your own.

curl --location --request POST '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>' \
--data '{
  "name": "Family Trust",
  "currency": "USD"
}'

Body properties

Property Type Required Description
name string Yes Portfolio name
currency string No Reporting currency, e.g. USD. Defaults to the currency inferred for the account. Ignored for Kubera for Business users — those portfolios always take the organisation's currency, so read currency back off the response rather than assuming the value you sent

Response

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

Errors — 400   for an invalid currency; 500 Maximum portfolio limit reached   when the plan caps portfolio count.


Update portfolio

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

Renames a portfolio and/or changes its reporting currency. This is the portfolio itself — to change an asset or debt inside it use Update item.

Changing currency     re-denominates the entire portfolio and makes Kubera recompute returns and rebuild its charts, which takes a while to settle. Send it only when the reporting currency really is changing. It is not how you record an item held in another currency — set the currency on the item instead.

curl --location --request POST '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>' \
--data '{
  "name": "Family Trust (2026)"
}'

Body properties

Property Type Required Description
name string No New portfolio name
currency string No New reporting currency. Must be a fiat currency

At least one of the two is required.

Response

{
  "data": {
    "id": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
    "name": "Family Trust (2026)",
    "currency": "USD"
  },
  "errorCode": 0
}

Errors — 400 Nothing to update   when both fields are absent; 400   for an invalid or non-fiat currency.


Delete portfolio

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

This is permanent. It deletes the portfolio and everything in it — every sheet, section, asset, debt, insurance policy, document, cash flow entry, and the complete value history of all of them. Nothing is archived and nothing is recoverable afterwards. To remove only part of a portfolio, archive the individual sheets, sections or items instead — archiving preserves their history so past net worth stays correct.

curl --location --request DELETE '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>'

Path parameters

Parameter Type Description
portfolioId string Portfolio to delete permanently

Response

{
  "data": {
    "success": true,
    "message": "Portfolio deleted successfully"
  },
  "errorCode": 0
}

Errors — 400 Cannot delete the only portfolio in the account  .


Portfolio CAGR

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

Compound annual growth rate for the portfolio, over each of Kubera's standard windows, for both net worth and investable assets — plus the same-period return of a set of market benchmarks to compare against.

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

Path parameters

Parameter Type Description
portfolioId string Portfolio to report on

Response

{
  "data": {
    "portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
    "portfolioName": "Personal",
    "portfolioCurrency": "USD",
    "portfolioCurrencyTickerId": 171,
    "portfolioStartDate": "2021-03-14",
    "isCustomStartDate": false,
    "cagrPercentages": {
      "ytd_networth": 7.42,
      "ytd_investable": 9.11,
      "qtd_networth": 2.03,
      "qtd_investable": 2.58,
      "yearly_networth": 11.87,
      "yearly_investable": 14.02,
      "alltime_networth": 9.65,
      "alltime_investable": 12.30
    },
    "cagrOldValues": {
      "ytd_networth": { "oldValue": 1250000, "date": "2026-01-01" },
      "alltime_networth": { "oldValue": 480000, "date": "2021-03-14" }
    },
    "market": {
      "GSPC.INDX": 10.44,
      "IXIC.INDX": 13.91,
      "BTC.CC": 22.07
    }
  },
  "errorCode": 0
}

Field notes

Field Description
cagrPercentages CAGR as a percentage for each window. Eight keys: ytd, qtd, yearly and alltime, each as _networth and _investable. Present only once the portfolio has data for today
cagrOldValues The starting value each window was measured from, as { oldValue, date }. Same eight keys. Always present
market Benchmark returns over the same span as alltime, keyed by index symbol (GSPC.INDX, BTC.CC, …). null when the portfolio has no start date. A benchmark that cannot be priced is omitted, so treat every key as optional
portfolioStartDate Where the all-time window begins — YYYY-MM-DD, or null if the portfolio has no history yet

Portfolio net worth history

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

The portfolio's net worth over time, one data point per day, oldest first. This is the series behind the net worth chart in the app.

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

Path parameters

Parameter Type Description
portfolioId string Portfolio to report on

Response

{
  "data": {
    "portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
    "portfolioName": "Personal",
    "portfolioCurrency": "USD",
    "portfolioCurrencyTickerId": 171,
    "portfolioStartDate": "2021-03-14",
    "isCustomStartDate": false,
    "portfolioDataPoints": [
      {
        "date": "2026-09-26",
        "value": 1342500,
        "investibleTotal": 910400,
        "assetTotal": 1520000,
        "debtTotal": 177500
      }
    ]
  },
  "errorCode": 0
}

value     is net worth — assetTotal     minus debtTotal     — in the portfolio currency. portfolioDataPoints     is an empty array when the portfolio has no history yet.


Top movers

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

The items whose value moved most over the last day and the last month, assets and debts ranked separately, largest change first.

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

Path parameters

Parameter Type Description
portfolioId string Portfolio to report on

Response

{
  "data": {
    "change": {
      "day": {
        "baseDate": "2026-09-27",
        "currentDate": "2026-09-28",
        "asset": [
          {
            "name": "Apple",
            "value": 24100,
            "currentValue": 24850,
            "archived": 0
          }
        ],
        "assetChangeTotal": 750,
        "assetTotal": 1520000,
        "debt": [],
        "debtChangeTotal": 0,
        "debtTotal": 177500
      },
      "month": { "...": "same shape, compared against a month ago" },
      "week": null,
      "year": null,
      "lastYearEnd": null
    },
    "currency": "USD",
    "portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
    "portfolioName": "Personal",
    "tickerId": 171,
    "tsCreated": 1790000000
  },
  "errorCode": 0
}

Only day     and month     are populated. The week     , year     and lastYearEnd     keys are always present but unset on this endpoint. A window is also unset when the portfolio is younger than it.

Within a window, value     is the item's value on baseDate     and currentValue     is its value today, both in the portfolio currency; archived     is 1     for an item archived since. tsCreated     is in seconds.


Create sheet

POST /api/v3/data/portfolio/{portfolioId}/sheet    

A sheet is a top-level tab that holds sections; sections hold the items. A sheet belongs to exactly one category and can only hold items of that category, so choose it deliberately.

A new sheet arrives with one empty section already in it (“Section 1”), so you can add items straight away. Use Create section only when you want additional sections.

Insurance     cannot be created here. A portfolio is given its single Insurance sheet when the portfolio itself is created, and there is no way to add another. Update sheet still accepts Insurance     , for that existing sheet.

curl --location --request POST 'https://api.kubera.com/api/v3/data/portfolio/<PORTFOLIO_ID>/sheet' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "name": "Private Equity",
  "category": "Asset",
  "updateFrequency": 3
}'

Body properties

Property Type Required Description
name string Yes Sheet name
category string No Asset (default) or Debt. Case-insensitive
updateFrequency number No How often Kubera reminds you to refresh manually-valued items on this sheet. 0 never (default), 1 weekly, 6 fortnightly, 2 monthly, 3 quarterly, 4 half-yearly, 5 yearly

Response

{
  "data": {
    "id": "5c2f0e21-7a44-4c0e-9a1e-2b6f0d3c8a19",
    "name": "Private Equity",
    "category": "Asset",
    "portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
    "sortKey": "3",
    "updateFrequency": 3,
    "tsArchive": 0,
    "sections": [
      {
        "id": "9fcbee08-3524-470f-9352-ba99f0f6cf37",
        "name": "Section 1",
        "sheetId": "5c2f0e21-7a44-4c0e-9a1e-2b6f0d3c8a19",
        "sortKey": "0",
        "expanded": 1,
        "tsArchive": 0
      }
    ]
  },
  "errorCode": 0
}

sortKey     is assigned automatically, appending the sheet after the existing sheets in its category. See the Sheet object.

Errors — 400 "category" must be one of [Asset, Debt]  .


Update sheet

POST /api/v3/data/sheet/{sheetId}    

Renames a sheet, reorders it, or changes its category or reminder cadence. Send only the fields you want changed; anything omitted keeps its current value.

Changing category     moves the whole sheet — and every item on it — between the asset, debt and insurance sides of the portfolio. That flips whether those items count towards net worth as owned or owed. A sheet cannot be moved to a different portfolio.

curl --location --request POST 'https://api.kubera.com/api/v3/data/sheet/<SHEET_ID>' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "name": "Private Markets",
  "sortKey": "1"
}'

Body properties

Property Type Required Description
name string No New sheet name
category string No Asset, Debt or Insurance
updateFrequency number No Same values as Create sheet
sortKey string No Numeric string positioning the sheet among the sheets of its category. "0" is first

At least one field is required.

Response

{
  "data": {
    "id": "5c2f0e21-7a44-4c0e-9a1e-2b6f0d3c8a19",
    "name": "Private Markets",
    "category": "Asset",
    "portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
    "sortKey": "1",
    "updateFrequency": 3,
    "tsArchive": 0
  },
  "errorCode": 0
}

The response is the updated sheet without its sections     .

Errors — 400 Nothing to update  ; 400 Invalid sheetId/Archived Sheet  .


Archive sheet

POST /api/v3/data/sheet/{sheetId}/archive    

Archives a sheet and everything under it — all of its sections and all of their items. This is exactly what the app's delete-sheet action does: the sheet stops appearing and stops counting towards net worth, while past values are preserved so historical net worth stays correct.

A sheet can only be un-archived from the Kubera app, not through this API.

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

Path parameters

Parameter Type Description
sheetId string Sheet to archive

Response

{
  "data": {
    "success": true,
    "message": "Sheet archived successfully"
  },
  "errorCode": 0
}

Errors — 400 Invalid sheetId/Archived Sheet  , for an unknown sheet or one already archived.


Create section

POST /api/v3/data/sheet/{sheetId}/section    

A section is the group that actually holds assets and debts, so a sheet needs at least one before anything can go in it. The section inherits its sheet's category.

Once it exists, put items in it by passing the sheet and section names to Create item's sheetName     / sectionName     . You only need this endpoint to create a section up front — Create item also makes one on demand when its sheetName     matches a sheet and its sectionName     matches nothing in it.

curl --location --request POST 'https://api.kubera.com/api/v3/data/sheet/<SHEET_ID>/section' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "name": "Venture Funds",
  "expanded": 1
}'

Body properties

Property Type Required Description
name string Yes Section name
expanded number No Whether the section renders expanded in the app: 1 (default) or 0. Cosmetic only

Response

{
  "data": {
    "id": "9fcbee08-3524-470f-9352-ba99f0f6cf37",
    "name": "Venture Funds",
    "sheetId": "5c2f0e21-7a44-4c0e-9a1e-2b6f0d3c8a19",
    "sortKey": "2",
    "expanded": 1,
    "columnSortKey": "",
    "columnSortOrder": "",
    "tsArchive": 0
  },
  "errorCode": 0
}

See the Section object.

Errors — 400 Invalid sheetId/Archived Sheet  .


Update section

POST /api/v3/data/section/{sectionId}    

Renames a section, reorders it, collapses or expands it, or moves it to another sheet in the same portfolio — its items move with it. Send only the fields you want changed.

Moving a section to a sheet of a different category changes whether its items count as owned or owed. A sheet in a different portfolio is rejected.

curl --location --request POST 'https://api.kubera.com/api/v3/data/section/<SECTION_ID>' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "name": "Venture Capital",
  "expanded": 0
}'

Body properties

Property Type Required Description
name string No New section name
sortKey string No Numeric string positioning the section within its sheet
expanded number No 1 or 0
sheetId string No Move the section into this sheet. Must be in the same portfolio

At least one field is required.

Response

{
  "data": {
    "id": "9fcbee08-3524-470f-9352-ba99f0f6cf37",
    "name": "Venture Capital",
    "sheetId": "5c2f0e21-7a44-4c0e-9a1e-2b6f0d3c8a19",
    "sortKey": "2",
    "expanded": 0,
    "columnSortKey": "",
    "columnSortOrder": "",
    "tsArchive": 0
  },
  "errorCode": 0
}

Errors — 400 Nothing to update  ; 400 Cannot move a section to a sheet in another portfolio  ; 400 Invalid sectionId/Archived Section  .


Archive section

POST /api/v3/data/section/{sectionId}/archive    

Archives a section and every item in it, preserving past values the same way Archive sheet does. To archive a single item instead, use Archive item.

Un-archiving is only possible in the Kubera app.

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

Path parameters

Parameter Type Description
sectionId string Section to archive

Response

{
  "data": {
    "success": true,
    "message": "Section archived successfully"
  },
  "errorCode": 0
}

Errors — 400 Invalid sectionId/Archived Section  .


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 item — value       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
costCurrency string No Currency for cost. Fiat only; falls back to the item's currency, then the portfolio's
ownership number No Percentage of the item the user owns — the app's “Portfolio Share”. 0–999, defaults to 100. Values above 100 are allowed, for a geared position
cmtdCap number No Committed capital, as a monetary amount. Never negative. Pairs with the fund schedule
parentId string No Create the item as a holding inside this existing item — e.g. a stock inside a brokerage account. See the note below
symbol string No Deprecated alias for ticker, used only when ticker is absent

portfolioShare     and share     are accepted as aliases for ownership     , in that order of precedence. Prefer ownership     .

cmtdCap     and ownership     apply to top-level items only. Setting either on a holding is rejected: committed capital rolls up into the parent item, and a holding's ownership is read from its parent — so set them on the parent instead.

parentId     turns a plain parent into a manual account whose value becomes the sum of its holdings. It rejects connected accounts, items that are already holdings, a parent in another portfolio, and ticker-backed plain items. sheetName     , sectionName     and isDebt     are ignored when it is set, since the holding inherits its parent's placement.

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
costCurrency string Currency for cost. Fiat only; falls back to the item's currency, then the portfolio's
ownership number Percentage of the item the user owns — the app's “Portfolio Share”. 0–999, defaults to 100. Values above 100 are allowed, for a geared position
cmtdCap number Committed capital, as a monetary amount. Never negative. Pairs with the fund schedule

portfolioShare     and share     are accepted as aliases for ownership     , in that order of precedence. Prefer ownership     .

cmtdCap     and ownership     apply to top-level items only. Setting either on a holding is rejected: committed capital rolls up into the parent item, and a holding's ownership is read from its parent — so set them on the parent instead.


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 item value history

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

The recorded value of a single item over time, oldest first. For a ticker-backed holding this is its market value; for a manually-valued item it is the values you have entered.

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

Path parameters

Parameter Type Description
itemId string Item whose history to fetch

Response

{
  "data": {
    "itemId": "cust-abc",
    "itemName": "Apple",
    "itemSymbol": "AAPL",
    "itemCurrency": "USD",
    "itemCurrencyTickerId": 171,
    "itemDataPoints": [
      { "date": "2026-09-26", "value": 24100, "quantity": 100 },
      { "date": "2026-09-27", "value": 24850, "quantity": 100 }
    ]
  },
  "errorCode": 0
}

value     is the market value in the item's currency. quantity     is the raw stored figure — the number of units for a ticker-backed holding — and is null     where none was recorded on or before that date. itemSymbol     is an empty string for an item with no ticker.


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

Delete cash flow entry

DELETE /api/v3/data/item/{itemId}/cashFlow/{date}    

This is permanent. It removes the single cash flow entry the item holds on that date, and cannot be undone.

The entry is addressed by date rather than by a row id: an item holds at most one entry per date — the same way Insert/update cash flow entry addresses one — and Get cash flow entries returns no id to quote back.

One entry per call; there is no bulk form. To clear an item's whole cash flow, delete each entry in turn, or do it in the app.

Kubera recomputes the item's IRR from the entries that remain, so deleting one changes its reported return. Deleting the last entry leaves the item with no cash-flow-based return — it is not switched back to a cost-basis IRR. This matches what the app itself does.

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

Path parameters

Parameter Type Description
itemId string Item the entry belongs to
date string YYYY-MM-DD, as returned by Get cash flow entries

Response

{
  "data": {
    "success": true,
    "deleted": true,
    "date": "2025-04-02",
    "cashIn": 100,
    "cashOut": 200,
    "currency": "USD",
    "note": "3rd investment",
    "message": "Cash flow entry deleted successfully"
  },
  "errorCode": 0
}

The response echoes the entry that was removed, so you can report what went.

Errors — 400 No cash flow entry on <date> for this item  . The message also names the dates that do have entries, so a caller can correct itself.


Get fund schedule

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

Returns an item's dated capital calls and distributions — typically for a private equity or other capital-commitment holding, alongside the item's committed capital (cmtdCap     ).

These entries are a schedule, not money that has moved. Kubera does not use them to compute IRR, returns, net worth or the item's value; nothing here changes a reported number. Actual money in and out lives in cash flow, and that is what drives IRR. An item may have both, and they will not agree if a call was scheduled but not yet paid.

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

Path parameters

Parameter Type Description
itemId string The item

Response

{
  "data": {
    "fundSchedule": [
      {
        "type": "capitalcall",
        "date": "2026-01-15",
        "value": 50000,
        "currency": "USD",
        "note": "Call 3 of 8"
      },
      {
        "type": "distribution",
        "date": "2026-06-30",
        "value": 12000,
        "currency": "USD",
        "note": ""
      }
    ]
  },
  "errorCode": 0
}

An empty array simply means no entries have been recorded. Each entry is a Fund schedule object.


Insert/update fund schedule entry

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

Adds a capital call or distribution to an item, or updates the existing entry of that type on that date.

The key is type     and date     together: an item may hold one capital call and one distribution on the same date, but not two of the same type. Writing a distribution therefore never disturbs a capital call on that date. When an entry of the same type already exists on that date it is updated in place — value     is replaced, not added to — and fields you leave out keep their stored values.

curl --location --request POST 'https://api.kubera.com/api/v3/data/item/<ASSET_ID>/fundSchedule' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>' \
--data '{
  "type": "capitalcall",
  "date": "2026-01-15",
  "currency": "USD",
  "value": 50000,
  "note": "Call 3 of 8"
}'

Body properties

Property Type Required Description
type string Yes capitalcall or distribution
date string Yes YYYY-MM-DD. May be future- or past-dated
currency string Yes Currency of value
value number No Positive for both types — the direction comes from type, not the sign
note string No Short note, e.g. Call 3 of 8

Response

{
  "data": {
    "success": true,
    "created": false,
    "type": "capitalcall",
    "date": "2026-01-15",
    "value": 50000,
    "currency": "USD",
    "note": "Call 3 of 8",
    "previous": {
      "value": 45000,
      "note": "Call 3 of 8 (provisional)"
    }
  },
  "errorCode": 0
}

created     is true     when a new entry was inserted and false     when an existing one was updated. previous     appears only on an update, carrying the values that were overwritten.

Errors — 400 Invalid type: <x>. Expected capitalcall or distribution  ; 400 Invalid currency: <x>  .


Delete fund schedule entry

DELETE /api/v3/data/item/{itemId}/fundSchedule/{type}/{date}    

This is permanent. It removes the single entry of that type on that date. Because the same date may hold one of each type, the type decides which of the two goes — passing the wrong one removes the wrong entry.

One entry per call; there is no bulk form.

No computed figure changes as a result. Any cash flow recorded for the same event is separate and is not removed by this — use Delete cash flow entry for that.

curl --location --request DELETE 'https://api.kubera.com/api/v3/data/item/<ASSET_ID>/fundSchedule/capitalcall/2026-01-15' \
--header 'Content-Type: application/json' \
--header 'x-api-token: <API_KEY>' \
--header 'x-timestamp: <TIMESTAMP>' \
--header 'x-signature: <SIGNATURE>'

Path parameters

Parameter Type Description
itemId string Item the entry belongs to
type string capitalcall or distribution
date string YYYY-MM-DD, as returned by Get fund schedule

Response

{
  "data": {
    "success": true,
    "deleted": true,
    "type": "capitalcall",
    "date": "2026-01-15",
    "value": 50000,
    "currency": "USD",
    "note": "Call 3 of 8",
    "message": "Fund schedule entry deleted successfully"
  },
  "errorCode": 0
}

Errors — 400 No <type> entry on <date> for this item  . The message also names the entries that do exist, as type date   pairs.


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 request-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.

Request 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 requested and is still cached.

Response — another report is already running

If a different recap request is already in flight, the request 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 request 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 request it again.

Poll recap report

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

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

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

Ticker object

Field Type Description
id number Kubera's internal ticker id
ticker string The short symbol. This is the value to send as ticker when creating an item
name string Full instrument name
type string stock, fund, derivative, bond, crypto, fiat or index
subType string Finer classification, e.g. Common Stock
code string Exchange code for the instrument
market string Exchange or market, e.g. NASDAQ
currency string Currency the instrument is priced in
countryCode string ISO country code

Sheet object

Field Type Description
id string Sheet id
name string Sheet name
category string Asset, Debt or Insurance. Decides which side of net worth the sheet's items count on
portfolioId string Portfolio the sheet belongs to
sortKey string Numeric string ordering the sheet within its category
updateFrequency number Reminder cadence for refreshing manual values. See Create sheet
tsArchive number Epoch seconds when the sheet was archived; 0 when it is active
irr string Internal rate of return, when computed
sections array Section objects. Returned by Create sheet, not by Update sheet

Section object

Field Type Description
id string Section id
name string Section name
sheetId string Sheet the section sits on
sortKey string Numeric string ordering the section within its sheet
expanded number 1 if it renders expanded in the app, 0 if collapsed. Cosmetic only
columnSortKey string Column the section is sorted by in the app
columnSortOrder string Sort direction for that column
tsArchive number Epoch seconds when archived; 0 when active

Fund schedule object

A scheduled capital call or distribution. These do not affect IRR, returns or net worth — see Get fund schedule.

Field Type Description
type string capitalcall or distribution
date string YYYY-MM-DD. May be in the future
value number Always positive — direction comes from type
currency string Currency of value
note string Free-text note; empty string when unset

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.