A GTM audit checks that each tag in your Google Tag Manager container fires once, on the right event, with a deliberate consent setting, and that you know who can publish. The nine checks below cover duplicate conversion tags, Custom HTML on all pages, hard-coded IDs, click triggers, consent, unused tags and variables, version notes and user access, and four of them end in a rule a script can enforce.
Start here
What Google already flags
Check 1: Does Tag Diagnostics flag a missing tag?
Conversion tags
Check 2: Does each conversion action have one tag?
Check 3: Does the conversion fire only on a confirmed submit?
Check 4: Are IDs variables, not typed values?
Everything else that runs
Check 5: Does any Custom HTML run on every page?
Check 6: Does every tag have a consent decision?
Check 7: Is anything left that nobody uses?
Who changes the container
Check 8: Does each version say what changed?
Check 9: Do the right people hold Administrator and Publish?
Turn each review note into a rule
Lauren Tan, an engineer at Cursor, said in a recorded talk on trusting coding agents that the worst place to be is enforcing a codebase's rules by hand in code review. Instead of writing such a comment, she says to ask "how do I turn this into a hard rule": a lint or a CI failure. She layers rules and skills on top but does not rely on them alone, since agents can forget them. A container review note such as "this tag has no consent setting" returns next quarter unless something fails when it does.
An exported container is JSON that Google says can be compared and stored in a version control system to review changes before publishing. The script below reads one and exits with an error on four of the notes above. It uses only fields from Google's Tag and Parameter references, and the tag ID shapes follow Google's tag ID examples.
import json, re, sys
from collections import defaultdict
data = json.load(open(sys.argv[1], encoding="utf-8"))
version = data.get("containerVersion", data)
errors, same, ids = [], defaultdict(list), defaultdict(set)
def values(params):
for p in params or []:
if isinstance(p.get("value"), str):
yield p["value"]
yield from values(p.get("list"))
yield from values(p.get("map"))
for tag in version.get("tag", []):
name = tag["name"]
if tag.get("paused"):
continue
if not tag.get("firingTriggerId"):
errors.append(f"{name}: no firing trigger, it never fires")
status = tag.get("consentSettings", {}).get("consentStatus", "notSet")
if status.replace("_", "").lower() == "notset":
errors.append(f"{name}: consent not configured")
params = tag.get("parameter", [])
same[(tag["type"], json.dumps(params, sort_keys=True))].append(name)
for v in values(params):
if re.fullmatch(r"(AW|G|GT)-[A-Z0-9]+|\d{6,}", v):
ids[v].add(name)
for names in same.values():
if len(names) > 1:
errors.append("same type and settings: " + ", ".join(names))
for value, names in ids.items():
if len(names) > 1:
errors.append(f"{value} typed into {len(names)} tags: use a Constant variable")
print("\n".join(errors) or "0 errors")
sys.exit(1 if errors else 0)Save it as gtm_rules.py and run it on each export before you publish. It catches exact copies (check 2), an ID typed into more than one tag (check 4), unset consent (check 6) and the trigger half of check 7. Click triggers, Custom HTML scope and version notes still need a person until each gets a line of its own.
I run this blog the same way. Rules for my writing agents, such as no em dashes, no links to removed pages and no invented frequency claims, now fail a lint script. I approve every commit myself, and no agent pushes on its own.
After the audit
A clean container sends each lead to Google Ads once, on a real submit, with consent decided. Which leads became a booked appointment or a sale is in your CRM or phone system, and getting that back into Google Ads bidding is the closed-loop measurement I set up.
Tags
Frequently asked questions
Does Google have a free GTM audit tool?
Partly. Tag Diagnostics, built into your Google tag settings, flags a missing conversion linker, missing Google tags, pages that never loaded the tag and an account with only one administrator. It does not review triggers, Custom HTML or consent settings tag by tag, so checks 2 to 8 still need a person or a script.
Can I audit a Google Tag Manager container with Read access?
For the review, yes. Read lets a user browse the tags, triggers and variables in a container without the ability to change anything. Fixing what you find needs Edit, creating a version needs Approve, and publishing it needs Publish.
Should I pause or delete unused tags in Google Tag Manager?
Pause a tag you will need again, such as one for a timed campaign, and delete the rest. Do not keep a tag off with a trigger exception: Google's tag best practices note that pausing or removing a tag takes its code out of the container, while blocking does not. Either change takes effect only after you save and publish.
How do I find unused variables in GTM?
Export the container under Admin > Export Container and search the file for each user-defined variable's name in double braces, the form tags, triggers and other variables use to reference it. A name that never appears that way is unused, and you can delete it with the bulk actions on the Variables table.