Skip to content

Expose Hirebase-Usage-* headers per call (return_meta=True); QuotaExceededError for plan-cap 429s - #2

Open
ssr145 wants to merge 2 commits into
mainfrom
feat/usage-headers-quota-error
Open

ssr145 wants to merge 2 commits into
mainfrom
feat/usage-headers-quota-error

Conversation

@ssr145

@ssr145 ssr145 commented Sep 8, 2026

Copy link
Copy Markdown

Summary

Closes #1 (items 1 and 2). Found during the 2026-09-07 metering QA pass on api-dev.

  • return_meta=True on every metered method (jobs.* and companies.*, sync and async). The call returns (result, meta) where meta is a new hirebase.ResponseMeta with status_code, lower-cased headers, request_id, and usage (a UsageSnapshot parsed from Hirebase-Usage-*, None on un-metered endpoints). Per review, this replaces the earlier client.last_usage attribute so there is no mutable per-client state.

    jobs, meta = client.jobs.search(query, limit=50, return_meta=True)
    meta.usage.included_remaining

    search and get carry Literal overloads so the tuple shape type-checks; the default return shape is unchanged.

  • QuotaExceededError: raised for 429s that carry X-Billing-Code: limit_exceeded (block-mode plan at its cap). Subclasses RateLimitError, so existing except RateLimitError code still works, and exposes .usage (e.g. included_remaining, which the API's reservation 429 relies on).

  • RateLimitError.retry_after from the Retry-After header on the request limiter's 429.

  • README, docs/jobs.md, docs/companies.md, docs/errors.md updated.

Correction to #1 item 3: the 0.1.0 string is in hirebase_cli, not hirebase._version; left untouched here.

Tests

tests/test_usage_headers.py reworked for the per-call shape (incl. a check that mock transports stubbing _request still work); full offline suite 76 passed, 5 skipped.

Branched from 0e8f0b5 (salary benchmark) to avoid conflicts with that work.

🤖 Generated with Claude Code

…limit 429s

- UsageSnapshot (hirebase.UsageSnapshot): parsed from the Hirebase-Usage-*
  / X-Billing-Code / Retry-After headers of the most recent response;
  available as client.last_usage on Client and AsyncClient.
- QuotaExceededError: raised for 429s carrying
  X-Billing-Code: limit_exceeded (plan allowance used up); subclasses
  RateLimitError so existing handlers keep working; carries .usage.
- RateLimitError now carries .retry_after and .usage.
- error_from_response accepts headers; _handle_response forwards them.
- README: quota tracking section and error table rows.

Refs #1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ssr145 ssr145 changed the title Expose Hirebase-Usage-* headers; QuotaExceededError for plan-cap 429s Expose Hirebase-Usage-* headers per call (return_meta=True); QuotaExceededError for plan-cap 429s Sep 9, 2026
Per review: usage metadata is returned with the call rather than kept as
mutable state on the client.

    jobs, meta = client.jobs.search(query, limit=50, return_meta=True)
    meta.usage.included_remaining

- New hirebase.ResponseMeta (status_code, lower-cased headers, request_id,
  usage: UsageSnapshot | None), built by Client._request_meta /
  AsyncClient._request_meta.
- return_meta on every metered jobs/companies method, sync and async.
  search/get carry Literal overloads so the tuple shape type-checks.
- Default return shape unchanged; tests that stub _request keep working.
- client.last_usage removed (unreleased). QuotaExceededError.usage stays.
- README + docs updated; tests reworked (76 passed).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

Expose Hirebase-Usage-* headers and distinguish quota 429 from rate-limit 429; fix version strings

1 participant