What is not billed: error codes and billing rules
A detailed breakdown of billing rules across HTTP status codes, JSON-RPC errors, and the Data API, with recommended actions for developers.
BlockVectra meters requests in Compute Units (CU). JSON-RPC and Data API calls are billed only after a response is obtained. This guide summarizes the billing determination rules across HTTP status codes, JSON-RPC calls, and the Data API, along with recommended actions for developers.
HTTP status codes and billing rules
The billing determination and handling rules for HTTP-level responses are as follows:
The method rows below apply to ordinary HTTP requests to /v1/{chain} and /v1/{chain}/{api_key}, not named routes such as /v1/chains. A valid WebSocket upgrade follows the WebSocket capability and authentication checks instead of the plain GET/HEAD rules.
| HTTP Status | Response Body | Scenario | Billed? | Recommended Action |
|---|---|---|---|---|
| 200 | JSON-RPC response (single or batch) | Normal response; all JSON-RPC layer errors (parse error, method rejection, upstream failure, node error) are also 200 | Evaluated per call | Inspect result or error for each call; if an error is returned, see JSON-RPC error handling below |
| 204 | Empty | All calls in the request are notifications | Notifications are billed as normal | No extra action required |
| 204 | Empty | OPTIONS preflight, for either a known or unknown chain | No | Send the JSON-RPC request with POST |
| 400 | Empty | Malformed HTTP message (cannot parse request line or headers, invalid chunked encoding), or more than 10 s between two reads of the request body | No | Check HTTP request syntax, headers, and transmission continuity |
| 402 | JSON, -32020 | Insufficient balance, allowance exhausted; when balance is known, error.data includes balance_units and balance_cu | No | Check your balance on the Console billing page or via GET /v1/topup/deposit-address (MCP get_deposit_address); top up on-chain to your account's dedicated address (see the Agent top-up guide) |
| 403 | Empty | Methods other than POST, OPTIONS, GET, or HEAD on these RPC routes (regardless of whether the chain name is known) | No | Change the HTTP request method to POST |
| 405 | Plain GET: JSON-RPC -32600, error.data.reason: method_not_allowed; HEAD: empty body. Both include Allow: POST | Plain GET or HEAD to a known, public chain, before key checks; also applies when the chain does not support WebSocket | No | Use POST for JSON-RPC |
| 401 | JSON, -32024 (missing_api_key or invalid_api_key) | Missing key on known chain, key unknown or disabled | No | Provide an active API key in x-api-key header (brand-new or rotated keys take a few seconds to take effect; wait a moment and retry) |
| 404 | POST/GET: JSON-RPC -32600 (error.data.reason: unknown_chain); HEAD: empty body, without Allow | POST, GET, or HEAD to an unknown or not-public {chain}, before key checks | No | Check the chain name in the URL against Supported Chains (must be exact lowercase slug) |
| 404 | empty body | Unmatched path (e.g. POST /v1, /v1/, POST /v1/{chain}/) | No | Include chain in URL (/v1/{chain}) |
| 408 | Empty | Exceeded 35 s from reading request headers to returning response | Possible: calls already forwarded to the node are billed as normal once the node responds | Do not unconditionally retry state-changing calls (e.g. eth_sendRawTransaction); a client disconnect does not cancel calls already forwarded |
| 413 | Empty | Request body > 2 MiB (2,097,152 bytes; see “10. Limits at a glance” in the full OpenAPI reference) | No | Keep request body under 2 MiB; split batches into smaller requests |
| 414 / 431 | Empty | URI too long (414) or request headers too large (431) | No | Shorten request URI or trim HTTP request headers |
| 429 | JSON, -32005 or -32022; carries Retry-After for rate limits (-32005); burst/batch size limits (-32022) do not carry it | Bucket balance depleted → -32005; single request CU exceeds burst capacity → -32022; account call rate limit depleted → -32005; calls in single request exceed limit → -32022 | No | For -32005 with Retry-After, wait the specified seconds before retrying; for -32022, split the request or reduce batch size (retrying as-is will never succeed) |
| 503 | JSON, -32021, with Retry-After | Billing data temporarily unavailable; server temporarily rejects request (not a balance issue, no need to top up); newly created keys return this until billing data syncs (usually a few seconds) | No | Not a balance issue, no need to top up; wait the seconds specified in Retry-After and retry |
Note: When accessed through Cloudflare, Cloudflare may return 52x or 1015 error pages; these are not generated by the service.
Charge and balance response headers: When sending
x-bv-meter: 1on HTTP requests (applicable to both JSON-RPC and Data API), a response that billed at least one call returnsx-bv-cu-charged(the Compute Units billed for this request, or the sum across billed calls of a batch) andx-bv-balance-units(the account's remaining balance units right after this charge, negative when overdrawn; omitted if the balance is unknown). Requests withoutx-bv-meter: 1, responses where nothing was billed, and 402, 403, 429, or 503 error responses omit both headers. These response headers are accessible to browser scripts via CORS, while WebSocket does not use them. The balance subtracts total unsettled usage rounded up once to whole units; hourly settlement rounds down, so the reported balance may rise by up to one unit after settlement.
JSON-RPC error codes and billing rules
The same error code can originate from the platform or the node, and billing differs:
- Errors generated by the platform itself: never billed;
- Errors returned by the node: passed through as-is and billed at the method weight, with only the node error codes listed below as exceptions.
Rule details
- Node errors not billed: The node's
-32002(batch timeout),-32003(batch response too large), and-32600(batch rejected as a whole) indicate that the node abandoned the call early; these and any notifications in the same batch are not billed. Node-32601(exposed method not implemented) and-32603(node internal failure) are not billed over HTTP or WebSocket and do not affect other calls or notifications in the batch. In addition,4444(pruned block) and-32000(historical state outside the node's state history window, defined bystate_window_blocksinGET /v1/chains) are not billed and do not affect other calls in the batch. - Node errors billed: Other errors returned by the node are billed at the method weight when they report the chain's outcome, such as
execution reverted(-32000or3withdata), the node's own-32602 invalid argument. - Balance admission and sync:
-32020indicates insufficient account balance and requires a top-up; when the balance is known,error.data.balance_unitsanderror.data.balance_cucarry what is left (it can be negative). A newly created key may return-32021(503) for a few seconds; waitRetry-Afterand retry. - Upstream failures: A
-32603generated by the platform due to upstream communication failure or malformed response (upstream unavailable,no response from upstream,malformed upstream response) carriesdata.reason: upstream_unavailable. - Notification billing: Notifications (204) are billed at their method weights.
JSON-RPC error codes table
| Code | Source | HTTP | Message | Reason | Billed? | Recommended Action |
|---|---|---|---|---|---|---|
| -32700 | BlockVectra | 200 | parse error | - | No (costs 1 CU rate-limit token; see “9. Rate limiting” in the full OpenAPI reference) | Fix request JSON syntax |
| -32600 | BlockVectra | 200 | invalid request | invalid_request | No (costs 1 CU rate-limit token; see “9. Rate limiting” in the full OpenAPI reference) | Fix JSON-RPC request syntax and structure |
| -32600 | BlockVectra | 200 | batch too large: max <N> calls | batch_too_large (+max) | No | Split batch into calls under the limit (standard batch limit is 100) |
| -32600 | BlockVectra | 200 | invalid request: ambiguous member name | invalid_request | No | Remove duplicate or ambiguous member names in JSON objects |
| -32601 | BlockVectra | 200 | method not available: <method> | - | No | Call only methods allowed for this chain (see Supported Chains) |
| -32600 | BlockVectra | 404 | unknown chain | unknown_chain | No | Check the chain name in the URL |
| -32602 | BlockVectra | 200 | eth_getLogs block range too large: max <N> blocks | - | No | Narrow the eth_getLogs block range (limit defined per chain, e.g. 1000 blocks) |
| -32602 | BlockVectra | 200 | tracer not allowed | - | No | Use an allowed native tracer (callTracer, flatCallTracer, prestateTracer, 4byteTracer, noopTracer, or omit) |
| -32602 | BlockVectra | 200 | trace timeout not allowed | - | No | Set a valid Go duration string with timeout ≤ 30s |
| -32010 | BlockVectra | 200 | node is syncing; calls are temporarily unavailable | - | No | Node is syncing, retry later (except for eth_chainId) |
| -32011 | BlockVectra | 200 | historical state is not available beyond the most recent <N> blocks | - | No | Query a more recent block (target block must be within the state window; avoid safe/finalized/earliest tags) |
| -32000 | BlockVectra | 200 | transaction not found | not_found | No | Verify transaction hash (0x + 64 hex characters) |
| -32000 | BlockVectra | 200 | block not found | not_found | No | Verify block hash or number |
| -32000 | BlockVectra | 200 | upstream response too large | response_too_large | No | Narrow query scope or split requests |
| -32005 | BlockVectra | 200 | - | overloaded | No | Server temporarily overloaded, retry later |
| -32005 | BlockVectra | 429 | rate limit exceeded | key_rate_limit / free_plan_call_limit / concurrency_limit | No | Reduce request frequency; honour Retry-After when present |
| -32022 | BlockVectra | 429 | request cost <N> CU exceeds burst capacity <M> CU | request_exceeds_burst | No | Split request or batch so single-request CU is below burst capacity |
| -32022 | BlockVectra | 429 | request has <N> calls, exceeding the free-plan limit of <M> calls per second | free_plan_batch_too_large (+max) | No | Split batch to fit under per-second limit, or upgrade to a paid plan |
| -32603 | BlockVectra | 200 | upstream unavailable | upstream_unavailable | No | Upstream communication failure, retry later |
| -32603 | BlockVectra | 200 | no response from upstream | upstream_unavailable | No | Upstream did not respond, retry later |
| -32603 | BlockVectra | 200 | malformed upstream response | upstream_unavailable | No | Upstream response malformed, retry later |
| -32603 | BlockVectra | 200 | - | - | No | Rare internal error, retry later |
| -32020 | BlockVectra | 402 | insufficient balance | balance_exhausted / free_grant_exhausted (+topup_url, and +balance_units / balance_cu when balance is known) | No | Check your balance on the Console Billing page or via GET /v1/topup/deposit-address (MCP get_deposit_address); top up on-chain to your account's dedicated address (see the Agent top-up guide) |
| -32021 | BlockVectra | 503 | billing data temporarily unavailable | - | No | Billing data syncing (not a balance issue); wait Retry-After seconds and retry |
| 4444 | Node | 200 | pruned history unavailable | - | No | Requested block was pruned by node; not billed; does not affect batch |
| -32000 | Node | 200 | historical state ... is not available | - | No | Outside node state history window; not billed; does not affect batch |
| -32000 | Node | 200 | old data not available due to pruning... | - | No | Outside node history window (window determined by state_window_blocks); not billed; does not affect batch |
| -32002 | Node | 200 | <node message> | - | No | Node timed out on batch and abandoned call; not billed; notifications in batch also not billed |
| -32003 | Node | 200 | <node message> | - | No | Node batch response too large and abandoned; not billed; notifications in batch also not billed |
| -32601 | Node | 200 | <node message> | - | No | Exposed method is not implemented by the node; use another supported method |
| -32603 | Node | 200 | <node message> | - | No | Node internal failure; retry with backoff |
| -32600 | Node | 200 | <node message> | - | No | Entire batch rejected by node; not billed; notifications in batch also not billed |
| Other | Node | 200 | <node message> | - | Yes (method weight) | Chain outcome (e.g. execution reverted, node -32602); check contract call parameters |
Data API billing rules
The Data API wraps read-only chain data into REST endpoints. Its billing and error handling follow these rules:
Rule details
- Only 2xx successful responses are billed.
- Unavailable operations outside coverage (such as unsupported chains or blocks outside trace coverage) return HTTP 422
no_coverage, which is not billed but counts toward rate limits. - HTTP 401, 402, 404, and 429 responses are not billed. For response headers (
x-bv-meter: 1), see HTTP status codes and billing rules.
Data API status codes table
| HTTP Status | Error Code / Scenario | Billed? | Recommended Action |
|---|---|---|---|
| 200 | Successful data response | Yes (Data API operation CU weight) | Parse data, meta, and next_cursor in the response envelope |
| 400 | Request parameters malformed or missing required fields | No | Check and fix query or body parameters |
| 402 | Balance exhausted (error.code: "insufficient_balance", includes balance_units and balance_cu when balance is known) | No | Check your balance on the Console billing page or via GET /v1/topup/deposit-address (MCP get_deposit_address); top up on-chain to your account's dedicated address (see the Agent top-up guide) |
| 401 | API key missing, unknown or disabled (error.code: "missing_api_key" or "invalid_api_key") | No | Pass an active API key in the x-api-key header |
| 404 | Unknown or not-public chain (error.code: "not_found"), or requested object does not exist | No | Check the chain slug in the URL (must be exact lowercase) and the request path |
| 409 | Requested block or window is above the current indexed height (error.code: "not_indexed_yet", includes indexed_through) | No | Query blocks up to indexed_through or retry later |
| 422 | Chain-specific operation unavailable (e.g. unsupported chain or outside trace coverage, error.code: "no_coverage") | No (counts toward rate limits) | Check supported features via GET /v1/status (free, keyless data_features) |
| 429 | Rate limit exceeded (error.code: "rate_limited"), or a single request costs more than the key's burst capacity (error.code: "cost_exceeds_burst") | No | Reduce request frequency; split oversized requests (a burst-exceeding request will never succeed as sent) |
| 503 | Data service temporarily unavailable (error.code: "unavailable"), or the chain is busy (error.code: "gateway_overloaded") | No | Retry later and honour Retry-After when present |
Query balance (GET /v1/account)
An API key holder can check balance and key quota details directly without incurring any billing or deducting Compute Units (CU):
curl -H "x-api-key: $BLOCKVECTRA_API_KEY" https://api.blockvectra.com/v1/account- Free and unbilled:
GET /v1/accountis free. It is never billed, does not deduct CU, and returns HTTP 200 with the current balance even when it is zero or negative (it never returns 402). - Authentication: Key authentication uses the
x-api-keyheader exclusively (path keys and Bearer tokens are not accepted). Missing header returns 401missing_api_key; invalid or revoked keys return 401invalid_api_key. (Expired keys return 403key_expired; transient service unavailabilities return 503auth_unavailableorbilling_unavailablewithRetry-After.) - Rate limiting: Has an independent limit of 5 requests per second per key ID, independent of CU metering and billing (see “10. Limits at a glance” in the full OpenAPI reference). Exceeding the limit returns HTTP 429
rate_limitedwith aRetry-Afterheader.
Response fields:
key_id: The API key's identifier string.plan: Account plan type (freewhen the account has a free-plan call-rate allowance;paidotherwise).balance_units: Remaining account balance in units (may be zero or negative).balance_cu: Remaining balance converted to Compute Units (CU).balance_as_of_age_ms: Milliseconds elapsed since the balance was read from its data source.key: Key-specific limits and quota details:cu_per_sec: Token-bucket refill rate in CU per second.burst_cu: Token-bucket burst capacity in CU.cu_cap: Lifetime CU cap for this key, ornullif uncapped.cu_cap_remaining: CU remaining undercu_cap, ornullif uncapped (can be zero or negative).expires_at: RFC 3339 expiration timestamp, ornullif the key never expires.
Example response:
{
"key_id": "<key_id>",
"plan": "<plan>",
"balance_units": <integer>,
"balance_cu": <integer>,
"balance_as_of_age_ms": <integer>,
"key": {
"cu_per_sec": <integer>,
"burst_cu": <integer>,
"cu_cap": <integer_or_null>,
"cu_cap_remaining": <integer_or_null>,
"expires_at": "<expires_at_or_null>"
}
}Pricing and upgrades
The specific cost for all billed calls is determined by published CU weights:
- To inspect weights for all methods and operations, see the method weight table and the JSON-RPC CU Metering Rules.
- For plan pricing and settlement details, see the Pricing page.
- Upgrading to a paid plan: making a paid top-up removes the Free Plan's per-second call limit; each key remains subject to CU rate and burst limits.
On-Chain Top-up Process
When your account balance is insufficient or you need higher throughput, top up on-chain in the console following these steps:
- Log in to the Console: Sign in to the BlockVectra Console.
- Go to the Billing Page: Navigate to the Billing page.
- Get Your Dedicated Address: In the on-chain top-up card, copy your account's dedicated top-up address or scan the QR code.
- Transfer Funds: Transfer only using the supported networks and USDC / USDT / USDG listed on the page. Supported networks and minimum top-up amounts are shown in the console.
- Automatic Crediting: Once detected on-chain, transactions show as "Processing"; once credited, credits are automatically added to your balance.
Important Notes:
- Only use the networks and tokens explicitly listed in the console. Transfers on unsupported chains or with wrong tokens cannot be credited automatically.
- Ensure each transfer meets the minimum top-up amount indicated in the console.
- Once your first paid top-up is credited, your account is upgraded to a paid account, removing the Free Plan's per-second call limit.
Agent or server programs can directly call top-up endpoints using an API key; see the Agent programmatic top-up guide.
Webhook push billing
Push has separate weights for delivered data events, successful history queries and billable address-days. Management calls other than event history, failed delivery attempts, automatic retries and control events are free. Each delivered event is charged once; customer replay and canonical events redelivered after a reorg are new billed deliveries. Address charges use each subscription’s maximum address count while online during the UTC day; the account free-address allowance is shared across subscriptions, with older subscriptions using it first. An address in two subscriptions is counted twice; adding chains changes event fees, not address fees.
See the Blockchain Webhook API guide for setup, signature verification and delivery recovery. The stablecoin payment guide covers receipt validation and polling backfill; WebSocket subscriptions use their own connection and notification metering. Request errors are listed in the error reference. The weights below come from GET /v1/plans.
| Usage | Billing unit | CU |
|---|---|---|
push.address_day | Billable address-day | 33 |
push.history | Successful history request | 25 |
push.log | Delivered data event | 150 |
push.native_transfer | Delivered data event | 150 |
push.token_transfer | Delivered data event | 150 |
Free addresses per account per UTC day: 1000
Free address allowance per account per UTC day, shared by all subscription groups regardless of plan. For each group, count its maximum address count while online during that day; allocate the allowance in ascending group ID order. The same address in two groups counts twice; the number of chains in a group does not multiply its address count. A group offline or deleted for the entire day contributes nothing. For each group, the remaining count after its share of the allowance is multiplied by the `push.address_day` CU weight in `method_weights`. The current configured allowance comes from the same pricing policy used for the address-day charge; it is not an account capacity limit or a separate allowance per group.
Example: 10 delivered native.transfer events, 2 successful history requests and 10 billable address-days cost 10 × 150 + 2 × 25 + 10 × 33 = 1880 CU. Billable address-days are counted after the account free-address allowance.
Next steps
- Browse the datasets directory to see every dataset BlockVectra indexes.
- See the free plan and pricing to check what your account includes.
- Log in to the console to create an API key.
Last updated:
Webhook push
Create one HTTP subscription with your HTTPS URL, chains and up to 1,000,000 addresses; native, token and log matches are POSTed with a signed body at least once. Verify signatures, deduplicate event IDs and replay retained matches.
Agent programmatic top-up
Top up an RPC and Data API account on-chain over HTTP. Developers and AI Agents use an API key to check supported tokens, retrieve a dedicated deposit address and poll credit status.