● Internal Runbook · Search Indexing

FinSanad SEO & Google indexing runbook

Everything that powers getting FinSanad pages into Google: the service account, the Indexing-API scripts, how ownership was verified from the server, and the exact commands to index new pages or submit the sitemap. This is the operational reference — bookmark it.

Tooling: /home/waheed/FinSanadSEO/ Project: finsanad (957661740511) Status: live & working
← Docs home
Overview

1What's set up

A Google Cloud service account can notify Google to (re)crawl FinSanad URLs on demand, without anyone logging into the Search Console UI. It authenticates with a private key, proves it owns finsanad.com, and calls the Indexing API.

ItemValue
GCP projectfinsanad · 957661740511
Service accountfinsanadseo-bot@finsanad.iam.gserviceaccount.com
APIs enabledIndexing API · Search Console API · Site Verification API
Verified sitehttps://finsanad.com/ (URL-prefix, FILE method)
Toolkit folder/home/waheed/FinSanadSEO/ (not web-served)
Key fileservice-account.json (chmod 600)
The one-liner you'll use most: cd /home/waheed/FinSanadSEO && node google-index.js <url…> — pings Google to crawl those URLs. With no arguments it re-submits the six blog posts.
Reference

2The toolkit & files

Everything lives in /home/waheed/FinSanadSEO/. All scripts are self-contained Node (built-ins only — no npm install); each JWT-signs the key, gets a scoped token, and calls a Google REST endpoint.

google-index.jsSubmits URLs to the Indexing API (URL_UPDATED). Usage: no args = the 6 blog URLs; or pass URLs; or --file urls.txt; or --delete <url> to signal removal.
verify.jsSelf-verifies the service account as an owner of https://finsanad.com/ via the Site Verification API (FILE method) — writes a token file into the web root and confirms. Run once (already done).
submit-sitemap.jsSubmits the sitemap via the Search Console API. Note: only works if the SA is a Search Console property member — see §3. Kept for reference.
diag.jsLists the Search Console properties the SA can see and its permission level on each — the go-to when something 403s.
service-account.jsonThe private key. Secret, chmod 600, never web-served, never committed.
urls.txtThe 6 blog URLs, one per line — for google-index.js --file urls.txt.
README.mdThe from-scratch service-account setup guide (create project, enable APIs, key, grant access).
Do not delete the verification token file /home/waheed/FinSanadWeb/googleec82ccd7f70c3ab7.html (served at https://finsanad.com/google…​.html). Google re-checks it periodically; removing it revokes ownership and indexing calls start failing with 403.
The gotcha worth knowing

3How ownership works — two separate systems

Google has two independent permission systems, and this trips everyone up:

① Site-verification ownership

Proven via the Site Verification API (the token file). This is what the Indexing API checks. ✅ The service account has this — which is why google-index.js works.

② Search Console property membership

The list of users on a property in Search Console. This is what the Search Console API (sitemaps, analytics) checks. The SA is not a member, and Google has no API to add one.

Consequence: the service account can push URLs to the Indexing API forever, but it cannot submit the sitemap by API. The sitemap must be submitted by a human account that owns the property (the Search Console UI, or the owner's own Cloud Shell session) — see §5.
Routine task

4Index new or updated pages

Whenever you publish or materially change a page, nudge Google to recrawl it:

# one or more URLs
cd /home/waheed/FinSanadSEO
node google-index.js https://finsanad.com/blog/your-new-post/

# re-submit the standard 6 blog URLs (no args)
node google-index.js

# a batch from a file (one URL per line)
node google-index.js --file urls.txt

# tell Google a page was removed
node google-index.js --delete https://finsanad.com/old-page/

A successful run prints ✓ <url> per URL and Done. N submitted, 0 failed. Quota is 200 URLs/day — far above what this site needs.

Honest caveat: the Indexing API is officially for JobPosting / BroadcastEvent pages. For ordinary blog/marketing pages it usually still triggers a crawl, but Google doesn't guarantee it. Treat it as a fast nudge on top of the sitemap (§5) and normal crawling — not the only lever.
Reliable path

5Submit the sitemap

The sitemap (https://finsanad.com/sitemap.xml) is the durable way to get all pages — current and future — discovered. Because of §3 it can't be done by the service account; do it as the property owner.

Option A — Search Console UI (20 seconds)

In Search Console for finsanad.com: left sidebar → “Sitemaps” (under the Indexing group — not under Settings). Enter sitemap.xml → Submit.

Option B — Cloud Shell, as the owning account

# 1) add the Search Console scope to your gcloud session (approve the auth link)
gcloud auth login --update-adc \
  --scopes=https://www.googleapis.com/auth/webmasters,openid,https://www.googleapis.com/auth/userinfo.email

# 2) token + list YOUR properties (note the exact siteUrl for finsanad)
TOKEN=$(gcloud auth print-access-token)
curl -s -H "Authorization: Bearer $TOKEN" \
  https://www.googleapis.com/webmasters/v3/sites | python3 -m json.tool

Then run the line matching your property type:

# URL-prefix property:  "siteUrl": "https://finsanad.com/"
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
 "https://www.googleapis.com/webmasters/v3/sites/https%3A%2F%2Ffinsanad.com%2F/sitemaps/https%3A%2F%2Ffinsanad.com%2Fsitemap.xml" \
 -w "HTTP %{http_code}\n"

# Domain property:  "siteUrl": "sc-domain:finsanad.com"
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
 "https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Afinsanad.com/sitemaps/https%3A%2F%2Ffinsanad.com%2Fsitemap.xml" \
 -w "HTTP %{http_code}\n"

HTTP 200 (or 204) means the sitemap is submitted.

When something breaks

6Diagnose problems

SymptomMeaningFix
403 "Failed to verify the URL ownership"SA lost site-verification ownershipRestore the token file, then re-run verify.js
403 SERVICE_DISABLEDAn API got disabled on the projectEnable it at the activation URL in the error
403 "insufficient permission for site"SA isn't a Search Console property member (sitemap calls)Expected — use §5 Option A/B instead
getMetadata → 404Normal right after submitting — Google hasn't crawled yetNone; check back later

Run node diag.js to see what the SA can actually access.

Where things stand

7Current status

✅Service account created, key on server (chmod 600), 3 APIs enabled.
✅SA self-verified as owner of https://finsanad.com/; token file live (HTTP 200).
✅All 6 blog URLs submitted to the Indexing API (6 submitted, 0 failed).
✅sitemap.xml live with all 6 blog URLs; robots.txt points to it.
⬜Submit sitemap.xml in Search Console once (§5) — the only step needing an owner account.
⬜Optional: rotate the service-account key (§8), since it was pasted in plaintext during setup.
Housekeeping

8Security & maintenance

  • Keep the key secret. service-account.json is a private key — chmod 600, outside any web root, never in git.
  • Rotate if exposed. Cloud Console → the service account → Keys → delete the old key → create a new JSON → replace service-account.json. Ownership and APIs are unaffected.
  • Leave the token file in place. Deleting googleec82ccd7f70c3ab7.html revokes ownership.
  • Least privilege. The SA only needs the Indexing / Site Verification / Search Console APIs — no IAM roles on the project are required for these calls.
Related: Search Demand Report (what to write) · Organic Growth Playbook (how to distribute) · the blog (what's published).

Internal SEO/indexing runbook · FinSanad · keep alongside the toolkit in /home/waheed/FinSanadSEO/.