Google Indexing API + Python OAuth2: Part 19 — WordsByEkta🌿

I Wrote 105 Blog Posts. Google Indexed None of Them. Here Is What Finally Worked — Google Indexing API for Blogger Using Python

Estimated Reading Time: 12 minutes

I started this blog in July 2025. I wrote consistently. I fixed my HTML. I submitted my sitemap. I requested indexing manually in Google Search Console — one post at a time, ten per day, the maximum allowed.

A year later, I did a site:wordsbyektaa.blogspot.com search on Google. The only result was a Google support thread from last year — where I had asked why my AdSense was rejected. Not a single blog post. One hundred and five posts, written across twelve months. Zero indexed by Google.

This is the honest story of everything I tried, everything that failed, and the one method that finally worked — explained so clearly that even if you have never opened a terminal window in your life, you can follow this.

Quick Answer

Yes — using the Google Indexing API with Python and OAuth2 authentication, you can submit all your Blogger posts to Google's Indexing API as a recrawl notification in one run. The setup takes about 30 minutes. After that, one command does everything. This guide covers every step — including every error I hit and exactly how I fixed it.

Important: Google officially says the Indexing API is intended for JobPosting and livestream pages only. I am sharing this as my personal Blogger experiment because the API accepted my URLs through OAuth2. This does not guarantee indexing, ranking, or long-term support. Treat this as an experiment, not a guaranteed fix.
My result: The script returned 105 accepted submissions and 0 failed requests. I am tracking Search Console separately to see which URLs Google eventually crawls or indexes.
A cinematic night-scene editorial illustration of a lone figure at a glowing control desk launching blog post URLs as signal beams into a starry sky, representing the Google Indexing API submission process for Blogger using Python OAuth2 — WordsByEkta
105 posts. Zero indexed. Until one Python script changed everything.

What I Tried That Failed — And Why

Before I tell you what worked, I want to tell you what did not — because every tutorial I found made it sound simple, and none of them mentioned any of this.

Method What Happened Why It Failed
Manual GSC URL Inspection 10 requests per day maximum 105 posts would take 10+ days, and Google still decides when to actually crawl
Sitemap Submission Submitted — status Success Google discovered posts but marked them "Discovered but not indexed" — 72 of them
IndexNow (Bing) Failed silently Blogger cannot host the key file required at domain root — covered in Part 13
Service Account for Google Indexing API ❌ 403 Permission Denied GSC does not accept service account emails as verified owners for Blogger
HTML Unique ID Verification ❌ Failed Adding service account unique ID to Blogger theme head tag does not grant GSC ownership
OAuth2 Google Indexing API ✅ 105/105 API submissions accepted Runs as your personal Google account — GSC ownership recognised instantly
About the Service Account vs OAuth2 debate: Many tutorials — and AI tools — will tell you to use a Service Account. They are not wrong in theory. Service Accounts work for Google Workspace setups with domain-wide delegation. But for a personal Blogger blog, GSC simply does not accept service account emails as verified owners. I spent hours on this before switching to OAuth2, which worked immediately.

What Is the Google Indexing API — And What Does It Actually Do?

The Google Indexing API is a tool Google built for websites to notify Google when content is published or updated. Officially, Google says it is meant for job postings and livestream pages. But in practice, many bloggers experiment with it for regular blog posts. In my case, the API accepted the submissions, but that does not mean Google officially supports this use for Blogger.

What it does: it sends Google a URL_UPDATED notification. According to Google, a successful HTTP 200 response means Google may try to recrawl the URL soon. It does not confirm indexing, ranking, or guaranteed crawling. This does not guarantee instant indexing — Google still decides whether to index based on content quality. For me, it at least moved the process from passive waiting to sending Google a direct update notification. For posts stuck in "Discovered but not indexed" for months, this is exactly the nudge they need.

What it does not do: it does not force Google to index anything. It is a signal, not a command.

