Skip to main content

WPA — WordPress Automation

CI Coverage Python PyPI License: MIT

CLI tool for WordPress automation — manage posts, pages, users, media, comments, and taxonomy terms via the REST API.

Install

pip install wpa

Or install from source:

git clone https://github.com/cadentdev/wpa.git
cd wpa
pip install -e .

Create a site config

wpa site add

This prompts for your WordPress URL, username, application password (hidden), and optional admin path. Configs are stored at ~/.config/wpa/<site-name>/.env with 600 permissions.

WordPress Application Password

  1. Log into wp-admin → Users → Your Profile
  2. Scroll to "Application Passwords"
  3. Enter name: "WPA CLI", click "Add New Application Password"
  4. Copy the generated password (use it during --new-site setup)

Usage

Manage posts

# List posts (with filtering)
wpa post list --site mysite
wpa post list --site mysite --status draft --author 1
wpa post list --site mysite --category 5 --tag 12
wpa post list --site mysite --search "announcement" --orderby date --order desc

# Get a single post
wpa post get 42 --site mysite

# Create a post
wpa post create --site mysite --title "My Post" --content "<p>Hello</p>" --status draft

# Create a post from a markdown file (YAML frontmatter supplies
# title/status/slug; CLI flags override frontmatter)
wpa post create --site mysite --file article.md --author 15 --categories 3,7

# Update a post
wpa post update 42 --site mysite --title "Updated Title" --status publish

# Delete a post (moves to trash; use --force to permanently delete)
wpa post delete 42 --site mysite
wpa post delete 42 --site mysite --force

Manage pages

# List pages
wpa page list --site mysite
wpa page list --site mysite --status publish --parent 10

# Get a single page
wpa page get 42 --site mysite

# Create a page from a markdown file (positional or --file; CLI flags
# like --parent/--author/--status override frontmatter)
wpa page create --site mysite pages/about.md
wpa page create --site mysite --file pages/about.md --parent 12

# Create a page from flags
wpa page create --site mysite --title "About" --content "<p>About us</p>"

# Publish shortcut (equivalent to wpa page create with markdown)
wpa publish pages/your-page.md --site mysite

# Update a page
wpa page update 42 --site mysite --title "New Title" --parent 10

# Delete a page
wpa page delete 42 --site mysite

Manage users

# List all users
wpa user list --site mysite

# List users as JSON, specific fields only
wpa user list --site mysite --format json --fields id,username,email,roles

# Export users to TSV
wpa user list --site mysite --format tsv > users.tsv

# Filter by role or search term
wpa user list --site mysite --role editor
wpa user list --site mysite --search "jane"

# Get a single user
wpa user get 42 --site mysite

# Create a user and email them a one-time set-password link.
# A strong random password is generated (never displayed) when no
# password flag is given — the recommended flow.
wpa user create --site mysite --username jdoe --email jdoe@example.com \
  --role author --send-email

# Create a user with an explicit password read from stdin
printf '%s\n' "$USER_PASSWORD" | wpa user create --site mysite \
  --username jdoe --email jdoe@example.com --role author --password-stdin

# Note: without --send-email, NO notification email is sent — the
# WordPress REST API cannot send one. wpa prints a reminder either way.

# Update a user
wpa user update 42 --site mysite --email newemail@example.com --role editor

# Set a user's role (shortcut for update --role)
wpa user set-role 42 editor --site mysite

# Delete a user (reassign their posts to user 1)
wpa user delete 42 --site mysite --reassign 1

Manage media

# List media items
wpa media list --site mysite
wpa media list --site mysite --media-type image --per-page 50

# List media as JSON, specific fields only
wpa media list --site mysite --format json --fields id,title,source_url,media_type

# Get a single media item
wpa media get 123 --site mysite

# Import (upload) a local file as a WordPress media item
wpa media import /path/to/photo.jpg --site mysite
wpa media import /path/to/photo.jpg --site mysite --title "Cover photo" --alt-text "Team at launch"

# Delete a media item (moves to trash; use --force to permanently delete)
wpa media delete 123 --site mysite
wpa media delete 123 --site mysite --force

Output formats: table (default), json, csv, tsv. Use --fields to select columns (available: id, username, email, first_name, last_name, display_name, roles, registered, url).

Manage comments

# List comments (default shows approved only; filter by status, post, author)
wpa comment list --site mysite
wpa comment list --site mysite --status hold
wpa comment list --site mysite --post 42 --status approved
wpa comment list --site mysite --author-email "reviewer@example.com"

# Count comments per moderation status (one lightweight request each)
wpa comment count --site mysite
wpa comment count --site mysite --status hold      # bare number
wpa comment count --site mysite --format json

# Get a single comment
wpa comment get 123 --site mysite

# Create a comment
wpa comment create --site mysite --post 42 --content "<p>Thanks for posting!</p>" \
    --author-name "Reviewer" --author-email "reviewer@example.com"

# Update comment content or metadata
wpa comment update 123 --site mysite --content "<p>Edited.</p>"

# Moderation shortcuts (wp-cli parity)
wpa comment approve   123 --site mysite
wpa comment unapprove 123 --site mysite   # move back to "hold"
wpa comment spam      123 --site mysite
wpa comment unspam    123 --site mysite   # restore from spam to approved
wpa comment trash     123 --site mysite   # soft delete

