使用方法
- 复制安装命令,在自己的智能体环境安装前先检查源码包。
- 从原始文档核对所需工具、凭据和支持的宿主。
- 说明输入材料与期望交付物,并对照来源检查输出。
npx skills add https://github.com/alpacahq/alpaca-skills --skill alpaca-broker-rate-limits-resilience开始前需了解
- 安装不包含模型订阅、API 额度或访问文件的权限。
- 仓库星数和安装量反映活跃情况,不等于独立质量或安全测试。
按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。
Alpaca — Rate Limits & Resilience
Alpaca's APIs are rate-limited and occasionally flaky under load. Any client that does more than a handful of calls — especially bulk jobs, backfills, and reconciliation sweeps — needs disciplined retry, backoff, and concurrency control. These patterns are transport-level and apply in any language.
Read alpaca-broker-integration first.1. Rate-limit headers — read them on every response
Alpaca returns standard headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | requests allowed in the window |
X-RateLimit-Remaining | requests left in the current window |
X-RateLimit-Reset | unix timestamp (seconds) when the window resets |
Parse them on every response, not just on errors. Two uses:
- Proactive: slow down as
Remainingfalls toward zero, rather than waiting to be throttled. Any warning threshold you pick is your own starting point, derived fromLimit— Alpaca publishes none. - Reactive: on
429, useResetto wait exactly until the window opens.
Broker limits are applied at the correspondent level — across your whole integration, not per end-user account, so every process you run shares one budget. They're set per partner and not published; react to the headers.
Sandbox limits are significantly lower than production and are not a reliable signal of production headroom. Alpaca explicitly says not to load-test against sandbox — mock the Alpaca calls instead and have the stub simulate latencies sampled from real production calls.
2. The retry loop (pseudocode)
MAX_ATTEMPTS = 10
INITIAL_DELAY_MS = 1000
MAX_DELAY_MS = 60000
backoff(n) = min(INITIAL_DELAY_MS * 2^(n-1), MAX_DELAY_MS) + random(0, 500)
for attempt in 1..MAX_ATTEMPTS:
res = http(request) # with a sane timeout (see §5)
limit, remaining, reset_at = parse_rate_headers(res.headers)
if remaining < low_water(limit): log_warn("approaching rate limit", reset_at)
if res.status == 429:
# wait until the window resets, plus a small buffer
wait = (reset_at - now()) if reset_at else backoff(attempt)
sleep(max(0, wait) + 1000 + random(0, 500))
continue
if res.status in (500, 502, 503, 504) or network_error:
sleep(backoff(attempt))
continue
return res # success or non-retryable 4xx
raise last_errorKey points:
- On `429`, wait until `X-RateLimit-Reset` + a ~1s buffer, then keep exponential backoff underneath —
Resetsays when the window rolls, not whether the herd behind you re-saturates it. - Exponential backoff (
base * 2^(attempt-1)) for network errors and 5xx, capped (the docs' example caps at 60s). With base 1s and 10 attempts the tail is minutes — fine for background jobs, too slow for user-facing calls (use fewer attempts there). - Jitter every wait. It's part of the documented recommendation, not an extra: a correspondent-wide limit means all your workers are throttled together and would otherwise retry in lockstep.
- Don't retry non-retryable 4xx (
400/403/422) — those won't fix themselves; surface them.
3. Bounded concurrency
Parallelism speeds bulk jobs but is the fastest way to hit limits. Use a fixed worker pool, not unbounded fan-out.
- Size the pool from `X-RateLimit-Limit`, not from a fixed number, and remember the limit is correspondent-level: every worker, cron job, and web process you run draws on the same budget. Tune down from observed
Remaining; your own downstream store is often the tighter constraint. - Cache per-entity reads within a run (e.g. an account's buying power, or a per-account transfer list) so you don't refetch the same thing across items in a batch.
- For per-item throttling, a small fixed sleep between calls (e.g. 100ms) is a crude-but-effective floor when you can't easily coordinate a pool.
4. Pagination loops
List endpoints page forward with a token — never assume one response is complete.
- Activities (
/v1/accounts/activities): the response is a bare JSON array with no pagination header — pass the `id` of the last activity in the page back aspage_tokenand stop when a page comes back short.page_sizedefaults to and maxes at 100 (no maximum whendateis set — it returns everything);directionisasc/desc, defaultdesc. - Market-data bars (
/v2/stocks/bars): page vianext_page_tokenin the body → pass back aspage_token. Rememberlimitcounts across all symbols and results sort by symbol-then-time, so a single page may contain only the first symbol(s) — keep paging. - Wrap each page fetch in the retry loop from §2.
token = null
loop:
page = fetch(url + (token ? "&page_token="+token : "")) # via retry loop
accumulate(page.items)
if body_token_endpoint:
token = page.next_page_token
if not token: break
else: # activities: bare array, no token
if len(page.items) < page_size: break
token = page.items[-1].id5. Timeouts & batch sizing
- Always set an HTTP timeout (15–30s is a reasonable starting point, not an Alpaca number). A hung connection without a timeout stalls a whole worker pool. (SSE streams are the exception — they're meant to stay open; see
alpaca-broker-sse-events.) - Use a shared HTTP client / connection pool rather than constructing one per request, so keep-alive and connection reuse work.
- Detect silence on the market-data WebSocket and reconnect + resubscribe. A slow client is disconnected, and the
407 slow clienterror is "not guaranteed to arrive before you are disconnected" — so a dead socket can look identical to a quiet one (seealpaca-broker-market-data). - When writing reconciliation results to your own store, chunk bulk inserts to stay under DB statement-size limits and keep transactions reasonable; pick the chunk size from your own database's behavior.
6. Resilience checklist for a bulk/cron job
- [ ] Rate-limit headers parsed every response; proactive warn near the limit.
- [ ]
429→ wait untilX-RateLimit-Reset+ buffer. - [ ] Exponential backoff with jitter for 5xx/network; capped delay and capped attempts.
- [ ] Non-retryable 4xx surfaced, not retried.
- [ ] Bounded worker pool; per-run caching of repeated reads.
- [ ] Pagination loop until the token is empty.
- [ ] HTTP timeout on every call; shared client.
- [ ] Bulk DB writes chunked and idempotent (upsert) — see
alpaca-broker-reconciliation-idempotency. - [ ] Structured logging with a trace/correlation ID per item for debugging partial failures.
Related skills: safe re-runs of jobs → alpaca-broker-reconciliation-idempotency; the heal/poll jobs that use these patterns → alpaca-broker-reconciliation-idempotency; market-data pagination specifics → alpaca-broker-market-data.