What You Need Before Starting

  • A Google account that is the verified owner of your blog in Google Search Console
  • A Windows, Mac, or Linux computer
  • Python installed (version 3.7 or above — download from python.org if you don't have it)
  • About 30 minutes
  • No prior coding experience needed — every command is given exactly as you need to type it
⚠️ Important: The Google account you use for this setup must be the same account that is the verified owner of your blog in Google Search Console. If you have a different account managing GSC, use that one — not a secondary account.

Phase 1: Google Cloud Console Setup

Step 1: Create a Project

  1. Go to console.cloud.google.com
  2. Sign in with your Google account — the same one verified in GSC
  3. Click the project dropdown at the top → click New Project
  4. Name it anything — for example Blogger Indexing
  5. Click Create
Note: Google Cloud Console offers a free trial with $300 credit. You do not need to use any of this credit for what we are doing. The Indexing API is free. Just dismiss the free trial banner if it appears.

Phase 2: Enable the Right API

🚫 This is where most people make a mistake. There are two different APIs that sound similar. You need the correct one or nothing will work.
API Name Use This?
Web Search Indexing API ✅ YES — this is the correct one
Google Search Console API ❌ NO — this is a different API for reading GSC data

How to enable it:

  1. In Google Cloud Console, go to APIs and Services → Library
  2. Search for Web Search Indexing API
  3. Click on it → click Enable
  4. Wait for it to activate — takes about 30 seconds

Phase 3: Create OAuth2 Credentials

OAuth2 credentials are what allow your Python script to run as you — using your personal Google account. This is why it works when the service account did not: Google Search Console recognises you as the verified owner immediately.

Step 1: Configure the Consent Screen

  1. Go to APIs and Services → OAuth Consent Screen (or Google Auth Platform → Audience)
  2. Select External → click Create
  3. Fill in:
    • App name: Blogger Indexing (anything)
    • User support email: your Gmail
    • Developer contact email: your Gmail
  4. Click Save and Continue through all remaining screens
  5. On the Test Users screen — click Add Users → add your Gmail → Save
Why add yourself as a test user? Because the app is in testing mode. If you skip this step, Google will show an "Access Blocked" error when you try to log in. Adding your own Gmail as a test user fixes this immediately.

Step 2: Create the OAuth2 Client

  1. Go to APIs and Services → Credentials (or Google Auth Platform → Clients)
  2. Click Create Credentials → OAuth 2.0 Client ID
  3. Application type: Desktop App
  4. Name: anything — for example BloggerIndexing
  5. Click Create
  6. A popup appears — click Download JSON
  7. Save this file as client_secrets.json in a folder on your computer
✅ What you now have: A file called client_secrets.json saved on your computer. This is your key. Keep it safe — do not share it or post it publicly.

Phase 4: Install Python and the Required Libraries

Check if Python is installed:

Before checking Python, open the terminal inside your project folder:

Beginner-friendly way to open the terminal in your project folder:

First, create one folder for this setup. You can name it Blogger Indexing. Save your client_secrets.json file inside this folder. Later, you will also save OAuth.py in the same folder.

Windows: Open that folder. Right-click on an empty blank area inside the folder, then click Open in Terminal or Open PowerShell window here.

Mac: Open that folder. Right-click the folder, choose Services, then click New Terminal at Folder. If you do not see this option, open Terminal manually, type cd , drag the folder into the Terminal window, and press Enter.

Once the terminal opens in that folder, type:

python --version

If you see a version number like Python 3.10.6 — you are good. If you get an error, download Python from python.org and install it before continuing.

Install the required libraries:

In the same terminal window, paste this command and press Enter:

pip install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client requests

Wait for it to finish. You will see text scrolling — this is normal. When it stops and you see your cursor again, it is done.

Phase 5: The Python Script

Create a new file in the same folder as your client_secrets.json. Name it OAuth.py. Paste the following script into it exactly as shown:

import pickle
import os
import requests
from xml.etree import ElementTree
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
import time

SCOPES = ['https://www.googleapis.com/auth/indexing']
CLIENT_SECRETS_FILE = 'client_secrets.json'
TOKEN_FILE = 'token.pickle'

# Add all your blog URLs here
BLOGS = [
    "https://yourblogname.blogspot.com",
    # Add more blogs below — one per line
    # "https://yoursecondblog.blogspot.com",
]

def get_credentials():
    creds = None
    if os.path.exists(TOKEN_FILE):
        with open(TOKEN_FILE, 'rb') as f:
            creds = pickle.load(f)
    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, 'wb') as f:
            pickle.dump(creds, f)
    return creds

