Documentation Index
Fetch the complete documentation index at: https://docs.redbark.co/llms.txt
Use this file to discover all available pages before exploring further.
The REST API is in beta. Endpoints, request formats, and response shapes may change. Pin your integration to a specific API version once versioning is available.
The Redbark REST API provides programmatic access to accounts, balances, transactions, connections, and brokerage data. Use it to build integrations, dashboards, or workflows on top of your banking and investment data.
Base URL
Authentication
All API requests require a Bearer token. Create an API key in the dashboard and include it in the Authorization header:
Authorization: Bearer YOUR_API_KEY
API access requires an active Developer or Professional plan. Trial subscriptions (status: trialing), legacy accounts within their 14-day grace period, and grandfathered accounts also retain API access. You can revoke keys from the dashboard at any time.
Want to connect an AI agent instead? See the MCP Server documentation.
Want to test your parsing code before creating a trial account? See Sample Responses for a coherent set of example JSON payloads covering every endpoint.
Rate limits
Limits are tiered by endpoint cost and applied per API key. Unauthenticated or failed-auth requests are limited to 60 per minute, bucketed by (client IP, first 16 chars of the supplied bearer token) — so probes with different partial keys from the same IP each get their own bucket, but a single probing key+IP combination is throttled before any DB lookup.
| Tier | Endpoints | Limit |
|---|
| Cheap | /v1/connections, /v1/accounts, /v1/categories | 60 / minute |
| Mid | /v1/balances | 30 / minute |
| Heavy | /v1/transactions, /v1/holdings, /v1/trades | 30 / minute |
In addition, heavy endpoints have a per-key concurrency cap of 4 in-flight requests. Calls that arrive while four heavy requests are already in progress on the same key return 429. Issue heavy requests sequentially, or fan them out across multiple keys, to stay under the cap.
First-party sync clients identified by user agent (redbark-sure-sync, redbark-actual-sync) use a separate 300 / minute pre-auth bucket keyed only by IP. This is not relevant for third-party integrations but explains why two distinct pre-auth limits exist.
Every response includes rate limit headers:
| Header | Description |
|---|
X-RateLimit-Limit | Maximum requests allowed per window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Epoch seconds when the current bucket resets. Emitted on every response so well-behaved clients can pace themselves before hitting 429. |
Retry-After | Seconds until the next request is allowed. Set on 429 responses, and on 503 responses when the per-connection upstream breaker is open. |
Truncation and deprecation headers
Some endpoints set additional response headers to signal operational state:
| Header | When | Meaning |
|---|
X-Redbark-Truncated: true | /v1/transactions, /v1/trades when the service stops paging upstream before exhausting the result set | Fires for transactions when any of: ~5,000 rows accumulated, or the per-request page cap is hit (50 pages when accountId is omitted, ceil((offset + limit) / 1000) + 1 pages when scoped). Combine with pagination.hasMore and continue paginating with offset + limit. |
X-Cache: HIT | MISS | BYPASS | Every response from a cached endpoint | HIT served from Redis, MISS recomputed and written to Redis, BYPASS cache skipped (non-GET, Redis unavailable, or payload over the cache size limit). |
Deprecation: true + Sunset: <date> + Link: <docs-url>; rel="deprecation" | A request uses a parameter shape that’s scheduled for removal | Migrate before the Sunset date. Currently set on /v1/transactions calls that omit accountId. |
All errors return a JSON envelope:
{
"error": {
"message": "Connection not found or does not belong to this user"
}
}
A 400 validation error includes a details array. Date-validation errors raised by the service layer carry their own message; schema-layer validation failures use the generic "Invalid request parameters" message:
{
"error": {
"message": "Invalid date parameters",
"details": [
"from: Must be YYYY-MM-DD or RFC3339 with timezone (e.g. 2026-05-09T23:35:19Z or 2026-05-09T23:35:19+10:00)"
]
}
}
{
"error": {
"message": "Invalid request parameters",
"code": "invalid_params",
"details": [
"limit: Number must be greater than or equal to 1"
]
}
}
| Field | Type | Description |
|---|
message | string | Error description. Always present. |
code | string | Machine-readable error code. Omitted when not set. |
details | string[] | Validation details. Omitted when empty. |
Stable code values currently emitted:
| Code | Status | When |
|---|
invalid_params | 400 | Schema validation failure (bad query/body shape) |
invalid_date_range | 400 | from is after to |
from_too_old | 400 | from is more than ~7 years ago |
wrong_endpoint_for_category | 400 | Transactions requested for a brokerage connection, or trades/holdings requested for a banking connection |
upstream_bad_request | 400 | Banking provider rejected the request with a 4xx (date format, missing scope, etc.); the upstream detail is appended to message |
body_too_large | 413 | Request body exceeded the 16 KiB limit |
missing_authorization | 401 | Authorization header is missing |
invalid_token | 401 | API key or OAuth access token is invalid or expired |
professional_required | 403 | Brokerage endpoint called by a Developer-plan customer |
insufficient_scope | 403 | OAuth access token lacks the required scope (e.g. mcp:read) |
not_found | 404 | Unknown path or method-not-allowed on a known path |
connection_not_found | 404 | Connection ID does not exist or does not belong to the caller |
account_not_found | 404 | Account ID does not exist or does not belong to the connection |
client_disconnected | 499 | Client aborted the request before the response was sent |
upstream_breaker_open | 503 | Per-connection upstream circuit breaker is open; Retry-After indicates cooldown |
HTTP status codes
| Status | Meaning |
|---|
200 | Success |
400 | Invalid parameters |
401 | Missing or invalid API key |
403 | Subscription required (API access requires a Developer or Professional plan), or professional_required for brokerage endpoints |
404 | Resource not found, or method not allowed on a known path (Hono collapses both to 404) |
405 | MCP only: GET or DELETE on /mcp. The MCP transport is Streamable HTTP POST-only. |
413 | Request body exceeded the 16 KiB limit (code: body_too_large) |
429 | Rate limited (per-IP, per-key, or per-key concurrency cap) |
499 | Client disconnected before the response was sent (code: client_disconnected) |
500 | Internal server error |
501 | Endpoint not implemented for the connection’s provider (e.g. balances on a provider that doesn’t expose them) |
503 | Banking/brokerage provider temporarily unavailable. Retry later. Carries Retry-After when the per-connection upstream circuit breaker is open (code: upstream_breaker_open). |
List endpoints support limit and offset query parameters:
| Parameter | Type | Default | Description |
|---|
limit | integer | Varies by endpoint | Maximum number of items to return. Passed as a query string (e.g. ?limit=50). |
offset | integer | 0 | Number of items to skip. Passed as a query string. |
Paginated responses include a pagination object:
{
"data": [...],
"pagination": {
"total": 142,
"limit": 50,
"offset": 0,
"hasMore": true
}
}
| Field | Type | Description |
|---|
total | number | null | Exact count of matching rows when known. null when the count is unknown — currently emitted by /v1/trades whenever hasMore is true (the count is only known once every page has been fetched), and may also be null on other paginated endpoints when an internal row ceiling is reached without exhausting the set. Use hasMore for next-page detection, not arithmetic on total. |
limit | number | Effective page size (may be clamped from the requested value). |
offset | number | Offset that produced this page. |
hasMore | boolean | true when at least one more page is available. |
Caching
Successful GET responses are cached in Redis per (userId, path, sorted query string). Cache state is exposed on every response via the X-Cache header:
| Value | Meaning |
|---|
HIT | Served from Redis. |
MISS | Recomputed; the fresh payload was written to Redis. |
BYPASS | Cache skipped. Emitted on non-GET requests, when Redis is unavailable, or when the serialised payload exceeds the cache size limit. |
Per-endpoint TTLs:
| Endpoint | TTL |
|---|
/v1/categories | 60 minutes |
/v1/connections | 60 seconds |
/v1/accounts | 60 seconds |
/v1/balances | 5 minutes |
/v1/holdings | 10 minutes |
/v1/trades | 10 minutes |
/v1/transactions | 30 minutes |
The cache is GET-only — non-GET requests fall through to the router’s standard 404 {"error":{"message":"Not found","code":"not_found"}} response and carry X-Cache: BYPASS.
Date validation
from and to accept either YYYY-MM-DD (a bare date) or RFC 3339 with an explicit timezone — Z for UTC or ±HH:MM for an offset. Naive datetimes like 2026-05-09T23:35:19 (no Z, no offset) are rejected with a 400 carrying:
from: Must be YYYY-MM-DD or RFC3339 with timezone (e.g. 2026-05-09T23:35:19Z or 2026-05-09T23:35:19+10:00)
This is enforced locally rather than at the upstream provider so callers see a clean 400 instead of a misleading 503.
Upstream provider errors
When the banking provider rejects a request with a 4xx (validation issue we don’t catch locally), the response is surfaced as a 400 with code: upstream_bad_request and message: "Upstream rejected request: <provider detail>". This avoids collapsing caller-input errors into the generic 503 "provider temporarily unavailable".
A per-(provider, userId, connectionId) circuit breaker protects /v1/transactions. After 3 upstream failures within a 60-second rolling window, the breaker opens for 60 seconds and subsequent requests for that connection fail fast with a 503, code: upstream_breaker_open, and a Retry-After header. The breaker is in-process per Railway replica, so one tripped replica does not stop other replicas from attempting the upstream.
OpenAPI specification
A machine-readable OpenAPI 3.1 spec is available at:
https://api.redbark.co/openapi.json
Interactive documentation is available at /doc via Swagger UI.
Quick start
Get your API key
Go to Settings > API Keys in the dashboard and create a new key. Copy the key. It is only shown once. List your connections
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.redbark.co/v1/connections
List accounts
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.redbark.co/v1/accounts
Get balances
Use account IDs (UUIDs) from step 3:curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.redbark.co/v1/balances?accountIds=a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890,b2c3d4e5-f6a7-8901-b2c3-d4e5f6a78901"
Fetch transactions
Use a connectionId (UUID) from step 2:curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.redbark.co/v1/transactions?connectionId=b7c4a1e2-8d3f-4e9a-9c5b-1f2a3e4d5c6b"
Brokerage endpoints
The Holdings and Trades endpoints provide access to investment portfolio data from brokerage connections. These endpoints require a Professional plan subscription.