Authentication
401 Unauthorized
“Invalid, expired, or missing API key”
- Confirm your key starts with
yoshi_
- Check that the
Authorization header uses the Bearer scheme: Authorization: Bearer yoshi_YOUR_KEY
- Verify the key hasn’t been revoked in your dashboard
- Make sure there are no extra spaces or newlines in the key
# Correct
curl https://api.yoshi.ai/accounts \
-H "Authorization: Bearer yoshi_3xK9mP..."
# Wrong — missing "Bearer"
curl https://api.yoshi.ai/accounts \
-H "Authorization: yoshi_3xK9mP..."
Rate limiting
429 Too Many Requests
“Rate limit exceeded”
Check the response headers to see your current limit and when it resets:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1712764800
Retry-After: 43
Wait Retry-After seconds before retrying. Implement exponential backoff for automated systems:
if (response.status === 429) {
const retryAfter = Number(response.headers.get("Retry-After")) || 60;
await new Promise((r) => setTimeout(r, retryAfter * 1000));
}
Use webhooks to replace polling. Every webhook you process is one fewer API call.
Webhooks
Not receiving events
- URL must be HTTPS. HTTP endpoints are rejected.
- Return
2xx within 15 seconds. If your handler times out, the event is marked as failed and retried.
- Check the portal. Open the self-service portal to see delivery attempts, response codes, and retry status.
- Check event filters. If you specified
filter_types when creating the endpoint, only those events are delivered.
Signature verification failing
- Use the raw request body for verification, not a parsed JSON object. Parsing and re-serializing changes whitespace and key order, which breaks the signature.
- Verify the timestamp is within your tolerance window (default: 5 minutes).
- Make sure you’re using the correct signing secret for this endpoint.
See Signature Verification for framework-specific examples.
Transactions
Duplicate transactions
Transactions can appear twice during the pending-to-posted transition. A pending transaction and its posted version may coexist briefly until the pending record clears.
Always use the transaction id for deduplication — don’t match on amount, date, or description.
Amount has unexpected sign
The amount field follows accounting convention:
- On a credit card account, a purchase (outflow) has a positive amount
- On a checking account, a purchase (outflow) has a negative amount
Use cash_flow_direction for logic and amount_absolute for display:
// Display
const label = `${tx.cash_flow_direction === "outflow" ? "-" : "+"}$${tx.amount_absolute}`;
// Logic
if (tx.cash_flow_direction === "outflow" && tx.amount_absolute > 100) {
// Large purchase
}
date_posted vs date_authorized
| Field | Use when |
|---|
date_posted | Reporting, accounting, reconciliation. This is the final settlement date. |
date_authorized | Showing when the user actually made the purchase. May be null for some transaction types. |
Data freshness
Stale data / as_of timestamp
Yoshi syncs data from your connected institutions on a recurring schedule. The as_of field on each account tells you when data was last refreshed.
If as_of is more than a few hours old:
- The institution may sync less frequently (some update once per business day)
- There may be a temporary connection issue
Show users the as_of timestamp so they know how current the data is. Do not promise real-time accuracy.
Missing recent transactions
Transactions appear after the next sync cycle, not instantly. If a user just made a purchase:
- It won’t appear until the next sync
- It may appear as
pending first
- It settles as
posted within 1–3 business days
Cursor returns the same data
- Make sure you’re passing the
cursor from the most recent response, not a cached cursor.
- Don’t store cursors between sessions — they’re designed for sequential traversal.
- If a cursor becomes invalid, the API returns
400. Restart pagination without a cursor.
See Pagination for the full guide.
Still stuck?
Include the request_id from the error response when reaching out for support. Every API response includes it in the meta object and as an X-Request-Id header:
{
"meta": {
"request_id": "req_a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}
Last modified on April 30, 2026