On this page
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.
| Item | Value |
|---|---|
| GCP project | finsanad · 957661740511 |
| Service account | finsanadseo-bot@finsanad.iam.gserviceaccount.com |
| APIs enabled | Indexing API · Search Console API · Site Verification API |
| Verified site | https://finsanad.com/ (URL-prefix, FILE method) |
| Toolkit folder | /home/waheed/FinSanadSEO/ (not web-served) |
| Key file | service-account.json (chmod 600) |
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.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.
URL_UPDATED). Usage: no args = the 6 blog URLs; or pass URLs; or --file urls.txt; or --delete <url> to signal removal.https://finsanad.com/ via the Site Verification API (FILE method) — writes a token file into the web root and confirms. Run once (already done).chmod 600, never web-served, never committed.google-index.js --file urls.txt./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.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.
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.
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.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.
6Diagnose problems
| Symptom | Meaning | Fix |
|---|---|---|
| 403 "Failed to verify the URL ownership" | SA lost site-verification ownership | Restore the token file, then re-run verify.js |
| 403 SERVICE_DISABLED | An API got disabled on the project | Enable 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 → 404 | Normal right after submitting — Google hasn't crawled yet | None; check back later |
Run node diag.js to see what the SA can actually access.
7Current status
chmod 600), 3 APIs enabled.https://finsanad.com/; token file live (HTTP 200).6 submitted, 0 failed).sitemap.xml live with all 6 blog URLs; robots.txt points to it.sitemap.xml in Search Console once (§5) — the only step needing an owner account.8Security & maintenance
- Keep the key secret.
service-account.jsonis 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.htmlrevokes 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.
Internal SEO/indexing runbook · FinSanad · keep alongside the toolkit in /home/waheed/FinSanadSEO/.