WordPress has become the most used content management system (CMS) due in no small part to its application programming interface (API). The WordPress REST API enables WordPress to “talk” with other applications written in various languages — including Python.
Python is an extensible programming language with diverse uses and a human-readable syntax, making it a powerful tool for remotely managing WordPress content.
Here are some WordPress REST API use cases for your apps and how you can use Python to support them:
- Use predefined templates to enable your app to turn raw data into formatted posts with explanations quickly.
- Build a back-office application on Django and Python that displays limited-time offers to your customers every time an object-specific discount or sales event occurs.
- Integrate Python scripts to run inside your WordPress site
This tutorial will help you create a simple Python console application that communicates with and executes operations on the WordPress REST API. The complete project code is also available.
Installing and Configuring WordPress
First, let’s install and run a WordPress website locally on your development machine. This is an excellent way to start with WordPress since you don’t have to create an account or buy a domain name for web hosting.
Before installing WordPress locally, some components are required to run on your computer, including the Apache web server, a local database, and the PHP language in which WordPress is written.
Fortunately, we can use DevKinsta, a free local WordPress development suite available for all major OSes (you don’t have to be a Kinsta customer to use it).
DevKinsta is available for Windows, Mac, and Linux, and installs WordPress plus all of its dependencies on your local machine.
Before installing DevKinsta, you must have Docker running locally, so download and install the Docker Engine if you haven’t yet.
After installing Docker Desktop, you can automatically download the package that fits your OS.
When you run the DevKinsta installer, Docker starts initializing immediately:
Next, choose New WordPress site from the Create new Site menu:
Now the DevKinsta installer requires you to create the credentials for the WordPress admin account:
Once installed, DevKinsta is a standalone application. Now you can access both the WordPress site (via the Open Site button) and the WordPress admin dashboard (WP Admin button).
Next, you need to enable SSL and HTTPS for your website. This improves the security of your website through an SSL certificate.
Now go to the DevKinsta app and click the Open site button. A new browser tab will show the home page of your WordPress site:
This is your WordPress blog, where you can start writing. But to enable Python to access and use the WordPress REST API, we must first configure the WordPress Admin.
Now click the WP Admin button on the DevKinsta app, then provide your user and password to access the WordPress Dashboard:
Once you’re logged in, you’ll see WordPress Dashboard:
WordPress uses cookie authentication as its standard method. But if you want to control it using the REST API, you must authenticate with a technique that grants access to the WordPress REST API.
For this, you’ll use Application Passwords. These are 24-character long strings that WordPress generates and associates with a user profile that has permission to manage your website.
To use Application Passwords, click the Plugin menu on the Dashboard, then search for the plugin with the same name. Then install and activate the Application Passwords Plugin:
To begin creating your application password, start by expanding the Users menu and clicking All Users:
Now, click Edit below your admin user name:
Scroll down the Edit User page and find the Application Passwords section. Here, provide a name for the Application Password, which you’ll use later to authenticate your Python app requests and consume the REST API:
Click Add New Application Password so WordPress can generate a random 24-character password for you:
Next, copy this password and save it in a safe location to use later. Remember, you won’t be able to retrieve this password once you close this page.
Finally, you must configure permalinks. WordPress allows you to create a custom URL structure for your permalinks and archives. Let’s change it so that a WordPress post titled, e.g., “Your First WordPress Website” can be accessed through the intuitive URL https://your-website.local:port/your-first-wordpress-website/. This approach brings several benefits, including improved usability and aesthetics.
To configure permalinks, expand the Settings section and click the Permalinks menu. Here, change the Common Settings to Post name:
Setting the permalink structure using the Post name structure is also necessary because it will allow us to retrieve posts later in our Python code using the JSON format. Otherwise, a JSON decoding error will be thrown.
How To Control WordPress From Python
WordPress is written in PHP, but it has a REST API that enables other programming languages, sites, and apps to consume its content. Exposing the WordPress content in REST architecture makes it available in JSON format. Therefore, other services can integrate with WordPress and perform create, read, update and delete (CRUD) operations without requiring a local WordPress installation.
Next, you’ll build a simple Python app to see how you can use the WordPress REST API to create, retrieve, update, and delete posts.
Create a new directory for your new simple Python project and name it something like PythonWordPress
:
../PythonWordPress
Now, you’ll create a virtual environment for your project, allowing it to maintain an independent set of installed Python packages, isolating them from your system directories and avoiding version conflicts. Create a virtual environment by executing the venv
command:
python3 -m venv .venv
Now, run a command to activate the .venv virtual environment. This command varies by OS:
- Windows:
.venvScriptsactivate
- Mac/Linux:
.venv/bin/activate
Next, store the configuration related to your WordPress account. To separate the app configuration from your Python code, create a .env file in your project directory, and add these environment variables to the file:
WEBSITE_URL="<>"
API_USERNAME="<>"
API_PASSWORD="<>"
Fortunately, reading the data above from a Python app is easy. You can install the Python-dotenv package so your application can read configuration from the .env file:
pip install python-dotenv
Then, install aiohttp, an asynchronous HTTP client/server for Python:
pip install aiohttp
Now add a file named app.py with the following code:
import asyncio
menu_options = {
1: 'List Posts',
2: 'Retrieve a Post'
}
def print_menu():
for key in menu_options.keys():
print (key, '--', menu_options[key] )
async def main():
while(True):
print_menu()
option = input_number('Enter your choice: ')
#Check what choice was entered and act accordingly
if option == 1:
print('Listing posts...')
elif option == 2:
print('Retrieving a post...')
else:
print('Invalid option. Please enter a number between 1 and 5.')
def input_number(prompt):
while True:
try:
value = int(input(prompt))
except ValueError:
print('Wrong input. Please enter a number ...')
continue
if value < 0:
print("Sorry, your response must not be negative.")
else:
break
return value
def input_text(prompt):
while True:
text = input(prompt)
if len(text) == 0:
print("Text is required.")
continue
else:
break
return text
if __name__=='__main__':
asyncio.run(main())
The code above displays a console menu and asks you to enter a number to choose an option. Next, you’ll expand this project and implement the code that enables you to list all posts and retrieve a specific post using its post id.
Fetching Posts in Code
To interact with WordPress REST API, you must create a new Python file. Create a file named wordpress_api_helper.py with the following content:
import aiohttp
import base64
import os
import json
from dotenv import load_dotenv
load_dotenv()
user=os.getenv("API_USERNAME")
password=os.getenv("API_PASSWORD")
async def get_all_posts():
async with aiohttp.ClientSession(os.getenv("WEBSITE_URL")) as session:
async with session.get("/wp-json/wp/v2/posts") as response:
print("Status:", response.status)
text = await response.text()
wp_posts = json.loads(text)
sorted_wp_posts = sorted(wp_posts, key=lambda p: p['id'])
print("=====================================")
for wp_post in sorted_wp_posts:
print("id:", wp_post['id'])
print("title:", wp_post['title']['rendered'])
print("=====================================")
async def get_post(id):
async with aiohttp.ClientSession(os.getenv("WEBSITE_URL")) as session:
async with session.get(f"/wp-json/wp/v2/posts/{id}") as response:
print("Status:", response.status)
text = await response.text()
wp_post = json.loads(text)
print("=====================================")
print("Post")
print(" id:", wp_post['id'])
print(" title:", wp_post['title']['rendered'])
print(" content:", wp_post['content']['rendered'])
print("=====================================")
Notice the use of the aiohttp library above. Modern languages provide syntax and tools that enable asynchronous programming. This increases the application responsiveness by allowing the program to perform tasks alongside operations like web requests, database operations, and disk I/O. Python offers asyncio as a foundation for its asynchronous programming framework, and the aiohttp library is built on top of asyncio to bring asynchronous access to HTTP Client/Server operations made in Python.
The ClientSession
function above runs asynchronously and returns a session
object, which our program uses to perform an HTTP GET operation against the /wp-json/wp/v2/posts
endpoint. The only difference between a request to retrieve all posts and a request for a specific one is that this last request passes a post id
parameter in the URL route: /wp-json/wp/v2/posts/{id}
.
Now, open the app.py file and add the import
statement:
from wordpress_api_helper import get_all_posts, get_post
Next, modify the main
function to call the get_all_posts
and get_post
functions:
if option == 1:
print('Listing posts...')
await get_all_posts()
elif option == 2:
print('Retrieving a post...')
id = input_number('Enter the post id: ')
await get_post(id)
Then run the app:
python app.py
You’ll then see the application menu:
Now try option 1 to view the list of posts that your Python app retrieves, and option 2 to select a post:
Creating Posts in Code
To create a WordPress post in Python, begin by opening the wordpress_api_helper.py file and add the create_post
function:
async def create_post(title, content):
async with aiohttp.ClientSession(os.getenv("WEBSITE_URL")) as session:
async with session.post(
f"/wp-json/wp/v2/posts?content={content}&title={title}&status=publish"
, auth=aiohttp.BasicAuth(user, password)) as response:
print("Status:", response.status)
text = await response.text()
wp_post = json.loads(text)
post_id = wp_post['id']
print(f'New post created with id: {post_id}')
This code calls the post
function in the session
object, passing the auth
parameter beside the REST API endpoint URL. The auth
object now contains the WordPress user and the password you created using Application Passwords. Now, open the app.py file and add code to import create_post
and the menu:
from wordpress_api_helper import get_all_posts, get_post, create_post
menu_options = {
1: 'List Posts',
2: 'Retrieve a Post',
3: 'Create a Post'
}
Then add a third menu option:
elif option == 3:
print('Creating a post...')
title = input_text('Enter the post title: ')
content = input_text('Enter the post content: ')
await create_post(title, f"{content}")
Then, run the app and try option 3, passing a title and content to create a new post in WordPress:
Choosing option 1 again will return the id and the title of the newly added post:
You can also open your WordPress website to view the new post:
Updating Posts in Code
Open the wordpress_api_helper.py file and add the update_post
function:
async def update_post(id, title, content):
async with aiohttp.ClientSession(os.getenv("WEBSITE_URL")) as session:
async with session.post(
f"/wp-json/wp/v2/posts/{id}?content={content}&title={title}&status=publish"
, auth=aiohttp.BasicAuth(user, password)) as response:
print("Status:", response.status)
text = await response.text()
wp_post = json.loads(text)
post_id = wp_post['id']
print(f'New post created with id: {post_id}')
Then open the app.py file and add code to import update_post
and the menu:
from wordpress_api_helper import get_all_posts, get_post, create_post, update_post
menu_options = {
1: 'List Posts',
2: 'Retrieve a Post',
3: 'Create a Post',
4: 'Update a Post'
}
Then, add a fourth menu option:
elif option == 4:
print('Updating a post...')
id = input_number('Enter the post id: ')
title = input_text('Enter the post title: ')
content = input_text('Enter the post content: ')
await update_post(id, title, f"{content}")
Then run the app and try option 4, passing a post id, title, and content to update an existing post.
Choosing option 2 and passing the updated post id will return the details of the newly added post:
Deleting Posts in Code
You can pass the post id to the REST API to delete a post.
Open the wordpress_api_helper.py file and add the delete_post
function:
async def delete_post(id):
async with aiohttp.ClientSession(os.getenv("WEBSITE_URL")) as session:
async with session.delete(
f"/wp-json/wp/v2/posts/{id}"
, auth=aiohttp.BasicAuth(user, password)) as response:
print("Status:", response.status)
text = await response.text()
wp_post = json.loads(text)
post_id = wp_post['id']
print(f'Post with id {post_id} deleted successfully.')
Now open the app.py file and add code to import delete_post
and the menu:
from wordpress_api_helper import get_all_posts, get_post, create_post, update_post, delete_post
menu_options = {
1: 'List Posts',
2: 'Retrieve a Post',
3: 'Create a Post',
4: 'Update a Post',
5: 'Delete a Post',
}
Then, add a fifth menu option:
elif option == 5:
print('Deleting a post...')
id = input_number('Enter the post id: ')
await delete_post(id)
Now run the app and try option 5, passing an id to delete the existing post in WordPress:
Note: The deleted post may still appear if you run the List Posts option:
To confirm that you’ve deleted the post, wait a few seconds and try the List Posts option again. And that’s it!
Summary
Thanks to the WordPress REST API and Python’s HTTP client libraries, Python apps and WordPress can team up and talk to each other. The benefit of the REST API is that it allows you to operate WordPress remotely from a Python app, where Python’s powerful language enables automated content creation that follows your desired structure and frequency.
DevKinsta makes creating and developing a local WordPress site quick and easy. It provides a local environment for developing WordPress themes and plugins and offers a simplified deployment model courtesy of its Docker-based, self-contained installation model.
What’s your experience working with Python and WordPress?
When ready to expand on that experience, you can read The Complete Guide to WordPress REST API Basics to explore other possibilities.
Leave a Reply