API v1.0.0 · available

finO$ API reference

Query your workspace’s financial data over HTTP: transactions extracted from bank statements, per-account balances, net worth and monthly summaries. You can also upload PDFs for processing.

Base URL

https://api.getfinos.com

Introduction

finO$ turns PDF bank statements into structured data. The API gives you programmatic access to that processed, normalised data: one single format no matter which bank the statement came from.

  • Normalised transactions from any bank, strongest in Mexico and Latin America
  • Per-account balances and net worth, carried forward across periods
  • Monthly summary and historical series of income, expense and net worth
  • Currency conversion at each transaction’s own date
  • Asynchronous PDF upload and processing
  • MCP connector: your AI agent (Claude, ChatGPT) queries this same data with your key
  • Official SDKs and notification webhooks Coming soon

Your first call

  1. 1 Create an API key in the app: Settings → API. It is shown only once, so store it as a secret.
  2. 2 Send it in the Authorization header of every request.
  3. 3 Ask for the monthly summary. If your workspace already has processed statements, those are your figures.

With your key in hand, this is the shortest request that returns something useful: the current month’s summary.

cURL
curl -H "Authorization: Bearer $FINOS_API_KEY" \
  "https://api.getfinos.com/api/v1/summary?month=2026-07"

Create an API key in the app

Authentication

Every request carries the API key in the Authorization header, using the Bearer scheme.

Authorization: Bearer fin_sk_...

The key is created in the app (Settings → API) and shown ONCE. Each key is bound to the workspace it was created in: do not send any workspace header; it is not needed and changes nothing.

The same key connects an MCP client: add the connector in Claude, ChatGPT, or whichever agent you use, and it queries these same endpoints with the scope you gave it.

Scopes

  • read Read transactions, accounts, categories, statements, summary and periods.
  • statements:write Upload PDFs for processing. It is separate because it draws down your wallet balance.

Before you write code

The sign matters

In amount, negative is money out and positive is money in. There is no separate direction field.

Not everything is confirmed

Every transaction carries a status. validated ones were reviewed by a person; pending ones came out of the automatic PDF extraction and nobody has confirmed them yet.

That is why the numbers do not always tie out (and it is not a bug)

/summary and /periods count VALIDATED transactions only, while /transactions returns both validated and pending. If you sum the list and compare it against the summary they will differ. When you need firm figures, use the summary or filter the list by status.

Currencies convert at each date’s rate

The display_currency parameter controls the output currency; the default is MXN.

Response shape

Every response shares the same envelope. Lists also carry pagination:

{
  "data": [ ... ],
  "pagination": { "limit": 50, "offset": 0, "total": 213, "has_more": true }
}

Use has_more to know whether to ask for more; no arithmetic on total and offset needed.

Endpoints

Eight operations over a single base URL. Every read operation works with the read scope.

Method Path Description
GET /api/v1/summary Monthly summary: net worth, cash flow and top spend
GET /api/v1/periods Month-by-month series of income, expense and net worth
GET /api/v1/transactions List transactions
GET /api/v1/accounts List accounts with their balance
GET /api/v1/categories List the workspace’s active categories
GET /api/v1/statements List statements and their processing state
GET /api/v1/statements/{id} Processing state of a statement
POST /api/v1/statements Upload a PDF for processing
GET

/api/v1/summary

Monthly summary: net worth, cash flow and top spend

The answer to “how am I doing?”. It is a SNAPSHOT of the requested month: net worth is the one at that month’s close, not today’s, and it matches what /periods returns for the same month. IMPORTANT: these figures count VALIDATED transactions only, while /transactions returns validated and pending ones. If you sum the transaction list and compare it against this summary they will not match, and that is not a bug.

Parameters

  • month string optional YYYY-MM. Defaults to the current month.
  • display_currency string optional · default: MXN Output currency. Everything is converted at the rate for its own date.

Returns One Summary

GET

/api/v1/periods

Month-by-month series of income, expense and net worth

One row per month. As in /summary, it counts VALIDATED transactions only. Balances carry forward: an account with no statement that month keeps its last known balance.

Parameters

  • from string optional YYYY-MM.
  • to string optional YYYY-MM. Defaults to the current month.
  • display_currency string optional · default: MXN

Returns List of Period

GET

/api/v1/transactions

List transactions

Returns both validated and pending transactions, newest first. Filter by date, bank, category, account, kind or free text.

Parameters

  • limit integer optional · default: 50 Maximum 200. A larger value is clamped rather than rejected.
  • offset integer optional · default: 0
  • from string<date> optional
  • to string<date> optional
  • bank string optional
  • category string optional
  • account_id string optional
  • kind string optional expense | income | internal_transfer | refund
  • search string optional Searches concept and counterparty, accent-insensitive.

Returns List of Transaction · paginated

GET

