query-cost-guard
Per-query cost guard for BigQuery. Set dollar or byte limits before any query runs.
Why
BigQuery charges per byte scanned. A single bad query can burn through your daily quota.
The native maximum_bytes_billed only speaks in bytes — this package lets you think in dollars.
Install
pip install query-cost-guard
# With CLI
pip install query-cost-guard[cli]
Usage
Dollar-based guard
from google.cloud.bigquery import Client
from query_cost_guard import QueryCostGuard, QueryParams
guard = QueryCostGuard(client=Client())
result = guard.query(
sql="SELECT * FROM `project.dataset.table` WHERE date > '2026-01-01'",
params=QueryParams(max_cost_usd=0.50),
)
result.rows # list[dict]
result.actual_cost_usd # 0.03
result.bytes_billed # 4_831_838_208
result.duration_seconds # 2.41
Byte-based guard
result = guard.query(
sql="SELECT * FROM `project.dataset.table`",
params=QueryParams(max_bytes=5 * 1024**4), # 5 TiB
)
max_cost_usd and max_bytes are mutually exclusive.
When cost exceeds the limit
BigQuery rejects the query before execution — no bytes are billed.
from query_cost_guard import QueryCostExceededError, QueryParams
try:
result = guard.query(sql=huge_query, params=QueryParams(max_cost_usd=0.10))
except QueryCostExceededError as e:
e.context.estimated_cost_usd # 2.34
e.context.max_cost_usd # 0.10
e.context.bytes_estimated # 374_000_000_000
With existing job config
from google.cloud.bigquery import QueryJobConfig, ScalarQueryParameter
result = guard.query(
sql=sql,
params=QueryParams(max_cost_usd=1.00, query_tag="observation_troubleshooting"),
job_config=QueryJobConfig(
query_parameters=[ScalarQueryParameter("account_id", "INT64", 115)]
),
location="US",
)
Pricing
Live pricing is fetched from the Cloud Billing Catalog API and cached for 24 hours. If the API is unavailable, falls back to the static rate of $6.25/TiB.
from query_cost_guard import QueryCostGuard, OnPricingFailure
# Fail hard if pricing can't be resolved
guard = QueryCostGuard(client=client, on_pricing_failure=OnPricingFailure.RAISE)
# Or pin a known price
guard = QueryCostGuard(client=client, price_per_tib_override=6.25)
Dry-run estimation
estimate = guard.estimate(sql="SELECT * FROM `project.dataset.big_table`")
estimate.estimated_bytes # 4_831_838_208
estimate.estimated_cost_usd # 0.0275
estimate.price_per_tib_usd # 6.25
CLI
Estimate query cost without executing:
query-cost-guard --project my-gcp-project estimate \
--query "SELECT * FROM \`project.dataset.table\`"
# Estimated bytes: 4,831,838,208
# Estimated cost: $0.0275 (at $6.25/TiB)
With a cost threshold:
query-cost-guard --project my-gcp-project estimate \
--query "SELECT * FROM big_table" \
--max-cost 0.50
# Guard: ✓ PASS (limit $0.50)
JSON output, file input, stdin:
query-cost-guard --project my-proj estimate --query "SELECT 1" --json
query-cost-guard --project my-proj estimate --file query.sql
cat query.sql | query-cost-guard --project my-proj estimate
Credentials default to Application Default Credentials. Override with --credentials /path/to/sa.json.
How it works
Uses BigQuery's maximum_bytes_billed on the QueryJobConfig. Single API call, no race condition, enforced server-side.
Both estimate() and the CLI use dry_run=True for zero-cost estimation.
License
MIT
Metadata
Release files for query-cost-guard 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| query_cost_guard-0.1.2.tar.gz | 1.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| query_cost_guard-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.0 MB
Release files / query_cost_guard-0.1.2.tar.gz
| Download URL | query_cost_guard-0.1.2.tar.gz |
|---|---|
| Size | 1.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9bb4b4f8a6dd51e8cd41fceba9297c812672348e053473d330ea34eee4fad288
|
|
BLAKE2b-256 checksum How to use checksums |
3dc0a4a8fca753cf96dd120acfc5fee55297d454697764a5631c02250340352f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 23, 2026.
Transparency logRelease files / query_cost_guard-0.1.2-py3-none-any.whl
| Download URL | query_cost_guard-0.1.2-py3-none-any.whl |
|---|---|
| Size | 10.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
daefc31fb143532b763731b3a6643d1307f72523f053289ab450bfa5fc33831f
|
|
BLAKE2b-256 checksum How to use checksums |
290ac0f56f5fa315f42c16237b4abf8f37e3a8f5789eab982cb840b431c3faec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 23, 2026.
Transparency log