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, and archive items.
On this page
- Authentication
- Required headers
- Signature generation
- More examples
- API limits
- Export – portfolio list
- Export – portfolio data
- Create – item
- Update – item
- Archive – item
- Get cash flow entries
- Insert/update cash flow
- Object reference
- Unofficial SDKs
Authentication
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:
| 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 the API key, timestamp, HTTP method, request path, and the compact JSON body, then sign it with HMAC-SHA256 using your secret. The hex digest is your x-signature .
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); // use 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 requests
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
bodyData = json.dumps(request_body, separators=(',', ':')) if request_body else ""
requestType = "POST"
request_path = f"/api/v3/data/item/{item_id}"
data = f"{api_key}{timestamp}{requestType}{request_path}{bodyData}"
signature = hmac.new(
secret.encode('utf-8'),
data.encode('utf-8'),
hashlib.sha256
).hexdigest() # x-signature
Note
The body must be serialized with compact encoding (no spaces between keys/values), and the exact same string must be sent as the request body. For a request with no body (e.g. GET), use an empty string.
For example, with Request.body = { value: 400 } and Request.path = /api/v3/data/item/<ASSET_ID/DEBT_ID> , add the headers:
x-timestamp: timestamp x-signature: signature
More examples
Worked examples using a sample key and secret:
const crypto = require('crypto-js');
let apiKey = 'a20d0129-c121-433c-90e3-97068458584d';
let secret = 's-397a324204a34921a72c9ec7a41c22f2';
Case 1 — GET (no body)
timestamp = 1726554618; bodyData = ``; data = `a20d0129-c121-433c-90e3-97068458584d1726554618GET/api/v3/data/portfolio`; signature = 8a24943d24d6def02b38fd6522a4a899883243f30e9f3bda715ddbb6345e0abf
Case 2 — POST (with body)
timestamp = 1726554715;
bodyData = `{"value":400}`;
data = `a20d0129-c121-433c-90e3-97068458584d1726554715POST/api/v3/data/item/27f59377-44e8-4a94-ac3b-f1e1c5080a48{"value":400}`;
signature = 584977d5822c3bf697ce929cc86ef4ac41331edba4f3d3c55d0d67361b76542e
API limits
There are different types of limits, all of which are subject to change at any time.
| Limit | Threshold |
|---|---|
| Rate | 30 requests per minute |
| Kubera Essential | 500 requests per day (UTC) |
| Kubera Black | 2000 requests per day (UTC) |
Export – portfolio list
Fetch the list of portfolios.
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": portfolio[],
"errorCode": 0
}
Portfolio object
{
"id": Portfolio Id,
"name": Portfolio Name,
"currency": Portfolio Currency
}
Example:
{
"id": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
"name": "Mike",
"currency": "USD"
}
Export – portfolio data
Fetch a particular user portfolio’s data.
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>'
Note
Use the PORTFOLIO_ID from the Export – user portfolio list response.
Basic response
{
"data": {
"asset": asset[],
"debt": debt[],
"id": Portfolio id,
"name": Portfolio Name,
"ticker": Portfolio Currency,
"timestamp": Time,
"document": document[],
"insurance": insurance[],
"totalAssets": { "amount": number, "currency": number },
"totalDebts": { "amount": number, "currency": number },
"netWorth": { "amount": number, "currency": number },
"costBasis": Number,
"unrealizedGain": Number,
"allocationByAssetClass": {
"Cash": Number,
"Crypto": Number,
"Stock": Number,
"Fund": Number,
"Derivative": Number,
"Investment": Number
}
},
"errorCode": 0
}
(Additional fields may be provided.)
Create – item
Note
Use the PORTFOLIO_ID from the Export – user portfolio list response.
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.
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
}'
For a 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
}'
Important
The value field is overloaded:
- When
tickeris set (a ticker-backed holding such as a stock, ETF, fund, crypto or precious metal),valueis the quantity held (shares/units), not a currency amount. Kubera derives the market value as quantity × price. - For a cash / manually-valued item,
valueis the monetary amount in the item’s currency.
cost is always a monetary amount (a total, not a per-unit price).
Allowed properties
| Property | Type | Description |
|---|---|---|
portfolioId |
string | Required. Portfolio to add the item to. |
name |
string | Required. Item name. |
value |
number | Required. Quantity (ticker items) or amount (cash items). |
ticker |
string | Ticker symbol, e.g. AAPL, BTC. When set, value is the quantity. |
currency |
string | Currency code for a cash item, e.g. USD. Ignored when ticker is set. Defaults to the portfolio currency. |
cost |
number | Cost basis as a monetary amount. |
description |
string | — |
isDebt |
boolean | true to create a debt. Defaults to false (asset). |
sheetName |
string | Target an existing section by sheet name (with sectionName ). |
sectionName |
string | Target an existing section by name. |
Note: symbol is accepted as a legacy alias for ticker ; prefer ticker .
Response
{
"data": {
"success": true,
"itemId": New Item Id,
"portfolioId": Portfolio Id,
"sectionId": Section Id
},
"errorCode": 0
}
Example:
{
"data": {
"success": true,
"itemId": "cust-abc",
"portfolioId": "6eb1ac79-2ae1-49e6-aada-3a5fb4fdce55",
"sectionId": "9fcbee08-3524-470f-9352-ba99f0f6cf37"
},
"errorCode": 0
}
Update – item
Note
Use the ASSET_ID / DEBT_ID from the Export – user portfolio data response.
Update a particular asset or debt.
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
}'
You can modify historical values by sending a POST request in the following format:
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"
}'
Allowed properties
| Property | Type | Description |
|---|---|---|
name |
string | — |
description |
string | — |
value |
number | — |
cost |
number | — |
date |
string | YYYY-MM-DD format. |
Archive – item
Note
Use the ASSET_ID / DEBT_ID from the Export – user portfolio data response.
Archive a single manual asset or debt. Behavior depends on the item:
- A holding (belongs to a parent account) has its value zeroed; the parent and sibling holdings are untouched.
- A top-level account is archived.
Only manually-added items can be archived. Connected/linked accounts (bank, brokerage, crypto, etc.) cannot be archived and are rejected.
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>'
No request body is required.
Response
{
"data": {},
"errorCode": 0
}
Get cash flow entries
Fetch the list of cash flow entries.
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": cashFlow[],
"errorCode": 0
}
cashFlow object
{
"cashIn": Number,
"cashOut": Number,
"currency": String,
"date": String,
"note": String
}
Example:
{
"cashIn": 300,
"cashOut": null,
"currency": "USD",
"date": "2025-02-02",
"note": "Initial investment"
}
Insert/update cash flow entries
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"
}'
Object reference
Asset / Debt / Insurance object
| Field | Description |
|---|---|
accountNumber |
Account number |
cashIn |
Value object |
cashOut |
Value object |
category |
Type — asset / debt / insurance |
committedCapital |
Value object |
connection |
Connection object |
cost |
Cost object |
costBasisForTax |
Value object |
description |
Item description |
exchange |
Exchange name |
holdingsCount |
Number of holdings |
id |
Asset / debt / insurance id |
investable |
Investable type. non_investable / investable_easy_convert / investable_cash |
irr |
IRR |
isin |
ISIN |
name |
Name |
note |
Item note |
ownership |
Ownership percentage |
parent |
Parent account object |
quantity |
Quantity |
rate |
Rate object |
sectionId |
Containing section id |
sectionName |
Section name |
sheetId |
Containing sheet id |
sheetName |
Sheet name |
subType |
More detailed item type |
taxability |
taxable / tax-deferred / tax-free |
taxOnUnrealizedGain |
Value object |
taxRate |
Tax percentage |
ticker |
Ticker symbol |
tickerId |
Ticker id |
tickerSector |
Sector name |
tickerSubType |
Asset class |
unfunded |
Value object |
value |
Value object |
Document object
| Field | Description |
|---|---|
fileType |
MIME type |
id |
Document id |
name |
Name |
size |
Size in bytes |
Value object
| Field | Description |
|---|---|
amount |
Numeric amount |
currency |
Currency code |
Connection object
| Field | Description |
|---|---|
accountId |
Account id |
aggregator |
Aggregator name |
id |
Connection id |
lastUpdatedTimestamp |
Last updated timestamp |
providerName |
Institution name |
Rate object
| Field | Description |
|---|---|
currency |
Currency |
price |
Price |
Example — holding
{
"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_to_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
{
"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"
}
}
Unofficial SDKs
Heads up
These are not maintained or verified by the Kubera team. Use at your own risk.