Quickstart

Create a token, find your household, and read your first page of transactions in about five minutes.

This guide makes three read requests and one optional write. You need a Penny account that belongs to a household with an active subscription.

Create a token

In the app, open Settings → API Tokens and create a token. Read only access is enough until the last step. The full token is shown once, so copy it before you close the screen, then store it in an environment variable:

export PENNY_API_TOKEN="penny_pat_YOUR_TOKEN"

Find your household

Almost everything in the API belongs to a household, and its ID is part of the path. List the households you can access:

curl https://api.penny.finance/v1/households \
  -H "Authorization: Bearer $PENNY_API_TOKEN"
200 response
{
  "households": [
    {
      "id": "Hh4vK8pQ2sLm",
      "name": "Chen Household",
      "currency": "USD",
      "timezone": "America/Los_Angeles",
      "role": "owner",
      "access_status": "available"
    }
  ]
}

Some fields are omitted here. Keep the id for the next requests:

export PENNY_HOUSEHOLD_ID="Hh4vK8pQ2sLm"

List transactions

Request the household's transactions from the start of a month. The response is ordered newest first:

curl "https://api.penny.finance/v1/households/$PENNY_HOUSEHOLD_ID/transactions?start_date=2026-08-01" \
  -H "Authorization: Bearer $PENNY_API_TOKEN"
200 response
{
  "transactions": [
    {
      "id": "Tx3fJ9mS6vBa",
      "name": "Morning coffee",
      "date": "2026-08-14",
      "amount": { "amount": "-6.50", "currency": "USD" },
      "pending": false,
      "account": { "id": "Ac7kP2mN9qRs", "name": "Everyday Checking" },
      "merchant": { "id": "Mr9dL3xT6bQa", "name": "Blue Bottle Coffee" },
      "household_finance_category": {
        "id": "Ct5nR8wY1jFu",
        "name": "Coffee & Dining"
      },
      "tags": []
    }
  ],
  "daily_sums": [
    { "date": "2026-08-14", "amount": { "amount": "-6.50", "currency": "USD" } }
  ],
  "next_cursor": "eyJkYXRlIjogIjIwMjYtMDgtMTQiLCAiZmgiOiAiOTUyMTFhYjAxMDVmYzA0MSJ9",
  "has_more": true
}

Each object carries more fields than shown; the endpoint reference describes all of them. Two conventions apply everywhere in the API: amounts are decimal strings, and expenses are negative while income is positive.

Read every page

When has_more is true, pass next_cursor back as cursor with the same filters. This loop collects every transaction since the start of the month:

const headers = { Authorization: `Bearer ${process.env.PENNY_API_TOKEN}` }
const url = new URL(
  `/v1/households/${process.env.PENNY_HOUSEHOLD_ID}/transactions`,
  "https://api.penny.finance"
)
url.searchParams.set("start_date", "2026-08-01")

const transactions = []
while (true) {
  const response = await fetch(url, { headers })
  const page = await response.json()
  if (!response.ok) throw new Error(`${response.status} ${page.code}`)

  transactions.push(...page.transactions)
  if (!page.has_more) break
  url.searchParams.set("cursor", page.next_cursor)
}

Every paged list works the same way. See pagination for page sizes and the lists that return everything in one page by default.

Make a change

Writes need a token with Read and write access, and a household role of member or owner. Create a tag, then add it to the transaction from the previous step:

curl -X POST "https://api.penny.finance/v1/households/$PENNY_HOUSEHOLD_ID/tags" \
  -H "Authorization: Bearer $PENNY_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Work meals" }'
201 response
{
  "id": "Tg8qN1cK5rWd",
  "name": "Work meals",
  "color": null,
  "transaction_count": 0
}
curl -X POST "https://api.penny.finance/v1/households/$PENNY_HOUSEHOLD_ID/transactions/bulk-add-tags" \
  -H "Authorization: Bearer $PENNY_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "transaction_ids": ["Tx3fJ9mS6vBa"], "tag_ids": ["Tg8qN1cK5rWd"] }'

The response returns each transaction that changed, with the new tag in its tags. With a read-only token, both requests fail with 403 read_only_token.

These requests change your real data. The API has no sandbox.

Next steps

  • Overview: authentication, household scope, pagination, and the money and date conventions.
  • Errors: the error format and every code the API returns.
  • Download the OpenAPI schema to import into an API tool or generate a client.

Last updated on

On this page