Trouble with Ghost Admin API: Issues Updating Posts after Removing Links


I’ve been working on automating a process for a Ghost blog where I need to remove all the links from the posts. The workflow I’ve been following is:

  • Fetch all articles using the Ghost Content API.
  • Process the articles to remove all the links.
  • Update the articles using the Ghost Admin API.

However, I’ve run into a series of issues during this process:

Errors and Issues:

Initially, there was an issue with generating the JWT token for the Admin API. Seems I’ve resolved this, but it led to another problem.

Despite generating the JWT token according to Ghost’s documentation, the Admin API consistently returns an “Invalid token: invalid signature” error.

While updating my code, I found that certain methods, like decode(), behaved differently between Python versions. For instance, in Python 3.11.5, the method decode was flagged as an error because the generated JWT was already a string. But working around that did not help me.

Here’s the Python code I’ve been using:

import requests
import re
import jwt
import time

# Ghost API details
api_url = "###"
content_api_key = "###"
admin_api_key = "###"

# Fetching the first post using the Content API
response = requests.get(f"{api_url}/ghost/api/v4/content/posts/?key={content_api_key}&limit=1")
data = response.json()
post = data['posts'][0]

# Removing all links from the HTML content
clean_content = re.sub(r'<a [^>]*>([^<]+)</a>', r'\1', post['html'])

# Converting the HTML to Mobiledoc format
mobiledoc_structure = {
    "version": "0.3.1",
    "markups": [],
    "atoms": [],
    "cards": [["html", {"cardName": "html", "html": clean_content}]],
    "sections": [[10, 0]]

# Encoding the JWT token for the Admin API
header = {
    "alg": "HS256",
    "typ": "JWT",
    "kid": admin_api_key.split(':')[0]

payload = {
    "iat": int(time.time()),  # current time
    "exp": int(time.time()) + 86400,  # current time + 24 hours
    "aud": "/v4/admin/"

token = jwt.encode(payload, admin_api_key.split(':')[1], algorithm="HS256", headers=header)

# Updating the post using the Admin API
update_url = f"{api_url}/ghost/api/v4/admin/posts/{post['id']}/"
headers = {
    "Authorization": f"Ghost {token}"  # Removed the .decode('utf-8') here
data = {
    "posts": [{
        "mobiledoc": str(mobiledoc_structure),
        "html": clean_content  # setting the HTML directly just for redundancy, mobiledoc should be enough

response = requests.put(update_url, json=data, headers=headers)

if response.status_code == 200:
    print("Post updated successfully!")
    print(f"Failed to update the post. Response: {response.text}")

I’m particularly perplexed by the “Invalid token: invalid signature” error, as I’ve followed the Ghost documentation closely when generating the JWT token. If anyone has encountered similar issues, especially with updating content on ghost with API, the Ghost Admin API and JWT token generation, I’d greatly appreciate your insights.

Thanks in advance!

This documentation says that exp needs to be a max of five minutes in the future:

I’m not sure that’s your problem or your only problem, but it might be worth trying.

I see you’re trying to do this in Python, but if you can’t get jwts working, you might consider switching over to using the javascript SDK, which handles all the jwt generation under the hood.

1 Like

You didn’t mention which Ghost version you are using, but I noticed your API call to update specifies v4, while v5 is the latest.