> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yoshi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a personal finance dashboard with the Yoshi API

> Fetch accounts, transactions, scores, and recurring data to build a personal finance dashboard. Includes caching strategies and webhook-driven updates.

Build a dashboard that shows a user's linked accounts, recent transactions, financial health scores, and recurring bills — all from a single API key.

## Fetch the overview

Start with `GET /me/summary` to get accounts, scores, and goals in one call:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Yoshi from "@yoshi-ai/sdk";

  const yoshi = new Yoshi();

  const summary = await yoshi.me.summary();

  console.log("Accounts:", summary.accounts.total);
  console.log("Yoshi Score:", summary.scores?.yoshi);
  console.log("Active Goals:", summary.goals.length);
  ```

  ```python Python theme={null}
  from yoshi import Yoshi

  yoshi = Yoshi()

  summary = yoshi.me.summary()

  print(f"Accounts: {summary.accounts.total}")
  print(f"Yoshi Score: {summary.scores.yoshi if summary.scores else 'N/A'}")
  print(f"Active Goals: {len(summary.goals)}")
  ```
</CodeGroup>

This single call replaces three separate requests to `/accounts`, `/scores`, and `/goals`.

## List recent transactions

Fetch the latest transactions across all accounts:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const page = await yoshi.transactions.list({ limit: 20 });

  for (const tx of page.data) {
    const direction = tx.cash_flow_direction === "outflow" ? "-" : "+";
    console.log(`${direction}$${tx.amount_absolute} ${tx.counterparty_name}`);
  }
  ```

  ```python Python theme={null}
  page = yoshi.transactions.list(limit=20)

  for tx in page.data:
      direction = "-" if tx.cash_flow_direction == "outflow" else "+"
      print(f"{direction}${tx.amount_absolute} {tx.counterparty_name}")
  ```
</CodeGroup>

Filter by account to show transactions for a specific card or bank account:

```bash theme={null}
curl "https://api.yoshi.ai/transactions?account_id=acc_abc123&limit=10" \
  -H "Authorization: Bearer yoshi_YOUR_KEY"
```

## Show balance history

Plot account balances over time with the balance series endpoint:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const balances = await yoshi.accounts.balanceSeries.list("acc_abc123", {
    days: 30,
  });

  for (const point of balances.points) {
    console.log(`${point.date}: $${point.balance}`);
  }
  ```

  ```python Python theme={null}
  balances = yoshi.accounts.balance_series.list("acc_abc123", days=30)

  for point in balances.points:
      print(f"{point.date}: ${point.balance}")
  ```
</CodeGroup>

## Add recurring streams

Show subscriptions, bills, and income streams:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const recurring = await yoshi.recurring.list();

  const bills = recurring.streams.filter(
    (s) => s.direction === "outflow" && s.is_active
  );
  const income = recurring.streams.filter(
    (s) => s.direction === "inflow" && s.is_active
  );

  console.log(`${bills.length} active bills, ${income.length} income streams`);
  ```

  ```python Python theme={null}
  recurring = yoshi.recurring.list()

  bills = [s for s in recurring.streams if s.direction == "outflow" and s.is_active]
  income = [s for s in recurring.streams if s.direction == "inflow" and s.is_active]

  print(f"{len(bills)} active bills, {len(income)} income streams")
  ```
</CodeGroup>

## Caching strategy

Not all data changes at the same rate:

| Resource          | Cache duration | Why                                  |
| ----------------- | -------------- | ------------------------------------ |
| Accounts metadata | 5–15 minutes   | Names and types rarely change        |
| Balances          | Do not cache   | Changes with every sync              |
| Transactions      | 1–5 minutes    | New transactions arrive periodically |
| Scores            | 1 hour         | Recalculated daily                   |
| Recurring streams | 1 hour         | Updated infrequently                 |

<Tip>
  Use [webhooks](/webhooks/overview) instead of polling. Subscribe to `transaction.created` and `balance.updated` events to refresh your dashboard in real time.
</Tip>

## Polling vs webhooks

For a simple dashboard, poll on a timer:

```typescript theme={null}
setInterval(async () => {
  const summary = await yoshi.me.summary();
  updateUI(summary);
}, 60_000); // every 60 seconds
```

For real-time updates, register a [webhook endpoint](/webhooks/management) and listen for events:

```typescript theme={null}
app.post("/webhooks/yoshi", async (req, res) => {
  const event = req.body;

  if (event.type === "transaction.created") {
    await refreshTransactions(event.data.account_id);
  } else if (event.type === "balance.updated") {
    await refreshBalances(event.data.account_id);
  }

  res.sendStatus(200);
});
```

## Handle pending transactions

Transactions can arrive in a `pending` state before they post:

```typescript theme={null}
const page = await yoshi.transactions.list({ limit: 50 });

const posted = page.data.filter((tx) => !tx.pending);
const pending = page.data.filter((tx) => tx.pending);
```

<Warning>
  Pending transactions may change amount, date, or even disappear before posting. Always use the transaction `id` for deduplication — don't rely on amount or description matching.
</Warning>

## What's next

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/webhooks/overview">
    Set up real-time updates instead of polling.
  </Card>

  <Card title="Data freshness" icon="clock" href="/concepts/data-freshness">
    Understand when and how data syncs.
  </Card>
</CardGroup>
