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.

DevKinsta's downloading page screenshot.
DevKinsta installation page.

When you run the DevKinsta installer, Docker starts initializing immediately:

DevKinsta's starting Docker screen.
DevKinsta starts Docker locally.

Next, choose New WordPress site from the Create new Site menu:

DevKinsta's create new WordPress site screen.
DevKinsta’s Create New Site menu.

Now the DevKinsta installer requires you to create the credentials for the WordPress admin account:

DevKinsta's new WordPress site creation screen.
DevKinsta displaying the New WordPress site form.

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).

DevKinsta's WordPress site info screen.
DevKinsta’s Site info panel.

Next, you need to enable SSL and HTTPS for your website. This improves the security of your website through an SSL certificate.

Enabling DevKinsta's SSL and HTTPS option.
DevKinsta’s “SSL and HTTPS” option.

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:

The home page of your local WordPress site.
WordPress homepage.

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:

WordPress admin dashboard login screen.
WordPress login form.

Once you’re logged in, you’ll see WordPress Dashboard:

Welcome to WordPress admin screen.
WordPress Dashboard page.

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:

Installing and activating WordPress Application Passwords plugin.
Application Passwords plugin for WordPress.

To begin creating your application password, start by expanding the Users menu and clicking All Users:

Expanded Users WordPress menu.
Expanded Users menu.

Now, click Edit below your admin user name:

Click the "Edit" button below your WordPress user under the Users menu.
WP-Admin WordPress interface.

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:

Application Passwords plugin dashboard.
Application Password page.

Click Add New Application Password so WordPress can generate a random 24-character  password for you:

The new password generated by Application Passwords plugin.
New Application Password page.

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:

Changing WordPress permalink settings.

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:

Starting the Python app connected to WordPress.
Python application menu.

Now try option 1 to view the list of posts that your Python app retrieves, and option 2 to select a post:

Try option 1 to view the list of posts that your Python app retrieves, and option 2 to select a post.
Python app showing the posts list and a single user-selected 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:

Creating a WordPress post with Python.
Python app displaying newly created WordPress post.

Choosing option 1 again will return the id and the title of the newly added post:

Returning the id and the title of the newly added post.
Python app returning the new post’s title and id.

You can also open your WordPress website to view the new post:

The newly created post in the browser.
Browser image of the new WordPress 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.

Updating a WordPress post.
Python app showing the updated menu.

Choosing option 2 and passing the updated post id will return the details of the newly added post:

Returning the details of the newly updated post.
Python app showing the updated 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:

Deleting a WordPress post with the Python app.
Python app showing deletion of the selected post.

Note: The deleted post may still appear if you run the List Posts option:

Python app listing posts.
Python app showing original post list.

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.

Salman Ravoof

Salman Ravoof is a self-taught web developer, writer, creator, and a huge admirer of Free and Open Source Software (FOSS). Besides tech, he's excited by science, philosophy, photography, arts, cats, and food. Learn more about him on his website, and connect with Salman on Twitter.