Skip to main content

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.

The API is in beta. Endpoints and fields may still change as we learn how you use it. Found a problem or need something that's missing? Write to support@finboom.app.

Base URL

https://finboom.app/api/v1
On this page

Quick start

  1. Open Settings, Integrations and create a key. Copy it: it's shown only once.
  2. Save it where your script can read it, such as an environment variable.
  3. Add your first expense:
Terminal
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.

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: 450 is ₹450, not paise.
  • Dates are YYYY-MM-DD. Entries can't be dated in the future.
  • Timestamps such as createdAt are ISO 8601 in UTC.
  • Lists come back as { data: [...], nextCursor }. When nextCursor isn't null, send it as cursor for 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.

Response400
{
  "error": "Amount must be greater than zero",
  "code": "VALIDATION_ERROR",
  "details": {
    "amount": [
      "Amount must be greater than zero"
    ]
  }
}
400VALIDATION_ERROR
A field is missing or invalid. details lists the problem for each field.
400INVALID_JSON
The body isn't valid JSON.
400INVALID_CURSOR
cursor didn't come from a previous page.
400INVALID_IDEMPOTENCY_KEY
Idempotency-Key is too long or has spaces or non-ASCII characters.
401UNAUTHORIZED
The key is missing, wrong, or revoked.
403PRO_REQUIRED
Your Pro plan has ended. Keys start working again when you renew.
403INSUFFICIENT_SCOPE
The key doesn't have the permission this endpoint needs. details shows required and granted.
403PLAN_LIMIT_REACHED
This would go over your plan's entry limit for the month.
404NOT_FOUND
No such record on this key's profile.
409IDEMPOTENCY_IN_PROGRESS
The first request with this Idempotency-Key is still running. Retry in a moment.
422IDEMPOTENCY_KEY_REUSED
This Idempotency-Key was already used for a different request.
429RATE_LIMITED
Too many requests. Wait for the number of seconds in the Retry-After header.
500SERVER_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.

Request
curl https://finboom.app/api/v1/me \
  -H "Authorization: Bearer $FINBOOM_API_KEY"
Response200
{
  "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.
Request
curl https://finboom.app/api/v1/accounts \
  -H "Authorization: Bearer $FINBOOM_API_KEY"
Response200
{
  "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.

Request
curl https://finboom.app/api/v1/accounts/cmh1acc0000hdfc \
  -H "Authorization: Bearer $FINBOOM_API_KEY"
Response200
{
  "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.
Request
curl https://finboom.app/api/v1/transactions \
  -H "Authorization: Bearer $FINBOOM_API_KEY"
Response200
{
  "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.

Request
curl https://finboom.app/api/v1/transactions/cmh4x9k2p0001 \
  -H "Authorization: Bearer $FINBOOM_API_KEY"
Response200
{
  "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.
Request
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"]}'
Response201
{
  "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.
Request
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"}]}'
Response201
{
  "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.
Request
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"]}'
Response200
{
  "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.

Request
curl -X DELETE https://finboom.app/api/v1/transactions/cmh4x9k2p0001 \
  -H "Authorization: Bearer $FINBOOM_API_KEY"
Response200
{
  "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.
Request
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"}'
Response201
{
  "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.

Request
curl https://finboom.app/api/v1/categories \
  -H "Authorization: Bearer $FINBOOM_API_KEY"
Response200
{
  "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).

Request
curl https://finboom.app/api/v1/tags \
  -H "Authorization: Bearer $FINBOOM_API_KEY"
Response200
{
  "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.