/api/v1/accounts

List accounts with their balance

Returns List of Account

GET

/api/v1/categories

List the workspace’s active categories

Returns List of Category

GET

/api/v1/statements

List statements and their processing state

Parameters

  • limit integer optional · default: 50 Maximum 200. A larger value is clamped rather than rejected.
  • offset integer optional · default: 0

Returns List of Statement · paginated

GET

/api/v1/statements/{id}

Processing state of a statement

Poll it after uploading until status is completed: at that point its transactions show up in /transactions.

Parameters

  • id string required

Returns One Statement

Other responses

  • 404 Not found, or not part of your key’s workspace.
POST

/api/v1/statements

Required scope: statements:write

Upload a PDF for processing

Requires the statements:write scope. Processing draws down your wallet balance (billed per page), which is why the permission is separate from read access. Responds 202: processing is asynchronous, so poll GET /api/v1/statements/{id} until status is completed. If your balance falls short and you already have previous statements, it is queued with status: pending and processed once you top up.

Request body · multipart/form-data

  • file string<binary> required The PDF. 10 MB maximum.
  • filename string optional Optional; defaults to the file’s own name.

Other responses

  • 202 Accepted. status is processing or pending.
  • 400 File missing, empty, over 10 MB, or not a PDF.
  • 402 Insufficient balance. Treat it differently from a failure: retrying does NOT help, you need to top up. Carries required_cents and available_cents.
  • 502 The PDF processor failed. Retrying may help.

Walking every transaction

