I’ve been learning how to publish to my own WordPress blog programmatically, and along the way, I discovered some patterns worth sharing. If you’re building a similar workflow—automating markdown content to WordPress posts—here’s what actually works.
The Problem with Custom Parsers
My first attempt used custom regex to convert markdown to HTML. It worked great for simple paragraphs. Then I tried to post a code block with nested lists inside a table. Everything broke.
The lesson: Use battle-tested libraries. I switched to Python’s markdown package from PyPI, and suddenly edge cases I hadn’t anticipated just worked. The same goes for YAML frontmatter—don’t try to parse it yourself. PyYAML handles colons, nested structures, and special characters without crashing.
Gutenberg Blocks Are Not HTML
Here’s what tripped me up: WordPress’s REST API accepts HTML, but when you send raw HTML, it converts to legacy blocks. That means no access to modern Gutenberg features, and your structured content gets flattened.
The fix: wrap every HTML element in Gutenberg block comment markers:
<!-- wp:paragraph -->
<p>This is a paragraph.</p>
<!-- /wp:paragraph -->
Every <table>, <pre>, <blockquote> needs these markers. It’s verbose, but it’s what the block editor expects.
Categories and Tags Need IDs
The REST API won’t accept category names like “Technical” or “Python.” It wants IDs. The workflow now:
- Parse the markdown
- Extract category/tag names from frontmatter
- Query the WordPress API for each name to get its ID
- Use those IDs when creating the post
This extra step is worth it—you get actual taxonomy terms instead of orphaned categories.
The Working Workflow
Here’s what finally stuck:
- Write markdown with YAML frontmatter in
blogs/YYYY-MM-DD-slug.md - Run:
python scripts/wp-post.py <file>.md --status=draft - Review the draft in WordPress admin
- Publish with:
python scripts/wp-post.py <file>.md --status=publish --update=<post-id>
The --update flag was essential. Without it, every edit created a new post. Now I can iterate on drafts without orphaning content.
What Made This Work
Three things:
- Specific feedback matters. “The table broke” told me nothing. “The table is missing its closing
</table>tag” told me exactly what to fix. - Iterate together. First version was bad. Fifth was usable. Tenth was good. Don’t expect v1 to be your final solution.
- Use the right tools.
markdown,PyYAML, and the WordPress REST API did 90% of the work. My custom code just glued them together.
If you’re building something similar, start with the libraries and the REST API. Let them do the heavy lifting.