Follow along step by step — by the end, your Blogger site publishes itself.
This is the no-fluff, copy-paste-and-go version. You'll go from a blank folder to a fully automated Blogger site that writes and publishes its own articles — using Python, Gemini AI, RSS feeds, the Blogger API, and GitHub Actions. Follow the steps in order, paste the code where shown, and you'll have it running in one sitting.
Jump to a step
- Create your accounts
- Enable the Blogger API + get your keys
- Build the project folder
- Script 1 — fetch RSS news automatically
- Script 2 — let Gemini pick & write the article
- Script 3 — auto-publish to Blogger
- Run it all with one command (main.py)
- Make it run 24/7 with GitHub Actions
- Quick SEO checklist
- If something breaks
- FAQ
Step 1 — Create the accounts you'll need
Five free things, five minutes. Open each link, sign up or sign in, and keep the tabs open — you'll need them in the next step.
- Blogger.com — create a new blog (this is where posts will go live)
- Google Cloud Console — to enable the Blogger API
- Google AI Studio — to get your free Gemini API key
- GitHub.com — to host the project and run it on a schedule
- VS Code + Python + Git — install all three on your computer
Step 2 — Enable the Blogger API and grab your keys
1 Enable the API
In Google Cloud Console: create a new project → search "Blogger API v3" in the API Library → click Enable.
Manage Blogger blogs and posts.
✓ API enabled
2 Create OAuth credentials
Go to Credentials → Create Credentials → OAuth Client ID, choose Desktop App, name it anything, then click Create. Download the JSON file and save it as client_secret.json in your project folder — you'll use it once to generate a refresh token, then it stays private forever.
3 Get your Gemini API key
Open AI Studio → click Get API Key → Create API Key → copy it. You'll paste this into your .env file in the next step.
Save these 5 values somewhere safe right now
GEMINI_API_KEY, BLOGGER_CLIENT_ID, BLOGGER_CLIENT_SECRET, BLOGGER_REFRESH_TOKEN (generated once using a small helper script), and BLOG_ID (found in your Blogger dashboard URL). You'll need all five in Step 3 and Step 8.
Step 3 — Build the project folder
Open VS Code → File → Open Folder → create a new empty folder, e.g. autoblog. Open the built-in terminal (Ctrl + `) and run:
Now build the folder structure. In VS Code's Explorer, create these folders and files:
// .gitignore
venv/
__pycache__/
.env
client_secret.json
Paste this into config/feeds.json — your news sources (add or remove as you like):
[
"https://techcrunch.com/feed/",
"https://feeds.arstechnica.com/arstechnica/index"
]
Paste this into .env (fill in your real values from Step 2):
GEMINI_API_KEY=your_key_here
BLOGGER_CLIENT_ID=your_client_id
BLOGGER_CLIENT_SECRET=your_client_secret
BLOGGER_REFRESH_TOKEN=your_refresh_token
BLOG_ID=your_blog_id
Never skip this
.env and client_secret.json hold your private keys. .gitignore above stops them from ever being uploaded to GitHub — double-check both filenames are listed before you push anything.
Step 4 — Script 1: pull fresh news automatically
Create scripts/fetch_rss.py and paste this in:
import json
from pathlib import Path
import feedparser
BASE_DIR = Path(__file__).parent.parent
FEEDS_FILE = BASE_DIR / "config" / "feeds.json"
OUTPUT_FILE = BASE_DIR / "data" / "articles.json"
def fetch():
feeds = json.loads(FEEDS_FILE.read_text(encoding="utf-8"))
articles = []
for url in feeds:
feed = feedparser.parse(url)
for entry in feed.entries:
articles.append({
"source": feed.feed.get("title", "Unknown"),
"title": entry.get("title", ""),
"summary": entry.get("summary", ""),
"link": entry.get("link", ""),
"published": entry.get("published", "")
})
OUTPUT_FILE.write_text(json.dumps(articles, indent=4, ensure_ascii=False), encoding="utf-8")
print(f"Downloaded {len(articles)} articles.")
if __name__ == "__main__":
fetch()
Save the file, then run it in the terminal:
Open data/articles.json — you should see a list of fresh article titles, summaries, and links. That's your raw material, fully automated, zero manual searching.
Step 5 — Script 2: let Gemini choose & write the article
Create scripts/write_article.py. This script sends your RSS articles to Gemini and asks it to pick the best one, then write a complete, ready-to-publish article as structured JSON:
import json, os
from pathlib import Path
import google.generativeai as genai
from dotenv import load_dotenv
load_dotenv()
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
model = genai.GenerativeModel("gemini-1.5-flash")
BASE_DIR = Path(__file__).parent.parent
articles = json.loads((BASE_DIR / "data" / "articles.json").read_text(encoding="utf-8"))
prompt = f"""
You are an expert SEO blog writer. From the list of articles below, pick the single
most interesting, non-duplicate topic and write a complete, original 800-1000 word
blog article about it. Respond ONLY with valid JSON in this exact format:
{{
"seo_title": "...",
"meta_description": "...",
"slug": "url-friendly-slug",
"labels": ["tag1", "tag2"],
"html": "<p>full article body in HTML</p>"
}}
Articles:
{json.dumps(articles[:30], ensure_ascii=False)}
"""
response = model.generate_content(prompt)
clean = response.text.strip().removeprefix("```json").removesuffix("```").strip()
article = json.loads(clean)
(BASE_DIR / "data" / "article.json").write_text(
json.dumps(article, indent=4, ensure_ascii=False), encoding="utf-8"
)
print("Article written:", article["seo_title"])
"seo_title": "Why Big Tech Is Racing to Build Its Own AI Chips",
"meta_description": "OpenAI, Google and Amazon are designing...",
"slug": "big-tech-ai-chips-race",
"labels": ["AI", "Technology"],
"html": "<p>..."
}
Run it the same way: python scripts/write_article.py. Open data/article.json — a complete, original article is sitting there, title, labels, and all.
Step 6 — Script 3: auto-publish straight to Blogger
Create scripts/blogger_publish.py. This authenticates with Google using your refresh token (so it never expires) and posts the article directly — no copy-pasting into the Blogger dashboard:
import json, os, requests
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
BASE_DIR = Path(__file__).parent.parent
def get_access_token():
r = requests.post("https://oauth2.googleapis.com/token", data={
"client_id": os.getenv("BLOGGER_CLIENT_ID"),
"client_secret": os.getenv("BLOGGER_CLIENT_SECRET"),
"refresh_token": os.getenv("BLOGGER_REFRESH_TOKEN"),
"grant_type": "refresh_token"
})
return r.json()["access_token"]
def publish():
article = json.loads((BASE_DIR / "data" / "article.json").read_text(encoding="utf-8"))
published_file = BASE_DIR / "data" / "published.json"
published = json.loads(published_file.read_text(encoding="utf-8")) if published_file.exists() else []
if any(p["slug"] == article["slug"] for p in published):
print("Already published. Skipping.")
return
token = get_access_token()
blog_id = os.getenv("BLOG_ID")
url = f"https://www.googleapis.com/blogger/v3/blogs/{blog_id}/posts/"
payload = {
"kind": "blogger#post",
"title": article["seo_title"],
"content": article["html"],
"labels": article["labels"]
}
res = requests.post(url, headers={"Authorization": f"Bearer {token}"}, json=payload).json()
published.append({"slug": article["slug"], "title": article["seo_title"], "url": res["url"], "id": res["id"]})
published_file.write_text(json.dumps(published, indent=4, ensure_ascii=False), encoding="utf-8")
print("Published Successfully!", res["url"])
if __name__ == "__main__":
publish()
Run python scripts/blogger_publish.py and check your terminal:
Open your Blogger dashboard — your article is live, with title, formatting, and labels already in place.
Step 7 — Run everything with one command
Create scripts/main.py to chain all three steps so you never run them one by one again:
import subprocess
subprocess.run(["python", "scripts/fetch_rss.py"])
subprocess.run(["python", "scripts/write_article.py"])
subprocess.run(["python", "scripts/blogger_publish.py"])
Test it: python scripts/main.py — RSS fetch, AI writing, and publishing all happen back to back, automatically.
Step 8 — Make it run 24/7 with GitHub Actions
1 Push your project to GitHub
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/yourname/autoblog.git
git push -u origin main
2 Add your secret keys to GitHub
On GitHub: open your repo → Settings → Secrets and variables → Actions → New repository secret. Add all five: GEMINI_API_KEY, BLOGGER_CLIENT_ID, BLOGGER_CLIENT_SECRET, BLOGGER_REFRESH_TOKEN, BLOG_ID.
3 Create the workflow file
Create .github/workflows/autoblog.yml and paste this in:
name: Lognito Autoblog
on:
workflow_dispatch:
schedule:
- cron: "0 4,16 * * *"
jobs:
run-autoblog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install -r requirements.txt
- run: python scripts/main.py
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
BLOGGER_CLIENT_ID: ${{ secrets.BLOGGER_CLIENT_ID }}
BLOGGER_CLIENT_SECRET: ${{ secrets.BLOGGER_CLIENT_SECRET }}
BLOGGER_REFRESH_TOKEN: ${{ secrets.BLOGGER_REFRESH_TOKEN }}
BLOG_ID: ${{ secrets.BLOG_ID }}
Commit and push this file. The cron schedule 0 4,16 * * * means it runs automatically twice a day, 4 AM and 4 PM UTC — adjust the numbers for your own schedule.
4 Test it manually first
On GitHub, open the Actions tab → click Lognito Autoblog → click Run workflow → Run workflow again to confirm.
Green checkmark = it worked. From now on, GitHub runs this on schedule whether your laptop is on, off, or on the other side of the world.
Step 9 — Quick SEO checklist for every auto-post
- Keep
seo_titleunder 60 characters,meta_descriptionunder 155 - Use a clean
slug— lowercase, hyphens, no special characters - Connect Google Search Console and Bing Webmaster Tools so new posts get indexed fast
- Keep 2-3 relevant
labelsper post for topical structure - Review published.json weekly to confirm no duplicates slipped through
Step 10 — If something breaks
| Error | Fix |
|---|---|
401 Unauthorized | Your refresh token expired or is wrong — regenerate it |
JSONDecodeError on article.json | Gemini's response wasn't clean JSON — re-run write_article.py |
| 0 articles downloaded | An RSS URL is dead — open it in a browser and replace it in feeds.json |
| Workflow not appearing on GitHub | Check the folder is exactly .github/workflows/, not .github/workflow/ |
| Article published twice | published.json wasn't committed back — commit it after every run |
Need more free browser-based tools while you build?
Check out Zedwiser.com for free productivity and content tools.
FAQ
Do I need to know Python well?
No — every script above is copy-paste ready. Basic comfort with running terminal commands is enough.
Will this keep running if I close my laptop?
Yes, that's the whole point of Step 8 — GitHub's own servers run it, not your computer.
Is this allowed on Blogger?
Yes, as long as content is original and useful — this pipeline generates real articles, it doesn't scrape or copy.
Can I change how often it posts?
Yes — just edit the cron line in autoblog.yml to whatever schedule you want.
More automation guides and free tools: Zedwiser.com.