wordpress-rest-curl/README.md
2026-07-30 19:09:50 +02:00

3.4 KiB

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.sh instead of the default config.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 for STATUS=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's date_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 in CATEGORIES.
  • --tag NAME — same as --category, for tags (repeatable). Merged with TAGS.

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