JavaScript
async function allTransactions(from, to) {
  const out = [];
  let offset = 0;

  for (;;) {
    const url = new URL('https://api.getfinos.com/api/v1/transactions');
    url.search = new URLSearchParams({ from, to, limit: '200', offset: String(offset) });

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.FINOS_API_KEY}` },
    });
    const { data, pagination } = await res.json();

    out.push(...data);
    if (!pagination.has_more) return out;
    offset += pagination.limit;
  }
}

// Ojo: incluye validadas y pendientes.
const confirmed = (await allTransactions('2026-01-01', '2026-07-31'))
  .filter((tx) => tx.status === 'validated');

Upload a PDF and wait for the result

cURL
# 1. Subir (responde 202 con el estado de cuenta creado)
curl -X POST "https://api.getfinos.com/api/v1/statements" \
  -H "Authorization: Bearer $FINOS_API_KEY" \
  -F "file=@estado-de-cuenta.pdf"

# 2. Consultar hasta que status sea "completed"
curl -H "Authorization: Bearer $FINOS_API_KEY" \
  "https://api.getfinos.com/api/v1/statements/STATEMENT_ID"

Data model

These are the objects the API returns. Fields marked required are always present in the response, even though their value may be null.

Transaction

Field Type Description
id string Stable transaction identifier.
statement_id string The statement this transaction was extracted from.
date string<date> | null Date of the movement.
amount number | null Negative = money out, positive = money in.
currency string | null ISO-4217 code (MXN, USD…).
kind string expense, income, internal_transfer or refund.
status string | null validated (confirmed by the user), pending or dismissed.
concept string | null Description exactly as the bank prints it.
counterparty string | null Counterparty, when the bank reports one.
merchant string | null Canonical merchant name.
category string | null Category key (see /categories).
bank string | null Issuing bank.
account_id string | null Account this transaction belongs to.
account_type string | null debit or credit.
original_amount number | null Amount in the original currency, if the bank reports a different one.
original_currency string | null Original ISO-4217 currency code.
fx_rate number | null Exchange rate applied for the conversion.

Summary

Field Type Description
month string Month in YYYY-MM format.
currency string | null ISO-4217 code (MXN, USD…).
net_worth number Assets minus debt AT THE CLOSE of the requested month, not today. Balances carry forward: an account with no statement that month keeps its last known balance. A month before your first statement returns 0.
assets number Debit account balances, including manually recorded holdings.
debt number Credit card debt, positive when owed.
net_worth_change number | null Change against the previous month. null when there is no prior month to compare against.
income number Income for the period. VALIDATED transactions only.
expense number Expense for the period. VALIDATED transactions only.
net number income - expense.
transaction_count integer How many validated transactions went into these figures.
top_expense_categories array The 5 categories with the highest spend that month.
msi_committed number | null Monthly commitment from interest-free instalment purchases FOR THAT MONTH. It decreases over time: each plan stops contributing once it is paid off, so a distant month may be 0. null for a month already in the past: we do not store the historical state of the plans, so any figure would be made up.

Period

Field Type Description
period string Period month, YYYY-MM.
income number Income for the period. VALIDATED transactions only.
expense number Expense for the period. VALIDATED transactions only.
net number income - expense.
transaction_count integer How many validated transactions went into this month’s figures.
assets number Debit balances at close, carried forward if there was no statement that month.
debt number Credit card debt at the close of the month.
net_worth number Assets minus debt at the close of the month.
msi_committed number | null Interest-free instalment commitment for THAT month. It is reported while no statement covers the month yet, even if the month has passed, because the commitment is real even when not extracted. null once the month is covered: the actual charge is already in expense, and reporting it separately would double-count it.

Account

Field Type Description
id string Account identifier.
bank string | null Issuing bank.
account_number string | null Masked by the bank.
account_type string | null debit or credit.
currency string | null ISO-4217 code (MXN, USD…).
balance number | null Balance at the latest statement close, including manually recorded holdings.
first_period_start string<date> | null Start of the earliest statement on file.
last_period_end string<date> | null End of the most recent statement on file.
statement_count integer How many statements have been processed for this account.
transaction_count integer How many transactions have been extracted from this account in total.

Category

Field Type Description
key string Stable key; this is what category carries.
label string Human-readable name.
kind string expense, income, internal_transfer or refund.
archived boolean Whether the category is archived.

Statement

Field Type Description
id string Statement identifier.
filename string Original file name.
bank string | null Issuing bank.
period_start string<date> | null First day covered by the statement.
period_end string<date> | null Last day covered by the statement.
status string | null pending (uploaded, not processed yet), processing, completed or error. This is how you know its transactions are ready to read.
page_count integer | null Pages detected in the PDF. This is what gets billed.
uploaded_at string<date-time> Upload timestamp.

The format is identical across every supported bank: normalisation happens earlier, when the PDF is processed. Per-bank detail in the bank pages .

Errors

Errors return { "error": "...", "code": "..." } with the matching HTTP status. These apply to any endpoint:

HTTP Description
401 Invalid or revoked key.
403 The key is missing the required scope.
404 The resource does not exist, or the workspace is no longer within your reach.
429 Too many requests. Honour the Retry-After header before retrying.
{
  "error": "Invalid API key",
  "code": "unauthorized"
}

Limits and behaviour

Behaviour worth knowing when you build against the API:

  • limit caps at 200. A larger value is clamped rather than rejected.
  • PDFs are capped at 10 MB per file.
  • Processing a PDF is asynchronous: the upload responds 202 and you poll the statement until its status is completed.
  • A 402 on upload means insufficient balance. Retrying does NOT help: you need to top up. The response carries required_cents and available_cents.
  • On a 429, honour the Retry-After header before retrying.

Pricing

Free every month

30 pages

Per workspace, on the Free plan · they don't roll over

Then, pay as you go

$5 MXN / page

Top up balance, drawn down by usage

The API has no separate cost and no subscription: you pay the same as in the dashboard, per processed PDF page. Reading data that is already processed is free.

A team with lots of clients? The Pro plan has more pages included and a lower price per page. See pricing · Cost calculator

API frequently asked questions

How do I get a finO$ API key?

You create it in the app, under Settings → API, once your account is open. The key is shown only once and starts with fin_sk_; store it as a secret because it cannot be viewed again. Each key is bound to the workspace it was created in.

How much does the API cost?

The API has no separate cost and no subscription. You pay the same as in the dashboard: $5 MXN per processed PDF page, with 30 free pages every month on the Free plan. Reading data that is already processed (transactions, balances, summaries) does not draw down your balance.

Why does the /transactions total not match /summary?

Because they do not count the same thing, by design. /summary and /periods use validated transactions only (reviewed by a person), while /transactions returns both validated and pending ones. If you need them to tie out, filter the list by status=validated or use the summary directly.

Does the API process PDFs or only read already-extracted data?

Both. POST /api/v1/statements uploads a PDF of up to 10 MB for processing and requires the statements:write scope. Processing is asynchronous: it responds 202 and you poll GET /api/v1/statements/{id} until status is completed, at which point its transactions appear in /transactions.

Are there official SDKs or webhooks?

Not yet. Today the API is REST over HTTP with Bearer authentication, so it works with any HTTP client. Official SDKs and notification webhooks are on the roadmap; in the meantime, you poll the statement status to know when a PDF has finished processing.

What does a 402 error on statement upload mean?

Insufficient wallet balance. It is different from a failure: retrying does not help, you need to top up. The response carries required_cents and available_cents so you know how much is missing. If you already have previous statements, the file is queued with status pending and processed once you top up.

Which currency are the amounts in?

MXN by default. The display_currency parameter changes the output currency on /summary and /periods, and conversion happens at the rate for each movement’s own date, not today’s rate. Each transaction also keeps its original amount and currency in original_amount and original_currency.

Create your API key

Open an account, process your first statement and generate the key from Settings → API. Every month, the first 30 pages are on us.

Start freeGo to my dashboard

Keep reading: Interactive reference · finO$ for developers · Plaid alternatives in LATAM