Developers
FinBoom APIBeta
Add and read your own entries from a script, a bot or a spreadsheet with a personal API key. The API is part of FinBoom Pro. Each key works on one profile and can only do what you allow.
Base URL
https://finboom.app/api/v1On this page
Getting started
Your key
Quick start
- Open Settings, Integrations and create a key. Copy it: it's shown only once.
- Save it where your script can read it, such as an environment variable.
- Add your first expense:
export FINBOOM_API_KEY="fb_..."
curl -X POST https://finboom.app/api/v1/transactions \
-H "Authorization: Bearer $FINBOOM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 450, "description": "Swiggy dinner"}'It shows up in the app straight away, with a category picked from your rules.
Authentication
Every request goes to https://finboom.app/api/v1 over HTTPS and sends the key in the Authorization header.
Authorization: Bearer fb_...- A key belongs to one profile. It never sees or changes another profile's data.
- Treat a key like a password. Don't put it in a web page, an app you share, or a public repo.
- If a key leaks, revoke it in Settings, Integrations. It stops working at once. Then create a new one.
- Keys pause when Pro ends and start working again when you renew.
Permissions
You choose what each key may do when you create it. Writing doesn't include reading: an "Add entries only" key can add expenses but can't read your history, which is the safest choice for a bot.
transactions:read- List and read transactions, categories and tags.
transactions:write- Add, edit and delete transactions and transfers. Doesn't include reading them back.
accounts:read- List accounts and read their balances.
accounts:write- Reserved. No endpoint uses it yet.
The presets in Settings are Add entries only (transactions:write), Read only (both read permissions) and Full access (all four).
Requests and responses
- Bodies are JSON. Send
Content-Type: application/json. - Amounts are plain numbers in the currency's main unit:
450is ₹450, not paise. - Dates are
YYYY-MM-DD. Entries can't be dated in the future. - Timestamps such as
createdAtare ISO 8601 in UTC. - Lists come back as
{ data: [...], nextCursor }. WhennextCursorisn't null, send it ascursorfor the next page. - Entries made through the API have
source: "API".
Safe retries
If a request times out, you can't tell whether it was saved. To retry without adding the entry twice, send an Idempotency-Key header with any unique value (a UUID works) on POST, PATCH and DELETE requests.
- Repeating the same request with the same key returns the first response, with the header
Idempotent-Replayed: true, and doesn't save anything again. - Keys are remembered for 24 hours.
- Using the same key for a different request returns 422.
- A 500 isn't remembered, so retrying with the same key runs the request again.
- Up to 255 printable ASCII characters, no spaces.
Rate limits
Limits are per account, shared by all your keys:
- 180 reads a minute (GET).
- 60 writes a minute (POST, PATCH, DELETE). Use the batch endpoint to add many entries.
Going over returns 429 with a Retry-After header giving the seconds to wait. API calls have their own budget, so a busy script never slows down the app.
Settings, Integrations shows each key's recent requests from the last 30 days, so you can check what a script has been doing.
Errors
Errors use the HTTP status and a body with a readable message and a stable code to check in your script.
{
"error": "Amount must be greater than zero",
"code": "VALIDATION_ERROR",
"details": {
"amount": [
"Amount must be greater than zero"
]
}
}- 400
VALIDATION_ERROR - A field is missing or invalid. details lists the problem for each field.
- 400
INVALID_JSON - The body isn't valid JSON.
- 400
INVALID_CURSOR - cursor didn't come from a previous page.
- 400
INVALID_IDEMPOTENCY_KEY - Idempotency-Key is too long or has spaces or non-ASCII characters.
- 401
UNAUTHORIZED - The key is missing, wrong, or revoked.
- 403
PRO_REQUIRED - Your Pro plan has ended. Keys start working again when you renew.
- 403
INSUFFICIENT_SCOPE - The key doesn't have the permission this endpoint needs. details shows required and granted.
- 403
PLAN_LIMIT_REACHED - This would go over your plan's entry limit for the month.
- 404
NOT_FOUND - No such record on this key's profile.
- 409
IDEMPOTENCY_IN_PROGRESS - The first request with this Idempotency-Key is still running. Retry in a moment.
- 422
IDEMPOTENCY_KEY_REUSED - This Idempotency-Key was already used for a different request.
- 429
RATE_LIMITED - Too many requests. Wait for the number of seconds in the Retry-After header.
- 500
SERVER_ERROR - Something broke on our side. It's safe to retry with the same Idempotency-Key.
Your key
Check your key
GET/api/v1/me
Permission: any valid key
Who the key acts as: your plan, the key's profile and base currency, and the permissions it holds. Any valid key can call it, so it's a good first request.
curl https://finboom.app/api/v1/me \
-H "Authorization: Bearer $FINBOOM_API_KEY"{
"plan": "pro",
"profile": {
"id": "cmh0prof00001",
"name": "Personal"
},
"baseCurrency": "INR",
"key": {
"id": "cmh2key000001",
"name": "Expense bot",
"keyPrefix": "fb_Xk29aQ7m",
"scopes": [
"transactions:write"
]
}
}Money
Accounts, transactions, transfers and categories: everything under the Money tab in the app.
Accounts
List accounts
GET/api/v1/accounts
Permission: accounts:read
The profile's money accounts in the same order as the app. Closed accounts are left out unless you ask for them.
Query parameters
includeInactiveboolean- Send true to include closed accounts.
curl https://finboom.app/api/v1/accounts \
-H "Authorization: Bearer $FINBOOM_API_KEY"{
"data": [
{
"id": "cmh1acc0000hdfc",
"name": "HDFC Savings",
"type": "BANK",
"bankName": "HDFC Bank",
"last4": "4821",
"currency": "INR",
"balance": 84250.5,
"openingBalance": 50000,
"openingBalanceDate": "2026-01-01",
"creditLimit": null,
"isDefault": true,
"isActive": true,
"createdAt": "2026-01-01T09:00:00.000Z"
}
],
"nextCursor": null
}Get an account
GET/api/v1/accounts/:id
Permission: accounts:read
One account by id, including its current balance.
curl https://finboom.app/api/v1/accounts/cmh1acc0000hdfc \
-H "Authorization: Bearer $FINBOOM_API_KEY"{
"id": "cmh1acc0000hdfc",
"name": "HDFC Savings",
"type": "BANK",
"bankName": "HDFC Bank",
"last4": "4821",
"currency": "INR",
"balance": 84250.5,
"openingBalance": 50000,
"openingBalanceDate": "2026-01-01",
"creditLimit": null,
"isDefault": true,
"isActive": true,
"createdAt": "2026-01-01T09:00:00.000Z"
}Transactions
List transactions
GET/api/v1/transactions
Permission: transactions:read
Entries newest first, 50 per page by default. When nextCursor isn't null, pass it as cursor to get the next page.
Query parameters
fromstring- YYYY-MM-DD, inclusive.
tostring- YYYY-MM-DD, inclusive.
type"EXPENSE" | "INCOME" | "TRANSFER"- Only this kind of entry.
accountIdstring- Entries in or out of this account, transfers included.
categorystring- A category id, e.g. FOOD.
tagstring- Entries with this tag.
qstring- Search description and notes.
limitnumber- 1 to 100. Defaults to 50.
cursorstring- nextCursor from the previous page.
curl https://finboom.app/api/v1/transactions \
-H "Authorization: Bearer $FINBOOM_API_KEY"{
"data": [
{
"id": "cmh4x9k2p0001",
"type": "EXPENSE",
"amount": 450,
"currency": "INR",
"date": "2026-11-05",
"description": "Swiggy dinner",
"notes": null,
"category": "FOOD",
"tags": [
"weekend"
],
"accountId": "cmh1acc0000hdfc",
"counterAccountId": null,
"destAmount": null,
"destCurrency": null,
"isRecurring": false,
"source": "API",
"createdAt": "2026-11-05T14:02:11.000Z",
"updatedAt": "2026-11-05T14:02:11.000Z"
}
],
"nextCursor": "WyIyMDI2LTExLTA1VDAwOjAwOjAwLjAwMFoiLCJjbWg0eDlrMnAwMDAxIl0"
}Get a transaction
GET/api/v1/transactions/:id
Permission: transactions:read
One entry by id.
curl https://finboom.app/api/v1/transactions/cmh4x9k2p0001 \
-H "Authorization: Bearer $FINBOOM_API_KEY"{
"id": "cmh4x9k2p0001",
"type": "EXPENSE",
"amount": 450,
"currency": "INR",
"date": "2026-11-05",
"description": "Swiggy dinner",
"notes": null,
"category": "FOOD",
"tags": [
"weekend"
],
"accountId": "cmh1acc0000hdfc",
"counterAccountId": null,
"destAmount": null,
"destCurrency": null,
"isRecurring": false,
"source": "API",
"createdAt": "2026-11-05T14:02:11.000Z",
"updatedAt": "2026-11-05T14:02:11.000Z"
}Add a transaction
POST/api/v1/transactions
Permission: transactions:write
Only amount is required. Everything else is filled in the way the app's own form would. Entries count toward your plan's monthly limit, the same as ones added in the app.
Body parameters
amountnumberRequired- Greater than zero, in the currency's main unit (450 means ₹450).
type"EXPENSE" | "INCOME"- Defaults to EXPENSE.
descriptionstring- Up to 300 characters. Used to pick a category when you don't send one.
categorystring- A category id from GET /categories, e.g. FOOD. When left out, your auto-categorisation rules choose one.
datestring- YYYY-MM-DD. Defaults to today (UTC). Can't be in the future.
currencystring- 3-letter code, e.g. INR or USD. Defaults to INR, so send it when the account uses another currency.
accountIdstring- Account from GET /accounts. Its balance updates. Left out, the entry isn't linked to an account.
notesstring- Up to 1000 characters.
tagsstring[]- Up to 20 tags, each up to 50 characters.
curl -X POST https://finboom.app/api/v1/transactions \
-H "Authorization: Bearer $FINBOOM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"amount":450,"description":"Swiggy dinner","accountId":"cmh1acc0000hdfc","tags":["weekend"]}'{
"id": "cmh4x9k2p0001",
"type": "EXPENSE",
"amount": 450,
"currency": "INR",
"date": "2026-11-05",
"description": "Swiggy dinner",
"notes": null,
"category": "FOOD",
"tags": [
"weekend"
],
"accountId": "cmh1acc0000hdfc",
"counterAccountId": null,
"destAmount": null,
"destCurrency": null,
"isRecurring": false,
"source": "API",
"createdAt": "2026-11-05T14:02:11.000Z",
"updatedAt": "2026-11-05T14:02:11.000Z"
}Add many at once
POST/api/v1/transactions/batch
Permission: transactions:write
Up to 100 entries in one call, each with the same fields as a single add. Either all are saved or none are, so a retry never leaves half a batch behind.
Body parameters
transactionsobject[]Required- 1 to 100 entries, each shaped like the body of POST /transactions.
curl -X POST https://finboom.app/api/v1/transactions/batch \
-H "Authorization: Bearer $FINBOOM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"transactions":[{"amount":120,"description":"Auto to office"},{"amount":85000,"type":"INCOME","description":"November salary","category":"SALARY"}]}'{
"data": [
{
"id": "cmh4x9k2p0001",
"type": "EXPENSE",
"amount": 450,
"currency": "INR",
"date": "2026-11-05",
"description": "Swiggy dinner",
"notes": null,
"category": "FOOD",
"tags": [
"weekend"
],
"accountId": "cmh1acc0000hdfc",
"counterAccountId": null,
"destAmount": null,
"destCurrency": null,
"isRecurring": false,
"source": "API",
"createdAt": "2026-11-05T14:02:11.000Z",
"updatedAt": "2026-11-05T14:02:11.000Z"
}
]
}Edit a transaction
PATCH/api/v1/transactions/:id
Permission: transactions:write
Send only the fields you want to change. The type of an entry can't be changed. Account balances update for you.
Body parameters
amountnumber- Greater than zero.
descriptionstring- Up to 300 characters.
categorystring- A category id from GET /categories.
datestring- YYYY-MM-DD. Can't be in the future.
currencystring- 3-letter code.
accountIdstring | null- Move the entry to another account, or null to unlink it.
counterAccountIdstring- Transfers only: the account money goes to.
notesstring- Up to 1000 characters.
tagsstring[]- Replaces the entry's tags.
curl -X PATCH https://finboom.app/api/v1/transactions/cmh4x9k2p0001 \
-H "Authorization: Bearer $FINBOOM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"category":"GROCERIES","tags":["weekend","family"]}'{
"id": "cmh4x9k2p0001",
"type": "EXPENSE",
"amount": 450,
"currency": "INR",
"date": "2026-11-05",
"description": "Swiggy dinner",
"notes": null,
"category": "GROCERIES",
"tags": [
"weekend",
"family"
],
"accountId": "cmh1acc0000hdfc",
"counterAccountId": null,
"destAmount": null,
"destCurrency": null,
"isRecurring": false,
"source": "API",
"createdAt": "2026-11-05T14:02:11.000Z",
"updatedAt": "2026-11-05T14:02:11.000Z"
}Delete a transaction
DELETE/api/v1/transactions/:id
Permission: transactions:write
Deletes the entry for good and updates the account balance. This can't be undone.
curl -X DELETE https://finboom.app/api/v1/transactions/cmh4x9k2p0001 \
-H "Authorization: Bearer $FINBOOM_API_KEY"{
"id": "cmh4x9k2p0001",
"deleted": true
}Transfers
Create a transfer
POST/api/v1/transfers
Permission: transactions:write
A transfer between two of the profile's accounts, such as paying a credit card bill. A loan part payment is a transfer into the loan's account. The currency is the "from" account's.
Body parameters
fromAccountIdstringRequired- Where the money leaves.
toAccountIdstringRequired- Where the money arrives.
amountnumberRequired- Greater than zero, in the "from" account's currency.
destAmountnumber- Only when the two accounts use different currencies: the amount that arrived.
datestring- YYYY-MM-DD. Defaults to today (UTC).
descriptionstring- Up to 300 characters.
notesstring- Up to 1000 characters.
tagsstring[]- Up to 20 tags.
curl -X POST https://finboom.app/api/v1/transfers \
-H "Authorization: Bearer $FINBOOM_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"fromAccountId":"cmh1acc0000hdfc","toAccountId":"cmh1acc0000card","amount":12000,"description":"Card bill"}'{
"id": "cmh4x9k2p0001",
"type": "TRANSFER",
"amount": 12000,
"currency": "INR",
"date": "2026-11-05",
"description": "Card bill",
"notes": null,
"category": "TRANSFER",
"tags": [],
"accountId": "cmh1acc0000hdfc",
"counterAccountId": "cmh1acc0000card",
"destAmount": null,
"destCurrency": null,
"isRecurring": false,
"source": "API",
"createdAt": "2026-11-05T14:02:11.000Z",
"updatedAt": "2026-11-05T14:02:11.000Z"
}Categories and tags
List categories
GET/api/v1/categories
Permission: transactions:read
Every expense and income category you can use, built-in and your own. Send the id as category when you add or edit an entry.
curl https://finboom.app/api/v1/categories \
-H "Authorization: Bearer $FINBOOM_API_KEY"{
"data": [
{
"id": "FOOD",
"label": "Food & Dining",
"type": "EXPENSE"
},
{
"id": "SALARY",
"label": "Salary",
"type": "INCOME"
}
],
"nextCursor": null
}List tags
GET/api/v1/tags
Permission: transactions:read
Tags used on this profile's entries, most used first (up to 500).
curl https://finboom.app/api/v1/tags \
-H "Authorization: Bearer $FINBOOM_API_KEY"{
"data": [
{
"name": "weekend",
"count": 42
}
],
"nextCursor": null
}Questions or a missing endpoint?
Write to support@finboom.app. Tell us what you're building and we'll help.