def get_all_post_urls():
    all_urls = []
    for blog in BLOGS:
        start = 1
        print(f"Fetching posts from {blog}...")
        blog_urls = []
        while True:
            feed_url = f"{blog}/feeds/posts/default?start-index={start}&max-results=25&alt=rss"
            try:
                response = requests.get(feed_url, timeout=10)
                root = ElementTree.fromstring(response.content)
                items = root.findall('./channel/item/link')
                if not items:
                    break
                for item in items:
                    blog_urls.append(item.text)
                if len(items) < 25:
                    break
                start += 25
                time.sleep(1)
            except Exception as e:
                print(f"Could not fetch {blog}: {e}")
                break
        print(f"Found {len(blog_urls)} posts")
        all_urls.extend(blog_urls)
    print(f"\nTotal posts across all blogs: {len(all_urls)}\n")
    return all_urls

def index_url(service, url):
    body = {"url": url, "type": "URL_UPDATED"}
    return service.urlNotifications().publish(body=body).execute()

# Main
creds = get_credentials()
service = build('indexing', 'v3', credentials=creds)
urls = get_all_post_urls()

print(f"Submitting {len(urls)} URLs to Google...\n")
success, failed = 0, 0

for i, url in enumerate(urls, 1):
    try:
        index_url(service, url)
        print(f"[{i}/{len(urls)}] OK: {url}")
        success += 1
        time.sleep(1)
    except Exception as e:
        print(f"[{i}/{len(urls)}] FAILED: {url} - {e}")
        failed += 1

print(f"\nDone! Success: {success} | Failed: {failed}")

Copy note: If Blogger changes < into text while copying, replace &lt; with the normal less-than sign: <.

What to change in this script: Only one thing — replace https://yourblogname.blogspot.com with your actual blog URL. If you have multiple blogs, add each one on a new line inside the BLOGS list, following the same format. Everything else stays exactly as shown.
Note: You do not need to find your RSS feed manually — and you do not need to understand what an RSS feed is. Just add your main Blogger URL in the BLOGS list. The script automatically builds the RSS feed URL and uses pagination to fetch more than the latest 25 posts. If your blog has under 200 posts, it should usually collect them in one run.

Phase 6: Run It — First Time Login

If your terminal is still open in the same project folder, where you saved client_secrets.json and OAuth.py, simply paste this command and press Enter:

python OAuth.py

The first time you run this, a browser window will open automatically. This is Google asking you to confirm that you allow the script to access your account. Log in with the same Gmail that is verified in GSC and click Allow.

You will see a message: "The authentication flow has completed. You may close this window."

Go back to your terminal. You will see the script running — fetching your posts and submitting them one by one:

[1/105] OK: https://yourblog.blogspot.com/2026/04/your-post.html [2/105] OK: https://yourblog.blogspot.com/2026/04/another-post.html ... Done! Accepted: 105 | Failed: 0
✅ What just happened: All your posts were submitted as URL_UPDATED notifications through Google's Indexing API. This means the API accepted the requests — Google may choose to recrawl and evaluate them. A file called token.pickle was also saved in your folder. This stores your login so you never need to go through the browser step again.

Phase 7: Running It Again for New Posts

Every time you publish new posts, open the same folder, right-click on a blank area, choose Open in Terminal, then paste this command and press Enter:

