How to Auto Publish Posts to Blogger Using Python and the Blogger API
Want to publish blog posts to Blogger automatically using Python — without ever opening the Blogger dashboard? In this step-by-step guide, you will learn how to use the Google Blogger API v3 with OAuth2 to programmatically create and publish posts from a Python script.
By the end of this tutorial, you will have two working scripts: get_blog_id.py to discover your Blog ID, and a production-ready publish_post.py that publishes posts directly to your Blogger blog.
👉 Live Project: You can view the complete working source code on GitHub: https://github.com/aiocrafters/autoblog-python
🎯 What you will build: A Python automation script that authenticates with Google using a stored refresh token and publishes formatted HTML blog posts to any Blogger blog — no browser, no manual login required.
✅ What You Need Before Starting
- A Google account with a Blogger blog already created
- Python 3.8+ installed on your system
- A Google Cloud project with the Blogger API enabled
- OAuth 2.0 credentials: Client ID and Client Secret
- A Google Refresh Token obtained from the OAuth flow
⚠ Prerequisite: This guide assumes you have already obtained your GOOGLE_REFRESH_TOKEN by completing the Google OAuth authorization flow. If you haven't done this yet, follow the step-by-step credentials setup guide first:
👉 How to Get Google Blogger API Credentials (Client ID, Secret, Refresh Token, Blog ID)
Once you have all four credentials ready, come back here to continue.
1 Install Required Python Packages
Open your terminal and install the necessary Google API client libraries and dotenv support:
pip install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client python-dotenv
Here is what each package does:
| Package | Purpose |
|---|---|
google-auth |
Core Google authentication library |
google-auth-oauthlib |
OAuth2 flow helpers |
google-auth-httplib2 |
HTTP transport for Google API calls |
google-api-python-client |
Official Google API client (includes Blogger API) |
python-dotenv |
Loads environment variables from a .env file |
2 Create Your .env File
Create a file named .env in your project folder. This file will store all your credentials securely, so they never appear directly in your Python code.
GOOGLE_CLIENT_ID=xxxxxxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx
GOOGLE_REFRESH_TOKEN=1//0xxxxxxxxxxxxxxxxxxxxxxxxxxxx
BLOGGER_BLOG_ID=1234567890123456789
⚠ Security Warning: Never commit your .env file to a public GitHub repository. Add it to .gitignore immediately by running:
echo ".env" >> .gitignore
Your credentials would give anyone full access to post on your Blogger blog.
💡 Where to find your values: Your GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET come from the Google Cloud Console under APIs & Services → Credentials. The GOOGLE_REFRESH_TOKEN is generated during the OAuth flow. The BLOGGER_BLOG_ID is obtained in the next step.
3 Find Your Blogger Blog ID
Before you can publish posts, you need your Blog ID — a unique numeric identifier for your Blogger blog. You can find it in two ways.
Method 1: From the Blogger Dashboard
- Open the Blogger Dashboard
- Select your blog
- Go to Settings → Basic
- Copy the Blog ID shown on the page
Method 2: Using get_blog_id.py (Recommended)
Create a file named get_blog_id.py and paste the following code:
import os
from dotenv import load_dotenv
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
load_dotenv()
creds = Credentials(
None,
refresh_token=os.getenv("GOOGLE_REFRESH_TOKEN"),
token_uri="https://oauth2.googleapis.com/token",
client_id=os.getenv("GOOGLE_CLIENT_ID"),
client_secret=os.getenv("GOOGLE_CLIENT_SECRET"),
)
creds.refresh(Request())
service = build("blogger", "v3", credentials=creds)
blogs = service.blogs().listByUser(userId="self").execute()
print("\nYour Blogs:\n")
for blog in blogs.get("items", []):
print(f"Title: {blog['name']}")
print(f"Blog ID: {blog['id']}")
print(f"URL: {blog['url']}")
print("-" * 50)
Run it with:
python get_blog_id.py
Expected output:
Your Blogs:
Title: My Blog
Blog ID: 1234567890123456789
URL: https://myblog.blogspot.com
--------------------------------------------------
Copy the Blog ID and add it to your .env file:
BLOGGER_BLOG_ID=1234567890123456789
4 Create publish_post.py
Now create the main script. This is a production-ready version of publish_post.py with:
- Environment variable validation with clear error messages
- Automatic token refresh using the stored refresh token
- Full error handling for API errors and network failures
- HTML content support for rich blog posts
- Optional draft mode — publish live or save as draft
- Clean success output with post URL and ID
Create a file named publish_post.py and paste the following:
import os
import sys
from dotenv import load_dotenv
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# ------------------------------------------------------------------
# Load environment variables
# ------------------------------------------------------------------
load_dotenv()
CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
REFRESH_TOKEN = os.getenv("GOOGLE_REFRESH_TOKEN")
BLOG_ID = os.getenv("BLOGGER_BLOG_ID")
# ------------------------------------------------------------------
# Validate configuration
# ------------------------------------------------------------------
required_vars = {
"GOOGLE_CLIENT_ID": CLIENT_ID,
"GOOGLE_CLIENT_SECRET": CLIENT_SECRET,
"GOOGLE_REFRESH_TOKEN": REFRESH_TOKEN,
"BLOGGER_BLOG_ID": BLOG_ID,
}
missing = [key for key, value in required_vars.items() if not value]
if missing:
print("\n❌ Missing environment variables:")
for item in missing:
print(f" - {item}")
sys.exit(1)
# ------------------------------------------------------------------
# Create Google credentials using refresh token
# ------------------------------------------------------------------
try:
creds = Credentials(
token=None,
refresh_token=REFRESH_TOKEN,
token_uri="https://oauth2.googleapis.com/token",
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
)
creds.refresh(Request())
except Exception as e:
print(f"\n❌ Failed to authenticate with Google: {e}")
sys.exit(1)
# ------------------------------------------------------------------
# Build Blogger API service
# ------------------------------------------------------------------
try:
service = build(
"blogger",
"v3",
credentials=creds,
cache_discovery=False
)
except Exception as e:
print(f"\n❌ Failed to initialize Blogger API: {e}")
sys.exit(1)
# ------------------------------------------------------------------
# Blog post content
# ------------------------------------------------------------------
TITLE = "My First Automated Blogger Post"
CONTENT = """
<h2>🚀 Blogger Automation Test</h2>
<p>Hello Blogger!</p>
<p>
This post was published automatically using Python,
Google OAuth, and the Blogger API.
</p>
<p>
If you can read this, your Blogger automation setup
is working successfully.
</p>
<hr>
<p><strong>Published via Python Script</strong></p>
"""
# Set to True if you want to create a draft instead of publishing
IS_DRAFT = False
post_body = {
"title": TITLE,
"content": CONTENT
}
# ------------------------------------------------------------------
# Publish post
# ------------------------------------------------------------------
try:
response = service.posts().insert(
blogId=BLOG_ID,
body=post_body,
isDraft=IS_DRAFT
).execute()
print("\n✅ Post created successfully!")
print("-" * 60)
print(f"Title : {response.get('title')}")
print(f"URL : {response.get('url')}")
print(f"ID : {response.get('id')}")
print("-" * 60)
except HttpError as e:
print("\n❌ Blogger API Error")
print(e)
except Exception as e:
print(f"\n❌ Unexpected error: {e}")
5 Run the Script
Make sure your .env file contains all four required values, then run:
python publish_post.py
Expected output on success:
✅ Post created successfully!
------------------------------------------------------------
Title : My First Automated Blogger Post
URL : https://yourblog.blogspot.com/2026/06/my-first-automated-blogger-post.html
ID : 9876543210987654321
------------------------------------------------------------
💡 Verify it worked: Open your Blogger Dashboard and check Posts. Your new post should appear either as published (live) or as a draft, depending on the value of IS_DRAFT in your script.
🔄 How the Authentication Works
Understanding the token flow is important for debugging and scaling this automation.
- Your
.envfile stores the Refresh Token permanently - When the script runs, it calls
creds.refresh(Request()) - Google's OAuth server exchanges the refresh token for a short-lived Access Token
- The access token is used to authenticate all Blogger API requests
- Access tokens expire after 1 hour — but the refresh token never expires (unless revoked)
- The script can run again any time and will always get a fresh token automatically
💡 Why use a Refresh Token? Unlike access tokens, refresh tokens are long-lived. Storing a refresh token in your .env file means your script can run on a schedule (e.g., via GitHub Actions) without any user interaction or browser pop-ups.
🛠️ Troubleshooting Common Errors
❌ Error: Missing environment variables
Cause: One or more keys in your .env file is missing or misspelled.
Fix: Open .env and verify all four keys are present: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN, and BLOGGER_BLOG_ID.
❌ Error: Failed to authenticate with Google
Cause: Your refresh token is invalid, revoked, or your Client ID/Secret is wrong.
Fix: Re-run the OAuth authorization flow to get a fresh refresh token. Make sure your Google Cloud project has the Blogger API enabled.
❌ HttpError 403: The caller does not have permission
Cause: The authenticated Google account does not have access to the specified Blog ID.
Fix: Run get_blog_id.py to confirm the Blog ID belongs to the authenticated account.
❌ HttpError 404: Not found
Cause: The BLOGGER_BLOG_ID in your .env is incorrect.
Fix: Copy the Blog ID exactly as shown by get_blog_id.py — it is a long numeric string.
📋 Complete .env Reference
Your final .env file should look like this:
GOOGLE_CLIENT_ID=123456789012-abcdefghijklmnop.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-AbCdEfGhIjKlMnOpQrStUv
GOOGLE_REFRESH_TOKEN=1//0AbCdEfGhIjKlMnOpQrStUvWxYzAb
BLOGGER_BLOG_ID=1234567890123456789
| Variable | Where to Get It |
|---|---|
GOOGLE_CLIENT_ID |
Google Cloud Console → APIs & Services → Credentials |
GOOGLE_CLIENT_SECRET |
Google Cloud Console → APIs & Services → Credentials |
GOOGLE_REFRESH_TOKEN |
Printed in terminal after completing the OAuth flow |
BLOGGER_BLOG_ID |
Output of get_blog_id.py or Blogger Dashboard → Settings |
🤖 Automate with GitHub Actions
Once your script works locally, you can run it automatically on a schedule using GitHub Actions — for free, without any server. The complete GitHub Actions workflow is already included in the autoblog-python repository for reference.
Create the file .github/workflows/publish.yml in your repository:
name: Auto Publish to Blogger
on:
schedule:
- cron: "0 6 * * *" # Runs daily at 6:00 AM UTC
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client python-dotenv
- run: python publish_post.py
env:
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
GOOGLE_REFRESH_TOKEN: ${{ secrets.GOOGLE_REFRESH_TOKEN }}
BLOGGER_BLOG_ID: ${{ secrets.BLOGGER_BLOG_ID }}
⚠ Add Secrets to GitHub: In your repository, go to Settings → Secrets and variables → Actions and add all four environment variables as repository secrets. Never hardcode them in the workflow file.
💡 Tip: The workflow_dispatch trigger allows you to manually click "Run workflow" inside the GitHub Actions tab to test your automation before waiting for the scheduled run.
🎯 Customization Ideas
📝 Publish HTML-Formatted Posts
The CONTENT variable in publish_post.py accepts full HTML. You can include headings, lists, images, links, code blocks, tables, and any standard HTML elements to create rich, beautifully formatted blog posts.
🤖 AI-Generated Blog Content
Combine this script with the Google Gemini API or OpenAI API to automatically generate article content and publish it directly to Blogger. This creates a fully automated content pipeline: generate → format → publish.
📰 Publish News Summaries
Fetch articles from an RSS feed (like Google News), summarize them with AI, and publish a daily digest post to your Blogger blog automatically — all using Python and GitHub Actions.
📅 Schedule Multiple Posts
Create a posts/ folder with multiple HTML files and modify the script to read them one by one on a schedule. This allows you to prepare a week's worth of content at once and have it published automatically each day.
✅ Final Thoughts
You now have a complete, production-ready setup to automatically publish posts to Blogger using Python. The key components are:
- Google OAuth2 Refresh Token — enables authentication without user interaction
- get_blog_id.py — discovers your Blog ID programmatically
- publish_post.py — publishes formatted HTML posts with full error handling
- GitHub Actions — runs the script automatically on a schedule for free
Once this pipeline is set up, you can extend it in any direction — from AI content generation to multi-blog publishing — without ever touching the Blogger dashboard again.
This automation approach works equally well for personal blogs, niche content sites, and content marketing pipelines. The same refresh token method can be reused for any Google API — not just Blogger.