Skip to content

fix(api): standard 429 response contract with retryAfter and Retry-After header (#272) - #530

Merged
Miracle656 merged 1 commit into
Divinity-Labs:mainfrom
sparkysolutions10-pixel:fix/rate-limit-retry-after-272
Oct 22, 2026
Merged

Miracle656 merged 1 commit into
Divinity-Labs:mainfrom
sparkysolutions10-pixel:fix/rate-limit-retry-after-272

Conversation

@sparkysolutions10-pixel

Copy link
Copy Markdown
Contributor

Closes #272

Summary of Changes

  • Implements standardized 429 Too Many Requests contract when callers exceed rate limits.
  • Guaranteed Retry-After HTTP header indicating exact seconds to wait before retrying.
  • JSON response body now provides consistent code: "rate_limited" and integer retryAfter field:
    {
      "statusCode": 429,
      "error": "Too Many Requests",
      "code": "rate_limited",
      "message": "Rate limit exceeded, retry in 42 seconds",
      "retryAfter": 42
    }
  • Documents rate limiting quotas across tiers (unauthenticated IP, authenticated API keys, and /status monitoring) and client retry instructions in README.md.
  • Added end-to-end unit tests in src/__tests__/rateLimitContract.test.ts.

@sparkysolutions10-pixel
sparkysolutions10-pixel force-pushed the fix/rate-limit-retry-after-272 branch from af52c95 to 363009d Compare October 19, 2026 20:22
@Miracle656
Miracle656 merged commit 8fecacf into Divinity-Labs:main Oct 22, 2026
1 check passed
Miracle656 added a commit that referenced this pull request Oct 22, 2026
  tsc --noEmit   clean
  vitest         99 files, 1140 passing (up from 97 / 1135)
  dropped-tests  none

  #280  close the vulnerabilities that are actually reachable
  #426  keep watched pairs as metric labels, bounded
  #529  GET /price/:asset/fiat for local currency
  #530  standard 429 response contract with retryAfter

#530 pairs well with what landed earlier: #307 standardised empty-vs-missing
status codes, and this does the same for rate limiting. An API that answers
429 without telling a client when to come back forces every consumer to invent
its own backoff, and they will not agree.

#461 (generate TypeScript and Python route snippets from the OpenAPI spec) was
dropped. It fails three of its own tests even after running `npm run
snippets:gen`:

  × render deterministically
  × committed files match the generator
  × contain no placeholder hosts or addresses

The regeneration produced no files at all, so the generator is not doing what
the test expects of it. The third case is the one I care about most -- a
snippet carrying a placeholder address is exactly how an invented contract id
reaches a contributor, which has already cost us a sweep. Worth getting right
rather than merging around.

Claude-Session: https://claude.ai/code/session_01USgemLt4Rnz4SGB1Srf3GB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nothing tells a caller what to do with a 429

2 participants