python OAuth.py

No browser login needed. The script reads your token.pickle file and authenticates silently. It fetches all current posts from your RSS feed automatically and submits them. You do not need to update any URL list manually.

What about the token expiring? The script automatically refreshes the token when needed. You will not be asked to log in again unless you manually delete the token.pickle file or revoke access in your Google account settings.

Every Error I Hit and How I Fixed It

Error 1: 403 Permission Denied

HttpError 403 — Permission denied. Failed to verify the URL ownership.

What it means: The account running the script is not the verified owner of the blog in GSC.

Fix: Make sure you log in with the exact Gmail account that is the verified owner in Google Search Console. If you used a service account before, switch to OAuth2 as shown in this guide.

Error 2: Access Blocked During Browser Login

Access blocked: Blogger Indexing has not completed the Google verification process.

What it means: Your Gmail was not added as a test user in the consent screen.

Fix: Go to Google Cloud Console → Google Auth Platform → Audience → Test Users → Add your Gmail. Then run the script again.

Error 3: Module Not Found

ModuleNotFoundError: No module named 'google'

What it means: The Python libraries were not installed correctly.

Fix: Run the pip install command from Phase 4 again. Make sure you are using the same Python version where you ran the install.

Error 4: File Not Found

FileNotFoundError: client_secrets.json not found

What it means: The script cannot find your credentials file.

Fix: Make sure client_secrets.json is in the exact same folder as OAuth.py. Check the filename — it must be exactly client_secrets.json with no extra words.

Daily Limits and What They Mean for You

Limit Details
Daily request limit 200 URLs per day per Google Cloud project
What happens if exceeded Script will show errors for remaining URLs — simply run again the next day
Does submitting the same URL twice cause problems? No — Google handles duplicate submissions gracefully
Does this guarantee indexing? No — Google still decides whether to crawl, index, or rank the page. The API only confirms that the notification was accepted.
If you have more than 200 posts across multiple blogs: Run the script for one blog per day, or split your BLOGS list and run on alternate days. The 200 limit resets every 24 hours.

Frequently Asked Questions

Why did the service account method fail?
Google Search Console does not accept service account emails as verified owners for personal Blogger blogs. OAuth2 works because it runs as your personal Google account — which is already the verified owner in GSC.

Is this officially supported by Google for blogs?
Officially, Google says this API is for job postings and livestream pages. Some bloggers experiment with it, but Google does not officially support the Indexing API for regular blog posts. Use it with that limitation in mind.

Will my posts definitely get indexed after this?
Not guaranteed. In my case, this helped get my URLs accepted for recrawl notification, but Google still decides whether to crawl, index, or rank each page. Google still evaluates content quality. Posts with thin content or duplicate issues may still be skipped. But if your pages were simply waiting to be recrawled, this may help Google notice the update request sooner.

Do I need to run this every time I publish?
Yes — run python OAuth.py after publishing new posts. It takes about 2 minutes and requires no browser login after the first time.

Can I use this for multiple blogs?
Yes — add each blog URL to the BLOGS list in the script. All blogs must be verified under the same Google account in GSC.

What is the token.pickle file?
It stores your login session so you don't need to log in via browser every time. Do not delete it — if you do, the browser login step will happen again on the next run. Do not share it with anyone.

Does this work for Bing too?
No — this submits to Google only. For Bing, see Part 13 of this series which covers the Bing Submission API via Make.com.

More From This Series

📊 SEO Health Checker
Analyze your website’s SEO basics instantly. Check titles, meta descriptions, headings, indexing signals and overall SEO health using our free browser-based SEO tool.
Open Free SEO Health Checker

Comments

Popular Posts

Stop Uploading PDFs Online — Unlock Them Yourself — WordsByEkta🌿

Publish Your Android App on Google Play Store — WordsByEkta🌿

How to Set Up Your Blogger About Me Page: Part 02 — WordsByEkta🌿