WordPress API
Interact with WordPress sites via the REST API. Create, update, and manage posts, pages, media, and more on any WordPress installation.
Quick Start
Single Site (Direct)
# Update a post
python3 scripts/update_post.py \
--url "https://example.com" \
--username "admin" \
--app-password "xxxx xxxx xxxx xxxx" \
--post-id 123 \
--content "New content here" \
--status "publish"
Multi-Site (CLI Wrapper) ⭐
# Setup: Copy config template and add your sites
cp config/sites.example.json config/sites.json
# Edit config/sites.json with your sites
# List configured sites
./wp.sh --list-sites
# Update post on specific site
./wp.sh digitizer-studio update-post --id 123 --content "New content"
# Run on all sites in a group
./wp.sh digitizer update-post --id 456 --status "publish"
# Batch update across multiple sites
python3 scripts/batch_update.py --group digitizer --post-ids 123,456 --status "publish"
Authentication
WordPress REST API supports two authentication methods:
1. Application Password (Recommended) ⭐
Setup:
- Go to:
https://yoursite.com/wp-admin/profile.php - Scroll to "Application Passwords"
- Name: "OpenClaw API" (or any name)
- Click "Add New Application Password"
- Copy the password (shows only once!)
Format: xxxx xxxx xxxx xxxx xxxx xxxx (24 characters with spaces)
Usage: Pass as --app-password to scripts or set WP_APP_PASSWORD environment variable
2. Basic Auth (Legacy)
Requires Basic Auth plugin. Less secure, not recommended for production.
Environment Variables
Set these to avoid passing credentials every time:
export WP_URL="https://example.com"
export WP_USERNAME="admin"
export WP_APP_PASSWORD="xxxx xxxx xxxx xxxx xxxx xxxx"
Multi-Site Management
Configuration
- Copy the example config:
cp config/sites.example.json config/sites.json
- Edit
config/sites.jsonwith your sites:
{
"sites": {
"my-site": {
"url": "https://mysite.com",
"username": "admin",
"app_password": "xxxx xxxx xxxx",
"description": "My WordPress site"
}
},
"groups": {
"all": ["my-site"]
}
}
CLI Wrapper (./wp.sh)
# List sites
./wp.sh --list-sites
./wp.sh --list-groups
# Single site operations
./wp.sh my-site update-post --id 123 --content "..."
./wp.sh my-site create-post --title "..." --content "..."
./wp.sh my-site get-post --id 123
./wp.sh my-site list-posts --per-page 10
# Group operations (runs on all sites in group)
./wp.sh all update-post --id 123 --status "publish"
Batch Operations
# Update multiple posts across multiple sites
python3 scripts/batch_update.py \
--group digitizer \
--post-ids 123,456,789 \
--status "publish"
# Update meta field on all sites
python3 scripts/batch_update.py \
--sites all \
--post-ids 100 \
--meta-key "seo_score" \
--meta-value "95"
# Dry run (see what would happen)
python3 scripts/batch_update.py \
--group all \
--post-ids 123 \
--status "draft" \
--dry-run
Individual Scripts
All scripts support both command-line arguments and environment variables.
Update Post
python3 scripts/update_post.py \
--post-id 123 \
--content "Updated content" \
--title "New Title" \
--status "publish"
Supports:
- Content (HTML or Gutenberg blocks)
- Title
- Status (publish, draft, pending, private)
- Featured image ID
- Custom fields (meta)
Create Post
python3 scripts/create_post.py \
--title "New Post" \
--content "Post content" \
--status "draft"
Get Post
python3 scripts/get_post.py --post-id 123
Returns full post data as JSON.
List Posts
python3 scripts/list_posts.py --per-page 10 --status "publish"
Options:
--per-page N- Posts per page (default: 10, max: 100)--page N- Page number--status STATUS- Filter by status (publish, draft, pending, private)--author ID- Filter by author
Plugin Integration
Detect Plugins
Automatically detect installed WordPress plugins (ACF, Rank Math, Yoast, JetEngine):
python3 scripts/detect_plugins.py
python3 scripts/detect_plugins.py --verbose
ACF (Advanced Custom Fields)
Read and write ACF fields with automatic fallback to postmeta:
# Get all ACF fields
python3 scripts/acf_fields.py --post-id 123
# Get specific field
python3 scripts/acf_fields.py --post-id 123 --field my_field
# Set fields from JSON
python3 scripts/acf_fields.py --post-id 123 --set '{"field1": "value1", "field2": "value2"}'
# Set single field
python3 scripts/acf_fields.py --post-id 123 --field my_field --value "my value"
Features:
- Automatic REST API detection with postmeta fallback
- Support for simple, repeater, and group fields
- JSON input/output for complex field structures
SEO Meta (Rank Math & Yoast)
Read and write SEO meta fields with auto-detection:
# Auto-detect plugin and get meta
python3 scripts/seo_meta.py --post-id 123
# Detect which SEO plugin is active
python3 scripts/seo_meta.py --post-id 123 --detect
# Get specific plugin's meta
python3 scripts/seo_meta.py --post-id 123 --plugin rankmath
python3 scripts/seo_meta.py --post-id 123 --plugin yoast
# Set SEO meta
python3 scripts/seo_meta.py --post-id 123 --set '{"title": "SEO Title", "description": "Meta description"}'
# Set with specific plugin
python3 scripts/seo_meta.py --post-id 123 --plugin rankmath --set '{"focus_keyword": "keyword"}'
Supported fields:
- Rank Math: title, description, focus_keyword, robots, canonical_url, schema
- Yoast: title, description, focus_keyword, canonical, meta_robots_noindex, meta_robots_nofollow
JetEngine Fields
Read and write JetEngine custom fields (stored as postmeta):
# Get all JetEngine fields
python3 scripts/jetengine_fields.py --post-id 123
# Get specific field
python3 scripts/jetengine_fields.py --post-id 123 --field my_field
# Set fields from JSON
python3 scripts/jetengine_fields.py --post-id 123 --set '{"field1": "value1", "field2": "value2"}'
# Set single field
python3 scripts/jetengine_fields.py --post-id 123 --field my_field --value "my value"
Elementor Content
Manage Elementor page builder content via _elementor_data meta field:
# Get Elementor data for a page
python3 scripts/elementor_content.py --post-id 123 --action get
# Update specific widget content
python3 scripts/elementor_content.py \
--post-id 123 \
--action update \
--widget-id "abc123" \
--content "New heading text"
Supported widget types:
- Headings (
heading) - Text Editor (
text-editor) - Buttons (
button) - Generic fallback for other widgets
How it works:
- Gets page data with
_elementor_datameta field - Parses JSON structure
- Finds widget by ID (recursively through nested elements)
- Updates content based on widget type
- Saves back to WordPress
Media Upload
Upload images and files to WordPress media library:
# Upload local file
python3 scripts/upload_media.py \
--file "/path/to/image.jpg" \
--title "My Image" \
--alt-text "Description for accessibility"
# Upload from URL
python3 scripts/upload_media.py \
--file "https://example.com/image.jpg" \
--title "Remote Image"
# Upload and set as featured image
python3 scripts/upload_media.py \
--file "image.jpg" \
--set-featured \
--post-id 123
Features:
- Supports local files and URLs
- Auto-detects MIME type
- Returns media ID for further operations
- Can set as featured image in one call
WooCommerce Products
Manage WooCommerce products via REST API v3:
# List products
python3 scripts/woo_products.py \
--consumer-key "ck_..." \
--consumer-secret "cs_..." \
--action list \
--per-page 10
# Get single product
python3 scripts/woo_products.py \
--consumer-key "ck_..." \
--consumer-secret "cs_..." \
--action get \
--product-id 456
# Create product
python3 scripts/woo_products.py \
--consumer-key "ck_..." \
--consumer-secret "cs_..." \
--action create \
--title "New Product" \
--price "29.99" \
--description "Product description" \
--status "publish"
# Update product
python3 scripts/woo_products.py \
--consumer-key "ck_..." \
--consumer-secret "cs_..." \
--action update \
--product-id 456 \
--price "39.99" \
--status "publish"
Authentication:
- Requires WooCommerce Consumer Key and Consumer Secret
- Get from: WooCommerce → Settings → Advanced → REST API
- Environment variables:
WC_CONSUMER_KEY,WC_CONSUMER_SECRET
Supported fields:
--title/--name- Product name--price- Regular price--sale-price- Sale price--description- Full description--short-description- Short description--status- publish, draft, pending, private--sku- Stock keeping unit--stock-quantity- Stock amount--manage-stock- Enable stock management
Gutenberg Blocks
WordPress uses Gutenberg block format for content. See references/gutenberg-blocks.md for details.
Example block:
<!-- wp:paragraph -->
<p>This is a paragraph.</p>
<!-- /wp:paragraph -->
API Reference
See references/api-reference.md for complete WordPress REST API documentation.
Common endpoints:
/wp-json/wp/v2/posts- Posts/wp-json/wp/v2/pages- Pages/wp-json/wp/v2/media- Media/wp-json/wp/v2/users- Users/wp-json/wp/v2/categories- Categories/wp-json/wp/v2/tags- Tags
Error Handling
All scripts return:
- Exit code 0 on success
- Exit code 1 on error
- JSON output to stdout
- Error messages to stderr
Security
- ✅ DO: Use Application Passwords
- ✅ DO: Use HTTPS only
- ✅ DO: Store credentials in environment variables
- ❌ DON'T: Hardcode passwords in scripts
- ❌ DON'T: Use Basic Auth in production
- ❌ DON'T: Commit credentials to git
Limitations
- Requires WordPress 4.7+ (REST API built-in)
- Requires Application Passwords plugin for WordPress <5.6
- Some endpoints require specific user permissions
- Rate limiting depends on host configuration