Skip to content

Instantly share code, notes, and snippets.

@wojtekadams
Last active October 17, 2025 19:58
Show Gist options
  • Select an option

  • Save wojtekadams/4e799c96554e170ac2ad24c3f012e4f4 to your computer and use it in GitHub Desktop.

Select an option

Save wojtekadams/4e799c96554e170ac2ad24c3f012e4f4 to your computer and use it in GitHub Desktop.
This Python script connects to the Ghost Admin API, retrieves all blog posts in batches, and automatically adds a specified tag to each post that doesn’t already have it.
import jwt
import time
import requests
# Instalation
## python3 -m venv ~/python-dev-env
## source ~/python-dev-env/bin/activate
## pip install requests PyJWT
## download ghost_cms_add_tag.py
## edit ghost_cms_add_tag.py -> add url, tag and admin key
# Configuration
api_url = "https://yourblog.com/ghost/api/admin"
admin_key = "YOUR_ADMIN_API_KEY"
new_tag = "wojtekadams_tag"
# JWT token generation
key_id, secret = admin_key.split(":")
iat = int(time.time())
header = {"alg": "HS256", "kid": key_id}
payload = {"iat": iat, "exp": iat + 5 * 60, "aud": "/admin/"}
token = jwt.encode(payload, bytes.fromhex(secret), algorithm="HS256", headers=header)
headers = {"Authorization": f"Ghost {token}"}
# Fetch all posts with pagination
all_posts = []
page_number = 1
while True:
r = requests.get(f"{api_url}/posts/?limit=100&page={page_number}", headers=headers)
r.raise_for_status()
posts = r.json()["posts"]
if not posts:
break
all_posts.extend(posts)
print(f"Fetching page {page_number} ({len(all_posts)} posts total)")
page_number += 1
print(f"Found {len(all_posts)} posts in total")
# Add a tag to each post if missing
for post in all_posts:
tags = post.get("tags", [])
tag_names = [t["name"] for t in tags if "name" in t]
if new_tag not in tag_names:
tags.append({"name": new_tag})
post_data = {
"posts": [{
"id": post["id"],
"updated_at": post["updated_at"],
"tags": tags
}]
}
update = requests.put(f"{api_url}/posts/{post['id']}/", headers=headers, json=post_data)
if update.ok:
print(f"Added tag '{new_tag}' to post: {post['title']}")
else:
print(f"Error updating post {post['title']}: {update.text}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment