Skip to content

chrmod/bidsmith

Repository files navigation

Bidsmith

Manage Google Ads campaigns as code. Version-controlled, reviewable, replayable.

You write what your campaigns should look like in plain-text files, you keep those files in GitHub like any other shared document, and bidsmith makes Google Ads match. Every change is a Git commit. Every change is reviewable as a pull request. Every change can be previewed before a single euro moves.

If you've used Google Ads Editor for bulk edits, bidsmith solves the same problem — but with full history, peer review, and an undo button that actually works.

Full docs and walkthroughs: chrmod.github.io/bidsmith.

Why bidsmith

  • Every change is a pull request. Edit a file, open a PR, have a teammate approve. "Who paused that campaign last Friday?" — Git tells you.
  • Preview before you spend. bidsmith plan shows you exactly what will change in Google Ads before anything ships. No surprises.
  • One source of truth. Your .bid files describe what should exist. bidsmith reconciles. Run it twice — the second run does nothing.
  • Roll back cleanly. git revertbidsmith apply. Five seconds.

Status

Pre-alpha, but usable end-to-end against a real Google Ads account. Validate, plan, and apply all work today.

Command What it does
validate Check that your .bid files are well-formed
plan Show the diff between your .bid files and what's live in Google Ads
apply Make Google Ads match the .bid files (prompts before changing anything)
pull Snapshot the live account
refresh Pull the live account down into .bid files (so you can adopt bidsmith without starting from scratch)
export Render a .bid file from a JSON description
fmt Tidy up .bid formatting

Covered today: campaigns (Search), ad groups, responsive search ads, keywords and negatives, shared keyword lists, budgets, location/language/proximity targeting, conversion actions, and call / customer assets. The full reference is on the docs site.

A .bid file looks like this

provider "google_ads" {
  customer_id       = "1234567890"
  login_customer_id = "9876543210"
}

resource "google_ads_campaign_budget" "summer" {
  name            = "Summer 2026"
  amount_micros   = 10000000
  delivery_method = "STANDARD"
}

resource "google_ads_campaign" "summer_search" {
  name                     = "Summer 2026 — Search"
  status                   = "PAUSED"
  advertising_channel_type = "SEARCH"
  campaign_budget          = google_ads_campaign_budget.summer.id

  manual_cpc {
    enhanced_cpc_enabled = false
  }
}

Check it:

$ bidsmith validate examples/basic
OK: 1 file(s) valid.

When something is wrong, errors point at the exact line:

  × invalid value "TURBO"; expected one of [STANDARD, ACCELERATED]
   ╭─[examples/broken/schema.bid:8:21]
 7 │   amount_micros   = "ten million"
 8 │   delivery_method = "TURBO"
   ·                     ───────
 9 │ }
   ╰────

Short forms for repetitive resources

Real campaigns accumulate dozens of keywords and negatives. The bulk and list forms keep the file readable:

resource "google_ads_ad_group_criterion" "warszawa_phrases" {
  ad_group = google_ads_ad_group.warszawa.id
  status   = "ENABLED"

  keyword { text = "klimatyzacja Warszawa",          match_type = "PHRASE" }
  keyword { text = "montaż klimatyzacji Warszawa",   match_type = "PHRASE" }
  keyword { text = "klimatyzator inwerterowy Wwa",   match_type = "PHRASE" }
}

resource "google_ads_shared_set" "competitor_brands" {
  name   = "Klima — competitor brands"
  status = "ENABLED"

  negative_keyword { text = "samsung", match_type = "BROAD" }
  negative_keyword { text = "lg",      match_type = "BROAD" }
  negative_keyword { text = "daikin",  match_type = "BROAD" }
}

resource "google_ads_ad_group_ad" "warszawa_rsa" {
  ad_group = google_ads_ad_group.warszawa.id

  ad {
    final_urls = ["https://example.com/warszawa/"]

    responsive_search_ad {
      headlines = [
        { text = "Klimatyzacja Warszawa", pin = "HEADLINE_1" },
        "Cicha praca, niski prąd",
        { text = "Bezpłatna wycena", pin = "HEADLINE_3" },
      ]

      descriptions = [
        "Montaż klimatyzacji split i multi w Warszawie.",
        "Działamy w Warszawie i okolicach.",
      ]
    }
  }
}

A 1025-line one-resource-per-keyword campaign comes out at 177 lines in this form. See examples/bulk/main.bid.

Install

brew install chrmod/tap/bidsmith

macOS and Linux (x86_64 / aarch64) are supported. Windows works through WSL — install guide.

Get started

The full walkthrough — including how to connect to your Google Ads account — lives at chrmod.github.io/bidsmith.

The short version:

  1. Install bidsmith — about 2 minutes.
  2. Set up GitHub — about 5 minutes if you've never used Git.
  3. Connect to Google Ads — about 15 minutes, one-time.
  4. Run your first plan.

Multiple files in one folder

Each .bid file's name acts as a folder for its resources. Two files in the same directory can each declare google_ads_campaign_criterion.broad_wikipedia without colliding — they're addressed as nadarzyn.google_ads_campaign_criterion.broad_wikipedia and warszawa.google_ads_campaign_criterion.broad_wikipedia. See examples/multi/ for a worked two-file example.

This makes the one-campaign-per-file workflow practical: shared negatives that appear across every campaign live in different files and don't conflict.

How a change flows

your laptop                              GitHub                            Google Ads
─────────────────                        ──────────────                    ──────────────
.bid files (edited)  ─── git push ──►   .bid files (canon)
                                              │
                                              │  pull request
                                              ▼
                                         reviewed + merged
                                              │
                                              │
bidsmith plan        ◄── git pull ───   .bid files (canon)
bidsmith apply       ──────────────────────────────────────►   campaigns, ad groups,
                                                                keywords, budgets

The .bid files in GitHub are the source of truth. Your laptop is where you edit and where you run apply. Google Ads is where the result shows up.

State lives on Google Ads itself — managed resources carry a bidsmith:address=<…> label. There is no separate state file to back up or share.

Build from source

If you'd rather build the binary yourself instead of installing via Homebrew:

cargo build --release
./target/release/bidsmith --help

Requires Rust 1.89+. The release binary is around 1.3 MB.

License

MIT