Know what a SQL change costs before you merge it.
DiffCost dry-runs each changed query or dbt model in your own BigQuery project, once on the base branch and once on the pull request, and posts the price change on the pull request. Dry runs are free and read no rows.
Free for every GitHub organization. A GitHub owner's first sign-in starts a 14-day Team trial, with no card.
Partitions scanned per run
Cost per run
+$7.57 per run, about +$227 a month at 30 runs. The query now reads every partition back to 2013.
No change. The filter limits the scan to partitions from 2024 on.
One report on the pull request
Every changed query or model gets a row: what it costs to run today, what it will cost after the merge, and why it changed. Each new push updates the same comment.
💰 DiffCost — BigQuery Cost Report (cost per run)
| Query / Model | Current | This PR | Scan Delta | Cost Change |
|---|---|---|---|---|
| fct_daily_revenue | $2.09 | $9.66 | +1.21 TiB | +$7.57 (↑362%) |
| stg_orders | $0.031 | $0.031 | 0 B | — |
| TOTAL | $2.12 | $9.69 | +1.21 TiB | +$7.57 (↑357%) |
⚠️ This PR increases query costs by $7.57/run (+357%, ≈$227.10/mo at 30 runs/mo).
Largest single-query increase: fct_daily_revenue +$7.57/run (↑362%).
📊 Data Scanned per run: 0.34 TiB → 1.55 TiB (+1.21 TiB)
🔍 Root cause analysis
🔴 fct_daily_revenue: Partition filter removed: order_date >= '2024-01-01'
Removing a partition filter causes a full table scan.
What DiffCost checks
The cost of each change, per run
BigQuery's dry run returns the bytes a query would scan. DiffCost prices them at your rate per TiB. On Editions (slot) pricing it reports bytes and a scan multiplier instead, because slot cost depends on your reservation.
The usual causes
It compares the SQL before and after the change and names a dropped partition filter on a date column,
a new SELECT * or a join with no condition as the likely cause.
- Partition filter removed: order_date >= '2024-01-01'
- SELECT * added
- CROSS JOIN added: dim_calendar
A merge check you can require
On Starter and Team, the DiffCost GitHub App posts a diffcost/cost-check status. It fails when the
largest single-query increase is over your limit in $/run or percent. Make it a required check to block the merge.
# .diffcost.yml thresholds: absolute: 5.00 # USD per run relative: 200 # percent
Your SQL stays with you
Queries go only to BigQuery, in your project, and sign-in can be keyless with Workload Identity Federation. DiffCost's service receives the repository, the pull request and numbers: per-run costs and bytes. Never SQL, table or model names, file paths or rows.
{
"repo": "acme/analytics",
"pr_number": 142,
"total_cost_before": 2.12,
"total_cost_after": 9.69,
"queries": [
{ "bytes_before": 367676688328, "bytes_after": 1699405171891,
"cost_before": 2.09, "cost_after": 9.66 },
{ "bytes_before": 5453577674, "bytes_after": 5453577674,
"cost_before": 0.031, "cost_after": 0.031 }
]
}
Set up in three steps
The quickstart has the exact commands and the full workflow file to copy.
-
Let a service account dry-run queries
BigQuery Job User on the project, and Data Viewer on the datasets your SQL reads. See BigQuery access.
-
Add the workflow
One job on every pull request: check out with full history, sign in to Google Cloud, run
diffcost/action@v1. dbt projects also pass their manifest; see the dbt guide. -
Open a pull request
The report appears as a comment. To block merges and keep a history, add an API key and the DiffCost GitHub App; see Merge blocking.
PRs from forks get structural analysis in the job summary only: GitHub gives them no secrets, no OIDC token and no write access, so the workflow skips the Google Cloud sign-in for them.
name: DiffCost on: pull_request permissions: contents: read pull-requests: write id-token: write jobs: diffcost: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: google-github-actions/auth@v2 if: github.event.pull_request.head.repo.full_name == github.repository with: workload_identity_provider: "projects/…/providers/github-provider" service_account: "diffcost-ci@my-project.iam.gserviceaccount.com" - uses: diffcost/action@v1 with: sql_paths: "queries/**/*.sql" gcp_project: "my-project"
Free to start
The report is free for every GitHub organization. Starter ($49 a month) adds merge blocking, Slack alerts and cost trends. Team ($149 a month) adds per-repository limits and cost by author.