# Hard delete (moves to trash by default; --force skips trash)
wpa comment delete 123 --site mysite
wpa comment delete 123 --site mysite --force

Manage taxonomy terms (categories, tags, custom)

# Categories via the alias (pre-sets --taxonomy=category)
wpa category list --site mysite
wpa category list --site mysite --search "news"
wpa category create --site mysite --name "Announcements" --description "Site announcements"
wpa category update 7 --site mysite --description "Major site announcements"
wpa category delete 7 --site mysite   # always permanent; terms cannot be trashed

# Tags via the alias (pre-sets --taxonomy=post_tag)
wpa tag list --site mysite
wpa tag create --site mysite --name "wordpress" --description "Posts about WordPress"
wpa tag delete 12 --site mysite

# Generic term interface for built-in or custom taxonomies
wpa term list --site mysite --taxonomy category
wpa term list --site mysite --taxonomy post_tag
wpa term list --site mysite --taxonomy genre      # custom taxonomy

wpa term get 7 --site mysite --taxonomy category
wpa term create --site mysite --taxonomy post_tag --name "api"
wpa term update 7 --site mysite --taxonomy category --name "Big Announcements"
wpa term delete 7 --site mysite --taxonomy category

Note on delete: The WordPress REST API does not support trashing taxonomy terms, so wpa term delete (and the category / tag aliases) always performs a permanent delete. There is no --force flag — force is implicit.

Output options

All list commands support these output modifiers:

# Output only IDs
wpa post list --site mysite --ids

# Output only the count
wpa post list --site mysite --count

# Output a single field per result
wpa post list --site mysite --field title

# Select specific columns
wpa user list --site mysite --fields id,username,email

# Debug mode (print HTTP request/response details)
wpa post list --site mysite --debug

Site management

wpa site add
wpa site list
wpa --version

Multi-site behavior

Configs --site flag Behavior
0 No Prompts to create a new config
1 No Uses the single config automatically
2+ No Prompts to select from list
Any Yes Uses the named config (error if not found)

Markdown file format

---
title: "Your Page Title"
slug: "your-page-slug"
status: draft
---

Page content in markdown here...
  • title (required): Page title
  • slug (optional): URL slug
  • status (optional): draft (default), publish, pending, or private

Site config format

Each site config is stored at ~/.config/wpa/<name>/.env:

WP_SITE_URL=https://example.com
WP_USER=your-username
WP_APP_PASSWORD=xxxx xxxx xxxx xxxx
WP_ADMIN_PATH=wp-admin
  • WP_ADMIN_PATH is optional (defaults to wp-admin). Override it if your site uses a custom admin URL.
  • The XDG_CONFIG_HOME environment variable is respected if set.

Migration from repo-root .env

If you have an existing .env in the repo root and no XDG configs, the tool will offer to migrate it on first run.

Safety and Security

  • Default status is always draft — never publishes unless frontmatter explicitly says otherwise
  • HTTPS enforced for public addresses — rejects http:// for public URLs; allows HTTP for private/LAN addresses (RFC 1918, localhost, .lan/.local/.test/.internal TLDs) with a warning
  • Credentials in XDG config — stored outside the repo at ~/.config/wpa/ with 600 permissions
  • Password input hidden — uses getpass during interactive setup
  • Status validation — rejects typos and invalid values in frontmatter
  • Site name validation — only alphanumeric characters and hyphens allowed
  • Connection error handling — timeouts and network failures produce clear messages, not tracebacks

Development

pip install -e '.[dev]'
pytest --cov=wpa --cov-report=term-missing

Links

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wpa-0.8.2.tar.gz (61.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wpa-0.8.2-py3-none-any.whl (42.0 kB view details)

Uploaded Python 3

File details

Details for the file wpa-0.8.2.tar.gz.

File metadata

  • Download URL: wpa-0.8.2.tar.gz
  • Upload date:
  • Size: 61.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for wpa-0.8.2.tar.gz
Algorithm Hash digest
SHA256 3c0938dc30728a1b3e6e30b4b866bb67faafdf3873cf1380510f45b69f76a675
MD5 efab09a1f155fe195b6eebabe0b1b71e
BLAKE2b-256 97e4889644623d1aec77087114faeae58c3529ca580471af95cd15ab3c600a20

See more details on using hashes here.

Provenance

The following attestation bundles were made for wpa-0.8.2.tar.gz:

Publisher: publish.yml on cadentdev/wpa

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wpa-0.8.2-py3-none-any.whl.

File metadata

  • Download URL: wpa-0.8.2-py3-none-any.whl
  • Upload date:
  • Size: 42.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for wpa-0.8.2-py3-none-any.whl
Algorithm Hash digest
SHA256 cb6f538ceb6d7668f858bf84ace6ba177bdd4b0b53f15dd4a7c6000e0bd4a904
MD5 77804eac38bf847e1fc95273fa502c51
BLAKE2b-256 04572b5496d66c8c46d1bd2e9e530bc786bd6f946729e6f0312b2035dd972b21

See more details on using hashes here.

Provenance

The following attestation bundles were made for wpa-0.8.2-py3-none-any.whl:

Publisher: publish.yml on cadentdev/wpa

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

This release

0.8.2 This release

2 files

0.8.1

2 files

0.8.0

2 files

0.4.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page