| lib | ||
| tests | ||
| .gitignore | ||
| config.sh.template | ||
| LICENSE | ||
| post.sh | ||
| README.md | ||
| site.sh.template | ||
| terms.sh | ||
wordpress-rest-curl
CLI tool for submitting posts to WordPress through its REST API using curl.
Install
WordPress
Application Passwords are built into WordPress core since 5.6 — no plugin needed. Go to Users → Profile → Application Passwords on your site, generate a new password for your user, and note it down. Your site must be served over HTTPS for this to work.
Local shell
Make sure you have curl and jq installed. If you want to enter text in
markdown format, install python-markdown2 (pip install markdown2 or your
distro's python3-markdown2 package).
Copy config.sh.template to ~/.config/wordpress-rest-curl/config.sh and
fill in whatever's shared across sites (TRANSFORM, EDITOR, STATUS, ...).
config.sh is always loaded first if it exists — it doesn't need USER,
PASSWORD, or SERVER filled in.
If you post to more than one WordPress instance, copy site.sh.template to
~/.config/wordpress-rest-curl/NAME.sh once per site, e.g.
blog-example-com.sh and news-example-com.sh, containing just the per-instance bits
(USER, PASSWORD, SERVER, and any CATEGORIES/TAGS that differ). Pick
one at run time with --site NAME (see Options below) — its values are
layered on top of config.sh, overriding only what it sets.
Copy the scripts to e.g. /usr/local/bin/wordpress-post and /usr/local/bin/wordpress-terms so that you can run them from anywhere. Keep them in the same directory as lib/config.sh, or copy that directory alongside them — both scripts source it.
Usage
- Run
post.sh - Text editor opens. Type your post, save it.
- Confirm the displayed post by pressing enter.
- Done!
Options
--site NAME— use~/.config/wordpress-rest-curl/NAME.shinstead of the defaultconfig.sh. Useful when posting to multiple WordPress instances with different credentials.--file PATH— use PATH as the post content instead of opening$EDITOR.--publish— shortcut forSTATUS=publish, posts live immediately.--edit POST_ID— update an existing post instead of creating a new one.--date DATE— backdate the post to DATE, an ISO 8601 UTC timestamp (e.g.2026-07-27T10:00:00Z, matching a Gitea commit date) — sent as the post'sdate_gmt.--category NAME— attach category NAME by name (repeatable). Looked up on confirm; created automatically if it doesn't exist yet. Merged with any numeric IDs already inCATEGORIES.--tag NAME— same as--category, for tags (repeatable). Merged withTAGS.
Categories and tags
post.sh's CATEGORIES/TAGS config vars are numeric IDs, but --category/--tag (see Options) let you pass names directly on the command line — no separate lookup step needed, they're resolved (or created if missing) as part of running post.sh.
terms.sh is for inspecting or managing terms directly (same --site NAME config as post.sh):
./terms.sh --list # id, name, slug, count — one per line
./terms.sh --type tag --list # tags instead of categories
./terms.sh --find "Hardware" # prints the id, or exits 1 if missing
./terms.sh --create "Hardware" # creates it, prints the new id
Testing
tests/run_tests.sh runs post.sh and terms.sh against a local mock of the
WordPress REST API (tests/mock_wp_server.py) — no real WordPress install or
network access needed. Requires the same dependencies as the scripts
themselves (curl, jq, python3-markdown2).