I found myself in a position of needing to publish to WordPress programmatically recently, but despite using WordPress for years, I’d never delved into its API before now. There are a few outdated Python modules out there, but none worked out of the box for me, so I figured I’d go straight to the API with my own code. Ultimately the hardest part of this (for me) was figuring out the right way to authenticate, so I’ll start with that.
Application Password
The non-code part of this (ew, yuck) involves downloading the WordPress plugin Application Passwords:
Then go into the Users section, select a account with administrative privileges, and scroll all the way to the bottom of the page, where you will find the Application Passwords section:
Here you can generate a new password, which you should definitely note in a secure location, as you’ll never be able to access it again (though you can revoke and regenerate it):
So now you have two things:
- A username
- An application password
Finally, Python Code: Authentication and Basic Post
Authenticating took me some time to figure out (other approaches just weren’t working for me), but with the application password, you should be good to go with this code:
import requests from datetime import datetime auth = ("username", "application_password_you_generated") url = "http://yourwebsite.com/wp-json/wp/v2/posts" post_data = { "date" : str(datetime.now().date()), "title" : "This is a sample title", "content" : "HTML is fine here (but is hard to post on the web).", "status" : "publish", } r = requests.post(url, auth=auth, json=post_data) print(r.status_code) # should be 201 with a successful post
Embedding an Image in Your Post
You’ll need to make a separate post request to upload images. Here’s a function to help:
import requests import json def add_a_wordpress_image(img_name): """ Takes an image name, posts to WP, returns image url. """ auth = ("username", "application_password_you_generated") url = "http://yourwebsite.com/wp-json/wp/v2/media" # Define the image media = { "file": open(f"path/to/image/file/{img_name}.png", "rb"), "caption": img_name, "description": img_name } # Post the image to WP image = requests.post(url, auth=auth, files=media) # ! files=, not json= ! # Extract and return the image URL from returned data return str(json.loads(image.content)["source_url"])
The above function returns the URL of the uploaded image. To use it in a post, simply put an img
tag in your HTML with the src
attribute pointing at the returned URL.
Last Thing: Automating Categories
It’s nice to have a category on one’s WordPress posts, but adding them to a post requires the category ID. Here’s a function that takes a category name, checks to see if it exists, and then creates it if not. In either case, it returns the category ID to use in a post.
import requests import json def lookup_or_create_wp_category(name): """ Takes a category name, looks up or creates category, returns ID """ auth = ("username", "application_password_you_generated") url = "http://yourwebsite.com/wp-json/wp/v2/categories?per_page=100" # Note that you'll need to figure out pagination # (or just use an API search, which is probably better) # if you have more than 100 categories on your site. # I'm nowhere close so I didn't concern myself! # Get list of categories r = requests.get(url, auth=auth) cats = list(json.loads(r.content)) # Find the category with the name we are searching using a generator cat = next((item for item in cats if item["name"] == name), None) # Return its ID if it exists (it will be None if not) if cat: return cat["id"] # Otherwise, add the category category = { "name" : name, "description" : name } r = requests.post(url, auth=auth, json=category) return str(json.loads(r.content)["id"])
And here’s how you might use it to add a category to a new post:
import requests cat_id = lookup_or_create_wp_category("category name") post_data = { "date" : str(datetime.now().date()), "title" : "This is a sample title", "content" : "HTML is fine here (but is hard to post on the web).", "status" : "publish", "categories" : [cat_id, ], } requests.post(url, auth=auth, json=post_data)
As always, I hope this is helpful to future travelers.