Skip to content

Report credits in zenrows usage, and say the allowance renews - #17

Open
pablomogamon wants to merge 2 commits into
ZenRows:mainfrom
pablomogamon:act/credits-renew-message
Open

Report credits in zenrows usage, and say the allowance renews#17
pablomogamon wants to merge 2 commits into
ZenRows:mainfrom
pablomogamon:act/credits-renew-message

Conversation

@pablomogamon

Copy link
Copy Markdown

Two small changes to the same blind spot: a client that runs out of credits is not told the one thing that would stop it retrying.

zenrows usage was dropping the credits

/v1/subscriptions/self/details returns usage_credits and credit_limit alongside the dollar figure. Neither was declared on UsageDetails, so they arrived through the index signature and went nowhere — the command printed dollars and left the reader to convert.

They cannot convert. The rate is per plan, not a platform constant: Free bills $0.001/credit (5,000 credits for $5) while a Business yearly plan bills 8.9997e-05. The payload carries plan.unit_cost for exactly this reason, and credit_limit × unit_cost === plan.price holds on the live response.

Before and after, run against a real account:

  Plan:    Business (YEARLY)
  Status:  ACTIVE
+ Credits: 62,982 of 35,999,978 used (0%) — 35,936,996 left
  Usage:   5.66823039823616 (0% of plan)
  API concurrency: 0 in use / 100 max
  Billing period ends: 2027-05-13T08:39:53Z

Credits are the unit the docs, the dashboard and the error messages all speak in, so they lead; the dollar line stays underneath. usage_credits, credit_limit and unit_cost are now declared, with unit_cost carrying a note against ever hardcoding a conversion factor — an easy mistake to make from one account's numbers and wrong on every other plan.

quotaExhausted did not mention the renewal

It told an exhausted account to "add credits or upgrade your plan" and stopped. Both are true, both cost money, and neither is the cheapest answer: the allowance comes back at the end of the billing period.

Leaving that out is what turns a wait into a retry loop. It matches what the API itself says on AUTH004 — "Purchase a new subscription to continue" — and the traffic shows the result: 413 accounts are against this wall, one of them at ~3.3 requests/second for seven days with zero successes.

The date is not hardcoded here. zenrows usage prints the exact period_ends_at, and is now suggested in both the claimed and unclaimed cases rather than only the claimed one.

No behaviour reclassified

POLICY_MAX_CREDITS_EXCEEDED already means the account wall, and a policy cap already raises POLICY_LIMIT_EXCEEDED for both max_pages_per_run and max_credits_per_run. I had read the docs table as evidence the two were conflated in code and came close to "fixing" that; they are not. Only wording, one suggested command, and three previously undeclared response fields change here. The docs table is corrected separately in ZenRows/docs#213.

Verification

  • typecheck clean, 187 tests green, 2 of them new.
  • zenrows usage was run against a live account, not only unit-tested — the output above is real, and 35,999,978 − 62,982 = 35,936,996 checks out.
  • The new tests pin the formatting and the credit_limit × unit_cost === plan.price invariant that makes the rate per-plan.

Opened from a fork: I do not have push on this repo.

Context: ACT-1581, ACT-1577.

quotaExhausted told an exhausted account to "add credits or upgrade your
plan" and stopped there. Both are true, and both cost money — but neither is
the cheapest answer, which is that the allowance comes back at the end of the
billing period.

Leaving that out is why an exhausted client retries instead of waiting or
deciding. It matches what the API itself says on AUTH004 ("Purchase a new
subscription to continue"), and the traffic shows the result: one account
spent seven days at ~3 requests/second against this wall with zero successes.

The date is not hardcoded here — `zenrows usage` prints the exact
period_ends_at, and now gets suggested in both the claimed and unclaimed
cases rather than only the claimed one.

No code-path change: POLICY_MAX_CREDITS_EXCEEDED already means the account
wall, and a policy cap already raises POLICY_LIMIT_EXCEEDED. Only the wording
and the suggested command move.
Verified against the live endpoint: /v1/subscriptions/self/details returns
usage_credits and credit_limit next to the dollar figure. Neither was declared
on UsageDetails, so the command printed dollars and left the reader to
convert.

They cannot. The rate is per plan, not a platform constant: Free bills
$0.001/credit (5,000 credits for $5) while a Business yearly plan bills
8.9997e-05. The response carries plan.unit_cost for exactly this reason, and
credit_limit * unit_cost === plan.price holds on the live payload.

So the command now leads with credits used, the credit limit and what is left,
which is the unit the docs, the dashboard and the error messages all speak in.
The dollar line stays underneath.

unit_cost is declared with a note against ever hardcoding a conversion factor
— an easy mistake to make from a single account's numbers, and wrong on every
other plan.
@pablomogamon
pablomogamon requested a review from tedi-z August 27, 2026 22:42
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.

1 participant