How to Use Blogger API to Post on Blogger: A Step-by-Step Guide
The Blogger API allows developers to programmatically interact with Blogger blogs, enabling tasks such as creating, updating, and deleting posts, managing comments, and retrieving blog information. This guide will focus on posting content.
1. Prerequisites
Before you begin, ensure you have the following:
- A Google Account: Necessary for accessing Google Cloud and Blogger.
- A Blogger Blog: The blog you intend to post to.
- Basic understanding of APIs: Familiarity with concepts like REST, HTTP methods (GET, POST, PUT, DELETE), and JSON.
- Python (or your preferred programming language): This guide will provide Python examples, but the concepts apply to other languages as well.
2. Set Up Your Google Cloud Project
- All Google API interactions require a project in Google Cloud Platform (GCP).
- Go to Google Cloud Console: Navigate to console.cloud.google.com.
- Create a New Project:
- Click on the project dropdown at the top of the page (usually displays "My First Project" or your current project name).
- Click "New Project".
- Give your project a meaningful name (e.g., "Blogger API Integration") and click "Create".
- Enable the Blogger API:
- Once your project is created, use the navigation menu (☰) on the left.
- Go to APIs & Services > Library.
- Search for "Blogger API".
- Click on "Blogger API" in the search results.
- Click the "Enable" button.
3. Create OAuth 2.0 Credentials
To access user data (like posting to your blog), your application needs authorization using OAuth 2.0.
- Go to Credentials Page: In the Google Cloud Console, navigate to APIs & Services > Credentials.
- Create Credentials:
- Click on "CREATE CREDENTIALS" at the top.
- Select "OAuth client ID".
- Configure Consent Screen: If you haven't done this before, you'll be prompted to configure your OAuth consent screen.
- Choose "External" for User Type and click "CREATE".
- Fill in the required fields:
- App name: A user-facing name for your application (e.g., "My Blogger Poster").
- User support email: Your email address.
- Developer contact information: Your email address.
- Click "SAVE AND CONTINUE".
- (Optional) Scopes: While you can add scopes here, it's often easier to define them in your code. For Blogger, you'll need the https://www.googleapis.com/auth/blogger scope.
- (Optional) Test users: Add your Google account as a test user.
- Review and go back to Dashboard.
- Create OAuth Client ID (Continued):
- After configuring the consent screen, go back to Credentials > Create Credentials > OAuth client ID.
- Application type:
- For a script running on your computer, choose Desktop app.
- For a web application, choose Web application and set authorized redirect URIs (e.g., http://localhost:8080 for local development, or your actual application's redirect URI).
- Give it a name (e.g., "Blogger Desktop Client").
- Click "CREATE".
- Download Credentials: A dialog will appear showing your Client ID and Client Secret. Download the JSON file (or copy the ID and Secret) and keep it secure. This file contains sensitive information.
4. Install Google Client Library (Python Example)
Google provides client libraries for various programming languages to simplify API interactions.
For Python, install the google-api-python-client and google-auth-oauthlib packages:
pip install google-api-python-client google-auth-oauthlib
5. Authentication Flow (Python Example)
The first step in using the API is to authenticate your application and obtain access tokens. This typically involves an OAuth 2.0 flow where the user grants permission to your application.
Place your downloaded client_secret_YOUR_CLIENT_ID.json file in the same directory as your Python script, or rename it to client_secret.json.
import os
import pickle
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/blogger']
def get_authenticated_service():
creds = None
# The file token.pickle stores the user's access and refresh
tokens, and is
# created automatically when the authorization flow completes
for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user
log in.
if not creds or not creds.valid:
if creds and creds.expired and
creds.refresh_token:
creds.refresh(Request())
else:
# Point to your downloaded
client_secret.json file
flow =
InstalledAppFlow.from_client_secrets_file(
'client_secret.json',
SCOPES)
creds =
flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
# Build the Blogger service object
service = build('blogger', 'v3', credentials=creds)
return service
# Example usage (don't run this directly, it's just for demonstration)
# service = get_authenticated_service()
# print("Authentication successful!")
Explanation of the get_authenticated_service function:
- It checks for an existing token.pickle file, which stores your refresh token. This allows your application to authenticate without prompting you to log in every time.
- If token.pickle doesn't exist or the credentials are invalid, it initiates the OAuth 2.0 flow using InstalledAppFlow. This will open a browser window for you to log in to your Google account and grant permissions.
- Upon successful authorization, an access_token and refresh_token are obtained. The refresh_token is saved to token.pickle for future use.
- Finally, it builds and returns the blogger service object, which you'll use to make API calls.
6. Get Your Blog ID
Most Blogger API operations require your Blog ID. You can find this in your Blogger dashboard URL or by using the API.
Method 1: From Blogger Dashboard URL
When you are logged into your Blogger dashboard and viewing your blog's posts, the URL will look something like this:
https://www.blogger.com/blog/posts/YOUR_BLOG_ID
The long string of numbers after /blog/posts/ is your Blog ID.
Method 2: Using the API
You can use the blogs.getByUrl method or blogs.listByUser method to retrieve your blog ID.
# Assuming 'service' is the authenticated Blogger service object
def get_blog_id(service, blog_url=None):
if blog_url:
try:
blog =
service.blogs().getByUrl(url=blog_url).execute()
print(f"Found blog '{blog['name']}'
with ID: {blog['id']}")
return blog['id']
except Exception as e:
print(f"Error getting blog by URL:
{e}")
return None
else:
try:
# List blogs for the authenticated
user
blogs =
service.blogs().listByUser(userId='self').execute()
if 'items' in blogs:
for blog in
blogs['items']:
print(f"Blog Name: {blog['name']}, ID: {blog['id']}, URL: {blog['url']}")
# You might want to
let the user choose or pick the first one
if blogs['items']:
return
blogs['items'][0]['id'] # Return the ID of the first blog found
else:
print("No blogs found
for this user.")
return None
except Exception as e:
print(f"Error listing blogs: {e}")
return None
# --- Main execution example ---
if __name__ == '__main__':
service = get_authenticated_service()
# Replace with your actual blog URL or leave None to list all
blogs
# blog_id = get_blog_id(service,
blog_url="https://yourblogname.blogspot.com/")
blog_id = get_blog_id(service) # This will list all your blogs
and pick the first one
if blog_id:
print(f"\nUsing Blog ID: {blog_id}")
else:
print("\nCould not determine Blog ID. Exiting.")
exit()
7. Post a New Blog Post
To create a new post, you'll use the posts.insert method.
# Assuming 'service' and 'blog_id' are already defined from previous steps
def create_blog_post(service, blog_id, title, content, labels=None,
is_draft=False):
"""
Creates a new blog post.
Note: Custom permalinks (URLs) and individual post search
descriptions
cannot be reliably set via the Blogger API.
"""
post_body = {
'kind': 'blogger#post',
'blog': {'id': blog_id}, # Associate the post with
the specific blog ID
'title': title,
'content': content, # HTML content is allowed here
'labels': labels if labels else [], # Optional
list of strings for labels
'status': 'DRAFT' if is_draft else 'LIVE' # Set to
'LIVE' for immediate publishing
}
# IMPORTANT: The 'customMetaData' field in the Blogger API is
not currently used
# by Blogger for the "Search Description" (meta description) of
individual posts.
# While you can send data to this field via the API, it will not
appear
# in the Blogger UI's "Search description" box or be used by
search engines for that post.
# To set a search description, you must do it manually in the
Blogger UI
# after enabling the "Search description" setting for your blog.
# The 'url' field is read-only and cannot be set via the API.
# Blogger automatically generates the permalink.
try:
print(f"\nAttempting to create post:
'{title}'...")
request = service.posts().insert(blogId=blog_id,
body=post_body, isDraft=is_draft)
response = request.execute()
print(f"Successfully created post:
'{response['title']}' (ID: {response['id']})")
print(f"View it at: {response['url']}")
return response
except Exception as e:
print(f"Error creating post: {e}")
return None
# --- Main execution example ---
if __name__ == '__main__':
# ... (code for getting service and blog_id from Section 5 &
6) ...
# Example: Create a new post
new_post_title = "My First Post from Blogger API"
new_post_content = "This is a **bold** new post published
programmatically using the Blogger API. " \
"You can include <i>HTML</i> content here."
new_post_labels = ["API", "Python", "Automation"]
# new_post_search_description = "A test post created using the
Blogger API with custom search description. This will NOT show up." # Removed
as it's not functional
created_post = create_blog_post(
service,
blog_id,
new_post_title,
new_post_content,
new_post_labels,
is_draft=False # Set to True to save as draft
)
if created_post:
# You can use created_post['id'] for subsequent
updates/deletions
post_id_to_update_or_delete = created_post['id']
8. Update a Blog Post
To update an existing post, you'll need its post_id and use the posts.update method.
# Assuming 'service' and 'blog_id' are already defined from previous steps
def update_blog_post(service, blog_id, post_id, new_title=None,
new_content=None, new_labels=None):
update_body = {
'kind': 'blogger#post',
'id': post_id, # This is crucial for updating the
specific post
}
if new_title:
update_body['title'] = new_title
if new_content:
update_body['content'] = new_content
if new_labels is not None: # Allow empty list to clear labels
update_body['labels'] = new_labels
try:
request = service.posts().update(blogId=blog_id,
postId=post_id, body=update_body)
response = request.execute()
print(f"\nSuccessfully updated post:
'{response['title']}' (ID: {response['id']})")
print(f"View updated post at: {response['url']}")
return response
except Exception as e:
print(f"\nError updating post: {e}")
return None
# --- Main execution example ---
if __name__ == '__main__':
# ... (code for getting service, blog_id, and potentially
creating a post to get its ID) ...
# Example: Update the created post
if 'post_id_to_update_or_delete' in locals(): # Check if the
variable exists
updated_post_content = "This post has been
<i>updated</i> programmatically. " \
"Adding more content and a new label."
updated_post_labels = ["API", "Python", "Updated"]
updated_post = update_blog_post(
service,
blog_id,
post_id_to_update_or_delete,
new_title="Updated Title from API!",
new_content=updated_post_content,
new_labels=updated_post_labels
)
9. Delete a Blog Post
To delete a post, you'll need its post_id and use the posts.delete method.
# Assuming 'service' and 'blog_id' are already defined from previous steps
def delete_blog_post(service, blog_id, post_id):
try:
request = service.posts().delete(blogId=blog_id,
postId=post_id)
request.execute()
print(f"\nSuccessfully deleted post with ID:
{post_id}")
return True
except Exception as e:
print(f"\nError deleting post: {e}")
return False
# --- Main execution example ---
if __name__ == '__main__':
# ... (code for getting service, blog_id, and potentially
creating/updating a post) ...
# Example: Delete the post
# IMPORTANT: Use this with caution! It permanently deletes the
post.
if 'post_id_to_update_or_delete' in locals(): # Check if the
variable exists
confirm_delete = input(f"\nDo you want to delete
post ID {post_id_to_update_or_delete}? (yes/no): ").lower()
if confirm_delete == 'yes':
delete_blog_post(service, blog_id,
post_id_to_update_or_delete)
else:
print("Deletion cancelled.")
Complete Python Script Example
Here's a consolidated example of the Python script integrating all the functions:
import os
import pickle
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
import json
from datetime import datetime
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/blogger']
def get_authenticated_service():
"""
Authenticates the user and returns the Blogger API service
object.
Uses token.pickle to store/retrieve credentials for subsequent
runs.
"""
creds = None
# Check if a token file exists
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If credentials are not valid or don't exist, initiate the
OAuth flow
if not creds or not creds.valid:
if creds and creds.expired and
creds.refresh_token:
# Refresh token if expired
creds.refresh(Request())
else:
# Load client secrets from the
downloaded JSON file
# Make sure client_secret.json is in
the same directory
if not
os.path.exists('client_secret.json'):
print("Error:
'client_secret.json' not found. Please download it from Google Cloud
Console.")
print("Go to APIs
& Services > Credentials, download OAuth Client ID JSON, and rename it
to client_secret.json.")
return None
flow =
InstalledAppFlow.from_client_secrets_file(
'client_secret.json',
SCOPES)
creds =
flow.run_local_server(port=0) # Runs a local server for the OAuth redirect
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
# Build the Blogger service object
service = build('blogger', 'v3', credentials=creds)
return service
def get_blog_id(service, blog_url=None):
"""
Retrieves the Blog ID. Can get by URL or list all blogs for the
user.
"""
if blog_url:
try:
blog =
service.blogs().getByUrl(url=blog_url).execute()
print(f"Found blog '{blog['name']}'
with ID: {blog['id']}")
return blog['id']
except Exception as e:
print(f"Error getting blog by URL
'{blog_url}': {e}")
return None
else:
try:
print("\nAttempting to list blogs
for the authenticated user...")
# List blogs for the authenticated
user
blogs =
service.blogs().listByUser(userId='self').execute()
if 'items' in blogs:
print("Available
Blogs:")
for i, blog in
enumerate(blogs['items']):
print(f" {i+1}. Name: {blog['name']}, ID: {blog['id']}, URL:
{blog['url']}")
if blogs['items']:
# Simple
selection for demonstration; in a real app, you might have a UI
while
True:
try:
choice = input("Enter the number of the blog you want to
use (or 'q' to quit): ")
if choice.lower() == 'q':
return None
idx = int(choice) - 1
if 0 <= idx < len(blogs['items']):
selected_blog = blogs['items'][idx]
print(f"Selected Blog:
'{selected_blog['name']}' (ID: {selected_blog['id']})")
return selected_blog['id']
else:
print("Invalid number. Please try again.")
except ValueError:
print("Invalid input. Please enter a number or 'q'.")
else:
print("No blogs found
for this user.")
return None
except Exception as e:
print(f"Error listing blogs: {e}")
return None
def create_blog_post(service, blog_id, title, content, labels=None,
is_draft=False):
"""
Creates a new blog post.
Note: Custom permalinks (URLs) and individual post search
descriptions
cannot be reliably set via the Blogger API.
"""
post_body = {
'kind': 'blogger#post',
'blog': {'id': blog_id}, # Associate the post with
the specific blog ID
'title': title,
'content': content, # HTML content is allowed here
'labels': labels if labels else [], # Optional
list of strings for labels
'status': 'DRAFT' if is_draft else 'LIVE' # Set to
'LIVE' for immediate publishing
}
# IMPORTANT: The 'customMetaData' field in the Blogger API is
not currently used
# by Blogger for the "Search Description" (meta description) of
individual posts.
# While you can send data to this field via the API, it will not
appear
# in the Blogger UI's "Search description" box or be used by
search engines for that post.
# To set a search description, you must do it manually in the
Blogger UI
# after enabling the "Search description" setting for your blog.
# The 'url' field is read-only and cannot be set via the API.
# Blogger automatically generates the permalink.
try:
print(f"\nAttempting to create post:
'{title}'...")
request = service.posts().insert(blogId=blog_id,
body=post_body, isDraft=is_draft)
response = request.execute()
print(f"Successfully created post:
'{response['title']}' (ID: {response['id']})")
print(f"View it at: {response['url']}")
return response
except Exception as e:
print(f"Error creating post: {e}")
return None
def update_blog_post(service, blog_id, post_id, new_title=None,
new_content=None, new_labels=None):
"""
Updates an existing blog post by its ID.
"""
update_body = {
'kind': 'blogger#post',
'id': post_id, # Crucial: specifies which post to
update
}
if new_title:
update_body['title'] = new_title
if new_content:
update_body['content'] = new_content
if new_labels is not None: # Allow passing an empty list to
clear labels
update_body['labels'] = new_labels
try:
print(f"\nAttempting to update post ID:
{post_id}...")
request = service.posts().update(blogId=blog_id,
postId=post_id, body=update_body)
response = request.execute()
print(f"Successfully updated post:
'{response['title']}' (ID: {response['id']})")
print(f"View updated post at: {response['url']}")
return response
except Exception as e:
print(f"Error updating post: {e}")
return None
def delete_blog_post(service, blog_id, post_id):
"""
Deletes a blog post by its ID. This action is irreversible.
"""
try:
print(f"\nAttempting to delete post ID:
{post_id}...")
request = service.posts().delete(blogId=blog_id,
postId=post_id)
request.execute()
print(f"Successfully deleted post with ID:
{post_id}")
return True
except Exception as e:
print(f"Error deleting post: {e}")
return False
# --- Main execution flow ---
if __name__ == '__main__':
service = get_authenticated_service()
if not service:
exit() # Exit if authentication failed
# Option 1: Provide your blog URL directly
# blog_id = get_blog_id(service,
blog_url="https://yourblogname.blogspot.com/")
# Option 2: List all your blogs and select one
blog_id = get_blog_id(service)
if not blog_id:
print("Blog ID not determined. Exiting.")
exit()
# --- Step 1: Create a new post ---
new_post_title = "My Auto-Generated Test Post"
new_post_content = """
<p>This is a <strong>test post</strong>
created using the <b>Blogger API</b> with Python.</p>
<p>Please remember that custom search descriptions cannot
be set via the API.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
"""
new_post_labels = ["Automation", "Python", "API Test"]
created_post = create_blog_post(
service,
blog_id,
new_post_title,
new_post_content,
new_post_labels,
is_draft=False # Set to True to save as draft
)
if created_post:
post_id_to_manage = created_post['id']
# --- Step 2: Update the created post after a
brief pause ---
input("\nPress Enter to update the post...") #
Wait for user
updated_title = "Updated Title: API Automation in
Action!"
updated_content = """
<p>This post has been
<em>updated</em> using the API.</p>
<p>New content has been added to demonstrate
the <b>update</b> functionality.</p>
<p><i>Revised on:</i> """ +
str(datetime.now()) + """</p>
"""
updated_labels = ["Automation", "Python", "API
Test", "Updated"]
update_blog_post(
service,
blog_id,
post_id_to_manage,
new_title=updated_title,
new_content=updated_content,
new_labels=updated_labels
)
# --- Step 3: Delete the post after another brief
pause ---
input("\nPress Enter to attempt to delete the post
(this is irreversible)...") # Wait for user
confirm_delete = input(f"Are you sure you want to
delete post '{updated_title}' (ID: {post_id_to_manage})? (yes/no): ").lower()
if confirm_delete == 'yes':
delete_blog_post(service, blog_id,
post_id_to_manage)
else:
print("Deletion cancelled.")
else:
print("Post creation failed, cannot proceed with
update/delete steps.")
print("\nScript finished.")

Start the discussion!