Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kash.bot/llms.txt

Use this file to discover all available pages before exploring further.

The REST API enforces a per-key sliding-window quota. When a key exceeds it, the next request returns 429 RATE_LIMIT_EXCEEDED with Retry-After and X-RateLimit-* headers so you can back off cleanly.

Per-tier defaults

TierRate limit (req/min)Daily spend capPer-trade capHigh-value confirm gate
free601,000 USDC500 USDCnone
developer30010,000 USDC2,500 USDCnone
enterpriseCustom (admin-set)100,000 USDC25,000 USDC25,000 USDC
mmCustom (admin-set)1,000,000 USDC50,000 USDC50,000 USDC
A tier sets four independent limits:
  • Rate limit — HTTP requests per minute, sliding window. Hit this and you get 429 RATE_LIMIT_EXCEEDED.
  • Daily spend cap — rolling 24h aggregate USDC volume per key. Hit this and you get 409 SPENDING_LIMIT_EXCEEDED with extensions.cap = "daily_volume". Sells and close-position intents do NOT draw against this cap.
  • Per-trade cap — single-trade USDC amount. Hit this and you get 409 SPENDING_LIMIT_EXCEEDED with extensions.cap = "per_trade".
  • High-value confirm gate — for tiers that have one, any trade ≥ this amount returns 202 Accepted with a confirmationToken; you then POST /v1/trades/{id}/confirm to actually execute. Two-step flow lets you catch a runaway bot before it commits a six-figure trade.
The rate-limit window is per user_id, not per individual key. If a single user holds two keys, HTTP requests across both share the quota — so issuing extra keys doesn’t multiply your throughput. The spending caps, however, are per key — each key gets its own daily allotment. Each user can hold at most 5 active keys at a time (revoked keys don’t count). This guards against quota-pooling abuse patterns.

Why these caps exist

The caps are not punitive. Five concrete reasons:
  1. Blast radius on key compromise. API keys leak — committed to git, dumped in error reports, laptop stolen. The cap bounds the loss between leak-time and revoke-time.
  2. Runaway-bot circuit breaker. A trading loop misfires (off-by-one in your sizing, retry storm on a timeout, missing dedup on a webhook). The cap is a hard stop you cannot disable from inside your own code.
  3. AMM stability. Kash markets use a Pythagorean AMM with thin per-outcome liquidity. A trade consuming more than ~5% of available liquidity moves the price substantially. Per-trade caps protect the AMM curve for everyone.
  4. Compliance ladder. Higher daily volumes implicitly require enhanced KYC, ToS attestation, and sanctions screening. The tier names map to that ladder.
  5. Delegation guardrails. API keys are a scoped delegation against the user’s Privy-managed smart account — Kash never holds the keys or the funds, but for the duration of the delegation a leaked key (or a runaway bot) can move balance up to whatever the smart account holds. Caps bound that blast radius. The user can revoke any key at any time; the cap is the runtime safety net while it’s active.
If you want a surface with no caps, the self-orchestrated track (@kashdao/protocol-sdk) is the right product — you sign and submit every transaction yourself with your own signer/RPC/bundler, and bear the full operational risk. Both paths are non-custodial; this one just removes the platform-side guardrails along with the platform-side orchestration.

Response headers (every response)

Every response — 200, 4xx, 429 — carries the live state of your bucket:
X-RateLimit-Limit:     300
X-RateLimit-Remaining: 287
X-RateLimit-Reset:     1730000060   # Unix epoch seconds; when remaining refills
  • X-RateLimit-Limit — your configured quota for the window.
  • X-RateLimit-Remaining — how many requests you can still send before the window resets.
  • X-RateLimit-Reset — Unix epoch seconds when Remaining refills back to Limit.
Use these to pace yourself proactively rather than waiting for a 429.

Hitting the limit

When you exceed the quota:
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 38
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1730000098

