Got a New Domain? The Day One Search Console Playbook
So you just spun up a new site. The domain is bought, the homepage loads, and the obvious next question is how to get Google to actually notice it. The answer is Google Search Console. Until your domain is verified there, you are flying blind. That is where Google shows you how you are performing in search, what pages are indexed, and whether anything is broken. Without it, the site could be invisible to Google and you would have no way of knowing.
This post walks through the whole thing from nothing. If you are brand new to the terminal, start with Your First Day With Claude Code to get Python and a proper setup in place first. Come back here when you can open Terminal and run a command without panicking.
The Old Way
Adding a single domain to Search Console through the web interface looks like this:
- Log into Search Console
- Click Add Property
- Pick Domain (not URL prefix, because Domain covers every subdomain and every protocol)
- Google hands you a DNS TXT record to prove ownership
- Open a new tab, log into Cloudflare
- Find the zone
- Add the TXT record
- Switch back to Search Console
- Click Verify
- Hope DNS has propagated. If not, wait, refresh, try again
It is not a disaster. It is eight or nine tab switches, two dashboards, and a string of characters you have to babysit. For a single domain, five minutes. If you manage a dozen client sites, it adds up fast.
The New Way
What I have been doing instead is running one command from the terminal:
python3 add-domain.py example.com
That is the whole thing. The script hits Google's Site Verification API, asks for a TXT token, drops that token into Cloudflare through the Cloudflare API, waits for propagation, verifies, and adds the site to Search Console as an owner. I watch it happen in the terminal and move on with my day.
The real prereq sits above everything else on this list: your domain needs to live on a Cloudflare account you control. The script drops the verification TXT record into Cloudflare via the API, so if your DNS is anywhere else, the whole pipeline falls apart at Step 6.
There are two paths, and one of them is massively easier.
The recommended path: buy the domain through Cloudflare Registrar in the first place. This is the move I make for every new site now. Cloudflare sells domains at wholesale cost (no markup, no "first year free then we triple it" nonsense), and the moment you complete the purchase, the zone is already in your Cloudflare account, nameservers already pointed, status already Active. There is nothing to migrate, nothing to wait for. You buy it at 9am and by 9:01 you are running add-domain.py on it.
To do that, go to your Cloudflare dashboard, click Domain Registration, Register Domains, search for the name you want, and check out. You are done before you finish this sentence.
The fallback path: your domain is already somewhere else. If you bought it on GoDaddy, Namecheap, or a registrar that is not Cloudflare, you have two options. You can transfer the domain to Cloudflare Registrar (cheaper at renewal, same wholesale pricing as above), or you can leave it with the current registrar and just change the nameservers to the pair Cloudflare gives you. Either way, add the domain at cloudflare.com, follow the on-screen nameserver instructions, and wait until Cloudflare shows the zone as Active before you keep going. Changing nameservers can take anywhere from a few minutes to a few hours depending on the old registrar.
Once the zone is Active in your Cloudflare dashboard, you are ready for Step 2.
Step 2: Grab Your Cloudflare Global API Key
Log into Cloudflare. Click your profile icon in the top right, then My Profile, then the API Tokens tab, then find Global API Key and click View. Copy it somewhere safe along with the email address on your Cloudflare account.
You need the Global Key, not a scoped token, because the script needs to reach every zone you might want to add later.
Step 3: Create a Google Cloud Project and Enable Two APIs
You need a Google Cloud project with two APIs turned on. If you do not already have a project, create one at console.cloud.google.com. Name it something like "domain automation" and pick it from the project dropdown at the top.
Then go to the API Library, search for each of these, and click Enable on both:
- Google Search Console API (sometimes called Web Search or Webmasters API)
- Google Site Verification API
About 30 seconds per API. If you skip either one, the script will fail at a different spot and the error will not tell you which API is the problem, so double-check both are green.
Step 4: Create OAuth Credentials
In the same Google Cloud project, go to APIs and Services, then Credentials, then Create Credentials, then OAuth client ID.
If it asks you to configure the OAuth consent screen first, do this:
- Pick User Type: External
- Fill in the app name (anything, for example "Domain Automation"), your email for user support, and your email again for the developer contact
- Click Save and Continue through the Scopes page (leave it empty, the script asks at runtime)
- On the Test Users page, click Add Users and add the Google account you will actually use to sign in. This is the gotcha that trips most people up. If you do not add yourself as a test user while the app is in Testing status, Google will block sign-in with a scary "access blocked" error. Add yourself now and save.
Back on the Credentials screen, click Create Credentials again, pick OAuth client ID, choose Desktop app as the application type, give it a name, and click Create. Google shows you a dialog with a Download JSON button. Download the file and save it somewhere you will remember. I put mine in the same folder as the script and renamed it to client_secrets.json.
The scopes the script needs are:
- auth/webmasters for adding sites to Search Console
- auth/siteverification for requesting DNS verification tokens
You do not pick these in the console. The script requests them at first run, and the consent screen will list them when you authorize.
Step 5: Install the Python Libraries
Open your terminal and run:
pip3 install google-auth google-auth-oauthlib google-api-python-client requests
That installs the four libraries the script uses. If pip3 is not recognized, you do not have Python installed yet. Install it from python.org (pick the latest Python 3) or walk through Your First Day With Claude Code which covers the whole setup.
Step 6: Save the Script
Create a new folder anywhere on your machine, for example ~/apps/domain-automation. Drop the client_secrets.json from Step 4 into that folder. Then save the script below as add-domain.py in the same folder.
Before you run it, open the file in a text editor and fill in the two config values at the top: your Cloudflare email and your Cloudflare Global API Key.
Click to expand the full Python script
cat <<'PYTHON' > add-domain.py
#!/usr/bin/env python3
"""Add a single domain to Google Search Console end-to-end."""
import json, os, subprocess, sys, time
import requests
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
# =========================================================
# CONFIG: fill these in before running
# =========================================================
CF_EMAIL = "your-cloudflare-email@example.com"
CF_KEY = "your-cloudflare-global-api-key"
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
CLIENT_SECRETS_FILE = os.path.join(SCRIPT_DIR, "client_secrets.json")
TOKEN_FILE = os.path.join(SCRIPT_DIR, "token.json")
SCOPES = [
"https://www.googleapis.com/auth/webmasters",
"https://www.googleapis.com/auth/siteverification",
]
CF_HEADERS = {
"X-Auth-Email": CF_EMAIL,
"X-Auth-Key": CF_KEY,
"Content-Type": "application/json",
}
def get_creds():
creds = None
if os.path.exists(TOKEN_FILE):
creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)
creds = flow.run_local_server(port=0)
with open(TOKEN_FILE, "w") as f:
f.write(creds.to_json())
return creds
def main():
if len(sys.argv) != 2:
print("Usage: python3 add-domain.py <domain>")
sys.exit(1)
domain = sys.argv[1].strip().lower().lstrip("www.")
creds = get_creds()
sv = build("siteVerification", "v1", credentials=creds)
wm = build("searchconsole", "v1", credentials=creds)
print(f"[google] requesting verification token for {domain}...")
tresp = sv.webResource().getToken(body={
"site": {"type": "INET_DOMAIN", "identifier": domain},
"verificationMethod": "DNS_TXT",
}).execute()
token_value = tresp["token"]
print(f"[google] token: {token_value}")
print(f"[cloudflare] looking up zone {domain}...")
z = requests.get(
f"https://api.cloudflare.com/client/v4/zones?name={domain}",
headers=CF_HEADERS,
).json()
if not z.get("result"):
print(f"[cloudflare] ERROR: zone {domain} not found")
sys.exit(1)
zone_id = z["result"][0]["id"]
print(f"[cloudflare] zone_id={zone_id}")
print("[cloudflare] adding TXT record...")
rec = requests.post(
f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records",
headers=CF_HEADERS,
json={"type": "TXT", "name": domain, "content": token_value, "ttl": 60},
).json()
if not rec.get("success"):
print("[cloudflare] FAILED:", json.dumps(rec, indent=2))
sys.exit(1)
print(f"[cloudflare] TXT record id={rec['result']['id']}")
print("[dns] waiting 20s for propagation...")
time.sleep(20)
for i in range(6):
dig = subprocess.run(
["dig", "+short", "TXT", domain, "@1.1.1.1"],
capture_output=True, text=True,
)
if token_value in dig.stdout:
print(f"[dns] visible after {20 + i * 10}s")
break
time.sleep(10)
print("[google] submitting verification...")
ins = sv.webResource().insert(
verificationMethod="DNS_TXT",
body={"site": {"type": "INET_DOMAIN", "identifier": domain}},
).execute()
print(f"[google] verified: owners={ins.get('owners')}")
print("[gsc] adding site to Search Console...")
try:
wm.sites().add(siteUrl=f"sc-domain:{domain}").execute()
except Exception as e:
print(f"[gsc] add error (may already exist): {e}")
sites = wm.sites().list().execute().get("siteEntry", [])
for s in sites:
if domain in s["siteUrl"]:
print(f"[gsc] CONFIRMED: {s['siteUrl']} permission={s.get('permissionLevel')}")
print("\nDONE")
if __name__ == "__main__":
main()
PYTHON
Step 7: First Run and OAuth Consent
The first time you run the script, Google needs your permission to read and write Search Console data on your behalf. Run the command with any domain you own on Cloudflare:
python3 add-domain.py example.com
Your browser opens automatically with a Google sign-in page. Pick the Google account that should own the Search Console property, click through the warnings about an unverified app (this is your own Cloud project, it is safe), grant the two scopes (webmasters and siteverification), and close the tab when it says authorization complete.
The script saves your approval to token.json next to the script. You will not be asked to sign in again until the token expires, and the script refreshes it automatically when it can.
From here on out, every new domain you want to add is one line:
python3 add-domain.py your-new-domain.com
About 30 seconds and you are done. No tabs, no copy-paste, no waiting for propagation.
Step 8: Confirm It Worked in Search Console
Open search.google.com/search-console in your browser, sign in with the Google account you used for the OAuth consent, and click the property dropdown in the top left. Your new domain is in there as a Domain property. Click it. The overview page looks empty for now because Google has not crawled anything yet, but that is normal for day one.
Step 9: Submit Your Sitemap
While you are in Search Console, give Google a head start by submitting your sitemap. In the left sidebar, click Sitemaps, type sitemap.xml in the box (or whatever path your site uses, for example sitemap_index.xml on WordPress), and click Submit. Google starts crawling within hours instead of waiting to stumble onto your site.
If you do not have a sitemap yet, that is your next task. Most static site generators produce one automatically. WordPress and most other CMS platforms have plugins for it.
Troubleshooting
A few things that trip people up on the first run:
"Access blocked: this app has not been verified." You forgot to add yourself as a test user on the OAuth consent screen. Go back to APIs and Services, OAuth consent screen, Test Users, and add the Google account you are signing in with. Then run the script again.
"FileNotFoundError: client_secrets.json" The script cannot find the credentials file. Make sure you downloaded the JSON from Step 4, renamed it to client_secrets.json, and put it in the same folder as add-domain.py.
"zone yourdomain.com not found" Your domain is not on Cloudflare yet, or it is on a Cloudflare account that does not match the API key in the config. Double-check that the zone shows as Active in your Cloudflare dashboard and that the Global API Key you copied belongs to the same account.
"TXT not visible yet, attempting verify anyway" followed by a Google error. DNS did not propagate fast enough. Re-run the command with the same domain. The script is safe to run twice, the second run uses the existing TXT record and just verifies.
pip3: command not found. Python 3 is not installed, or it is installed but not on your PATH. Install Python 3 from python.org and restart your terminal.
The first-run browser tab never opens. You are running the script over SSH on a machine without a browser. Run it on your local machine for the first-time authorization, then copy the generated token.json to the remote machine. After that, the remote machine can refresh the token on its own.
What Happens Under the Hood
For the curious, here is what the script does on every run:
- Load your Google OAuth token and refresh it if it has expired
- Ask the Site Verification API for a fresh TXT token scoped to the domain
- Look up the zone ID in Cloudflare so it knows where to write
- Create a TXT record at the root of the zone with a 60 second TTL
- Wait 20 seconds, then use dig to confirm the record is visible on the public internet
- Tell Google to verify. Google checks the TXT, confirms it matches, and marks you as owner
- Add the property to Search Console as a Domain property, which covers every subdomain
- Print the confirmation line so you know the site is live in Search Console
None of those steps need a human. The whole run finishes in about 30 seconds.
When You Have a Pile of Domains Instead of One
If you are reading this and thinking, "I have a dozen orphaned domains I never added to Search Console," there is a bulk version of this same approach. Sixteen Domains Added in Two Minutes walks through the multi-domain flow for cleaning up a year of drift in one afternoon.
Need help setting this up? Create a ticket on MyTechSupport.com. Vetted specialists can walk you through the prereqs and the first run.
Step 10: Teach Claude Code to Run It for You
Here is where this gets really fun. Once the script works, you can wire it up as a Claude Code skill so you do not even have to remember the command. Next time you want to add a domain, you just say it out loud.
A skill is a markdown file Claude Code loads at startup. You put it in your Claude commands folder (for example ~/.claude/commands/add-to-gsc.md) and write the trigger phrases plus the steps Claude should take. Mine looks something like this:
cat <<'SKILL' > ~/.claude/commands/add-to-gsc.md
Add a new domain to Google Search Console end-to-end, no clicks.
## When to invoke
Auto-invoke when the user says any of:
- "add X to google search console"
- "add X to GSC"
- "get X into search console"
- "verify X in search console"
## Steps
1. Extract the domain from the user's request
2. Run: python3 ~/apps/domain-automation/add-domain.py DOMAIN
3. Confirm the result and offer to open the property in Chrome
SKILL
Then in your global CLAUDE.md (the file that governs behavior across all your projects), add one line to the auto-invoke section mapping natural phrases to the skill. Something like:
Says "add X to google search console", "add X to GSC", "verify X in search console" → /add-to-gsc
Restart Claude Code and try it. You say "help me add newdomain.com to google search console" in plain English, and Claude runs the script for you. No command to remember. No folder to find. No prereqs to re-explain. The first time you do this you will smile, because you just crossed the line from automation to something closer to having an assistant that already knows your systems.
This is the real reason to build these scripts. The first win is "a task that took ten minutes now takes thirty seconds." The second, bigger win is "a task I used to have to think about now happens when I mention it." Skills are where Claude Code stops being a terminal tool and starts feeling like muscle memory.
The Bigger Picture
This is the pattern I keep coming back to with Claude Code. A task that should feel boring and clicky, like bouncing between two SaaS dashboards with a sticky note full of verification tokens, turns into a single command you can forget about. The real win is not saving the five minutes once. It is that every future domain you spin up is one line away from search data, and you never think about the process again.
If you have never used Claude Code before, Your First Day With Claude Code walks through the full setup from scratch, no coding background required.
See Also
- The 100 Step New Site Checklist for the full day-one-to-maintenance list this post fits inside (this playbook is step 31)
- Sixteen Domains Added in Two Minutes for the bulk version of this playbook
- Claude Code for the terminal agent that wrote the script and can write yours
- Cloudflare for why it is the best DNS host for workflows like this one
- Google Search Console for what Search Console is and why every domain you own should be in it
- Your First Day With Claude Code for installing Claude Code from scratch if this is your first time
See Also
Some links in this article are affiliate links. If you purchase through them, we may earn a commission at no extra cost to you. This helps support our content.
This article blends original content, AI-assisted drafting, and human oversight. How I write.
Stay Updated
Get notified when new content is published.
No spam. Unsubscribe anytime.