> ## 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.

# Data freshness and sync model

> Understand when and how Yoshi syncs financial data from connected institutions, what the as_of timestamp means, and how to handle stale data gracefully.

Yoshi syncs data from your connected financial institutions automatically. This page explains the sync model, how to interpret freshness signals, and how to build UIs that handle stale data gracefully.

## How syncing works

When you connect a financial account, Yoshi begins syncing data from that institution on a recurring schedule:

| Data type           | Typical sync frequency      | Notes                                                |
| ------------------- | --------------------------- | ---------------------------------------------------- |
| Transactions        | Every few hours             | New and updated transactions are pulled in each sync |
| Balances            | With each sync              | Updated alongside transactions                       |
| Investment holdings | Daily                       | Some institutions update less frequently             |
| Recurring streams   | After each transaction sync | Patterns are re-analyzed as new data arrives         |
| Scores              | Daily                       | Recalculated once per day based on the latest data   |

<Info>
  Sync frequency varies by institution. Some provide near real-time data, while others update once per business day.
</Info>

## The `as_of` timestamp

Each account includes an `as_of` field that tells you when its data was last refreshed:

```json theme={null}
{
  "id": "acc_abc123",
  "name": "Chase Checking (...4521)",
  "balance_current": 4523.17,
  "as_of": "2026-04-10T08:30:00.000Z"
}
```

Use `as_of` to show users when their data was last updated:

```typescript theme={null}
const account = accounts[0];
const lastSync = new Date(account.as_of);
const minutesAgo = Math.round((Date.now() - lastSync.getTime()) / 60000);

if (minutesAgo > 60) {
  console.log(`Balance may be stale (last updated ${minutesAgo} minutes ago)`);
}
```

## Use webhooks instead of polling

Rather than polling endpoints on a timer, subscribe to webhook events for real-time updates:

| Event                 | Fires when                                            |
| --------------------- | ----------------------------------------------------- |
| `transaction.created` | New transactions are synced                           |
| `transaction.updated` | Existing transactions change (e.g., pending → posted) |
| `balance.updated`     | Account balance changes                               |
| `investment.updated`  | Investment holdings are refreshed                     |
| `score.updated`       | Financial health scores are recalculated              |

```bash theme={null}
curl -X POST https://api.yoshi.ai/v1/webhooks/endpoints \
  -H "Authorization: Bearer yoshi_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks",
    "filter_types": ["transaction.created", "balance.updated"]
  }'
```

See the [Webhooks guide](/webhooks/overview) for full setup instructions.

## Handle stale data in your UI

Financial data may be minutes to hours old depending on the institution. Here are patterns for communicating freshness to users:

**Show the last sync time:**

```
Chase Checking: $4,523.17 · Updated 2 hours ago
```

**Flag potentially stale data:**

```typescript theme={null}
const STALE_THRESHOLD_MS = 4 * 60 * 60 * 1000; // 4 hours
const isStale = Date.now() - new Date(account.as_of).getTime() > STALE_THRESHOLD_MS;
```

**Don't promise real-time accuracy.** Balances and transactions reflect the most recent sync, not the current moment. A user may have made a purchase minutes ago that hasn't synced yet.

## Pending transactions

Transactions with `pending: true` are authorized but not yet settled. They can:

* Change amount (tips, holds)
* Change date
* Disappear entirely (voided authorizations)

Once a transaction posts (`pending: false`), it's final. Always use the transaction `id` for tracking — don't match on amount or description, which can change.

## What's next

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/webhooks/overview">
    Get real-time notifications when data changes.
  </Card>

  <Card title="Best practices" icon="lightbulb" href="/concepts/best-practices">
    Patterns for building reliable integrations.
  </Card>
</CardGroup>