{
  "type": "https://docs.kash.bot/developer-docs/api-errors/RATE_LIMIT_EXCEEDED",
  "title": "Rate limit exceeded",
  "status": 429,
  "code": "RATE_LIMIT_EXCEEDED",
  "detail": "API key has exceeded its 300 req/min quota.",
  "requestId": "req_..."
}
Always respect Retry-After (seconds). The TypeScript SDK does this automatically with exponential backoff and jitter.

Algorithm

The rate limiter uses a sliding window for read paths (markets, portfolio, account, traces, webhooks list) and a token bucket for write paths (POST /v1/trades, POST /v1/trades/{id}/confirm).
  • Sliding window — smooth, no burst. Best for sustained polling.
  • Token bucket — allows short bursts (capacity = limit; refill = limit/60 per second). Best for human-driven actions.
Both algorithms are keyed on user:{userId}.

Per-route rate limit policies

Most endpoints share the per-key tier quota. A few are tighter:
Endpoint patternAlgorithmNotes
GET /v1/markets*, GET /v1/portfolio*sliding-windowStandard tier quota.
GET /v1/markets/{id}/quotesliding-windowStandard tier quota; backed by RPC.
POST /v1/trades, POST /v1/trades/{id}/confirmtoken-bucketTighter — write paths cost more.
POST /v1/webhooks/events/{id}/redelivertoken-bucketReplay amplification cap (max 5 redeliveries per event).

Best practices

  • Read your headers. Cheaper than retry-after-rejection. Aim to keep X-RateLimit-Remaining above 10% of X-RateLimit-Limit under steady-state load.
  • Use ETags on read paths. A 304 Not Modified doesn’t count toward your quota — see ETags.
  • Use the SDK. It handles Retry-After, exponential backoff, jitter, and idempotency-safe retries automatically.
  • Distribute across keys carefully. Rate-limit quota is per-user_id, so multiple keys for the same user don’t help with HTTP throughput. (Spending caps ARE per-key.) If you need higher throughput, the upgrade path is below.
  • Don’t hammer the quote endpoint. Quotes are RPC-heavy. Cache results client-side for the freshness window your strategy can tolerate (typically 5–15s).

Kill switches

Independent from rate limiting, every route has an ops-controlled kill switch. When triggered (rare — usually during chain RPC degradation), the route returns:
503 Service Unavailable
Content-Type: application/problem+json

{
  "code": "ROUTE_DISABLED",
  "title": "Route temporarily disabled",
  "detail": "This endpoint is currently disabled by ops. Try again later."
}
Treat 503 ROUTE_DISABLED like Retry-After — back off and retry. Most kill switches are toggled off within minutes.

How to upgrade your tier

Hitting RATE_LIMIT_EXCEEDED or SPENDING_LIMIT_EXCEEDED repeatedly during normal operation means you’ve outgrown your tier — that’s the signal to upgrade. Today, every tier above free is provisioned manually:
  • freedeveloper (5× rate limit, 10× spending caps): email [email protected] with your account email and a one-line description of your use case. Self-serve via Stripe is on the roadmap; until it ships we provision developer-tier keys on a case-by-case basis.
  • Anywhere → enterprise (100k/day,100k/day, 25k/trade, custom rate limit): contact sales. Custom pricing based on volume commitment.
  • Anywhere → mm (1M/day,1M/day, 50k/trade, full counterparty terms): contact partnerships. KYB, ToS attestation, and possibly escrow arrangements — this is a relationship, not a pricing tier.
enterprise and mm tiers can override any of the four axes per-key as part of the contract — a key can have a different daily_spend_limit_usdc, per_trade_limit_usdc, or high_value_trade_threshold_usdc than the tier default.

Telemetry

Your per-key telemetry — including 24h rateLimitRejections count — is exposed via GET /v1/account/usage. If you’re seeing unexpected 429s, that’s the first place to check.
curl https://api.kash.bot/v1/account/usage \
  -H "X-API-Key: $KASH_API_KEY" | jq '.data.auth["24h"].rateLimitRejections'

Next

Errors

The full RFC 7807 error format and code catalog.

ETags

If-None-Match short-circuits — free quota for unchanged data.