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

# Best practices for building with the Yoshi API

> Patterns for error handling, retry strategies, caching, webhook processing, security, and rate limit budgeting when building on the Yoshi API.

Follow these patterns to build reliable, performant, and secure integrations with the Yoshi API.

## Error handling

Always check the response envelope before accessing data:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const response = await fetch("https://api.yoshi.ai/accounts", {
    headers: { Authorization: `Bearer ${process.env.YOSHI_API_KEY}` },
  });

  if (!response.ok) {
    const { error, meta } = await response.json();
    console.error(`[${meta.request_id}] ${error.code}: ${error.message}`);
    throw new Error(error.display_message || error.message);
  }

  const { data } = await response.json();
  ```

  ```python Python theme={null}
  response = httpx.get(
      "https://api.yoshi.ai/accounts",
      headers={"Authorization": f"Bearer {os.environ['YOSHI_API_KEY']}"},
  )

  if response.status_code != 200:
      body = response.json()
      error = body["error"]
      request_id = body["meta"]["request_id"]
      raise Exception(f"[{request_id}] {error['code']}: {error['message']}")

  data = response.json()["data"]
  ```
</CodeGroup>

Include `request_id` in error logs and support tickets — it lets Yoshi trace exactly what happened.

## Retry strategy

| Status | Retry? | Strategy                                            |
| ------ | ------ | --------------------------------------------------- |
| `429`  | Yes    | Wait `retry_after` seconds, then retry              |
| `500`  | Yes    | Exponential backoff: 1s, 2s, 4s (max 3 retries)     |
| `503`  | Yes    | Exponential backoff                                 |
| `400`  | No     | Fix the request — the input is invalid              |
| `401`  | No     | Check your API key                                  |
| `404`  | No     | The resource doesn't exist                          |
| `409`  | No     | Duplicate idempotency key with different parameters |

```typescript theme={null}
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.ok) return response;

    if (response.status === 429) {
      const retryAfter = Number(response.headers.get("Retry-After")) || 60;
      await sleep(retryAfter * 1000);
      continue;
    }

    if (response.status >= 500 && attempt < maxRetries) {
      await sleep(1000 * Math.pow(2, attempt));
      continue;
    }

    return response; // Don't retry 4xx errors
  }
}
```

<Tip>
  The SDKs handle retries automatically with configurable `maxRetries` (default: 2).
</Tip>

## Caching

| Resource                        | Safe to cache | Duration                    |
| ------------------------------- | ------------- | --------------------------- |
| Account metadata (names, types) | Yes           | 5–15 minutes                |
| Balances                        | No            | Changes every sync          |
| Transactions list               | Briefly       | 1–5 minutes                 |
| Scores                          | Yes           | 1 hour (recalculated daily) |
| Recurring streams               | Yes           | 1 hour                      |
| Card products catalog           | Yes           | 24 hours                    |

Never cache balance data — it changes with every sync and users expect current numbers.

## Webhook processing

Respond fast, process later:

```javascript theme={null}
app.post("/webhooks/yoshi", async (req, res) => {
  // Verify signature
  const event = verifyWebhook(req);

  // Acknowledge immediately
  res.sendStatus(200);

  // Process asynchronously
  await queue.add("process-event", event);
});
```

<Warning>
  Return a `2xx` response within 15 seconds. If your handler takes longer, Yoshi marks the delivery as failed and retries.
</Warning>

**Idempotent processing:** Events can be delivered more than once. Use `event.id` to deduplicate:

```javascript theme={null}
async function processEvent(event) {
  const already = await db.get(`processed:${event.id}`);
  if (already) return;

  await handleEvent(event);
  await db.set(`processed:${event.id}`, true, { ex: 86400 });
}
```

## Security

**Never expose API keys client-side.** API keys grant full access to the user's financial data. Always make API calls from a backend server.

```
❌ fetch("https://api.yoshi.ai/accounts", {
     headers: { Authorization: "Bearer yoshi_3xK9mP..." }
   })  // in browser JavaScript

✅ // Browser calls YOUR backend, which calls Yoshi
   fetch("/api/accounts")
```

**Use environment variables.** Don't hardcode keys in source code:

```bash theme={null}
export YOSHI_API_KEY=yoshi_3xK9mP...
```

**Rotate keys periodically.** Create a new key, update your services, then revoke the old one. Multiple active keys are supported for zero-downtime rotation.

**Verify webhook signatures.** Always [verify the signature](/webhooks/verification) before processing webhook events — otherwise anyone can send fake events to your endpoint.

## Rate limit budgeting

Each API key has a limit of **100 requests per minute**. If you have multiple services sharing a key:

* Track usage across services using the `X-RateLimit-Remaining` header
* Prioritize user-facing requests over background jobs
* Use webhooks to reduce polling — each webhook you process replaces multiple poll requests
* Consider separate keys for different services so they have independent limits

```typescript theme={null}
const remaining = Number(response.headers.get("X-RateLimit-Remaining"));
if (remaining < 10) {
  console.warn("Approaching rate limit, throttling background jobs");
  pauseBackgroundJobs();
}
```

## What's next

<CardGroup cols={2}>
  <Card title="Errors" icon="triangle-exclamation" href="/errors">
    Full error codes reference.
  </Card>

  <Card title="Rate limits" icon="gauge-high" href="/rate-limits">
    Detailed rate limiting documentation.
  </Card>
</CardGroup>
