AI Blog Bridge
Publish structured blog posts to WordPress over the REST API: Gutenberg blocks, images, SEO meta, JSON-LD schema, and an on-page audit that blocks anything broken from shipping.
by Christian Monge · github.com/christian259200/wp-ai-blog-bridge · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/christian259200/wp-ai-blog-bridge/archive/refs/heads/main.zipReadme
AI Blog Bridge
A WordPress plugin that receives blog posts over the REST API and publishes them as proper Gutenberg content: headings with anchors, tables, key takeaways, a table of contents, embedded video, internal links with tooltips, SEO meta, JSON-LD schema, and an on-page audit that runs on every publish.
Write wherever you want, with whatever tool you want. Send it here. Get a finished post.
Why
Publishing AI-written content to WordPress usually goes one of two ways: you paste into the editor and lose an hour on formatting, or you push raw HTML through the REST API and end up with a wall of paragraphs, no schema, no internal links, and no idea whether any of it is good.
This sits in between. You send structure, it renders structure, and it tells you what is wrong before you find out from Search Console three months later.
What it does
Content
- Markdown to Gutenberg blocks: headings with anchors, lists, tables, images, quotes
- YouTube URLs become privacy-friendly
youtube-nocookieembeds, lazy loaded - Key takeaways box, automatic table of contents, sources section
- FAQ section plus
FAQPageJSON-LD BlogPostingschema, canonical, Open Graph and Twitter Card tags- Internal links injected into the body by anchor text, with
titletooltips - Author byline, freshness stamp, cornerstone flag
Quality control
An audit runs on every publish and returns errors, warnings and notes:
| Check | Threshold |
|---|---|
| Meta title | 30-70 chars, contains the focus keyword |
| Meta description | 120-160 chars, contains the focus keyword |
| First paragraph | contains the focus keyword |
| Headings | exactly one H1, 3-10 H2, keyword whole in one of them |
| Length | 300 words minimum |
| Internal links | 3-10, varied anchor text |
| Citations | at least one outbound link |
| Images | alt text required, title recommended |
| Structure | a list or a table, a question heading or an FAQ |
| Semantic cues | at least one, so language models know what to quote |
| URL | under 100 chars, at most two directories |
Keyword matching folds diacritics, so que es serigrafia matches
qué es serigrafía. That is not cosmetic: without it every accented language
fails every keyword check.
SEO plugin support
Writes meta for Rank Math, Yoast and SEOPress, and detects which one is active.
When one of them is active, the plugin does not publish its own
BlogPosting node, because they already publish one and two Article nodes for
the same URL is a defect rather than extra coverage. It publishes the FAQPage
node either way, since no SEO plugin builds that from this payload. Change it
under Settings -> AI Blog Bridge -> BlogPosting schema.
Install
- Download the zip from Releases, or build it with
python build-zip.py - Upload at
Plugins → Add New → Upload Plugin - Activate
- Open
Settings → AI Blog Bridgeand copy the API token
Publish
cd client
cp .env.example .env # fill in three lines
pip install -r requirements.txt
python publish.py post.md
Authentication works two ways: the plugin's API token in an X-ABB-Token
header, or a standard WordPress Application Password over HTTP Basic. Prefer the
Application Password unless a security plugin has disabled them.
The endpoints require an authenticated account with publish_posts. Nothing
here is open to the public.
Post format
Markdown with YAML frontmatter. Everything except title, slug and content is optional.
---
title: "What is DTF printing and when it beats screen printing"
slug: what-is-dtf-printing
status: publish
description: "What is DTF printing, how the transfer works, which fabrics it suits and how it compares with screen printing."
focus_keyword: "what is dtf printing"
categories: [Blog]
tags: [dtf, apparel]
toc: true
key_takeaways:
- "DTF prints onto film and transfers with heat, so fabric type stops mattering."
internal_links:
- anchor: "screen printing"
url: https://example.com/screen-printing/
title: "Read the full screen printing guide"
faq:
- question: "What does DTF stand for?"
answer: "Direct to film."
---
What is DTF printing: a technique that prints a design onto a transparent film
and transfers it to the garment with heat and pressure.
Full field reference in examples/.
Useful commands
python publish.py post.md --dry-run # inspect the payload, send nothing
python publish.py posts/ --glob "*.md" # a folder
python publish.py --ping # check the connection
python publish.py --audit 123 # re-audit a published post
python publish.py posts/ --strict # fail the run on any warning
python publish.py --stale 90 # list posts not updated in 90 days
Publishing a batch: one at a time, with a pause. Shared hosting throttles bursts and answers with an HTML error page instead of JSON.
for f in posts/*.md; do python publish.py "$f" --strict; sleep 20; done
--strict matters more in a batch than anywhere else. A dropped internal link
is only a warning, and a warning in the middle of a long run scrolls past. Two
posts in a batch of seven shipped with a missing link before this flag existed.
About the CSS, read this before installing on a client site
assets/abb-frontend.css does two different jobs, and they are not equally
portable.
Portable. Styling for the blocks the plugin creates: the takeaways box, the
table of contents, tables, video embeds, the byline, the sources list. These are
abb-* classes and work on any theme.
Not portable. Layout fixes written for the XStore theme: hiding the sidebar on posts, forcing the archive into a card grid, removing the share row and the breadcrumb banner. Roughly half the file. On another theme those rules match nothing, which is harmless, but you will not get the same layout.
If you use a different theme, keep the abb-* sections and replace the theme
ones. Pull requests that generalise this are very welcome.
Optional: meta title and description generator
A metabox in the post editor generates a meta title and description with OpenAI. It stays off unless you configure a key.
Put the key in wp-config.php, not in the database:
define( 'ABB_OPENAI_KEY', 'sk-...' );
The rest of the plugin does not need it. Content generation happens on your own machine, not on the server.
Security
- REST endpoints require an authenticated user with
publish_posts - The shared token is compared with
hash_equals, which resists timing attacks - No credential is stored in the plugin's code
- The OpenAI key is read from
wp-config.phpfirst, the database only as fallback - Uninstalling removes the plugin's own options and log table
If you find a security problem, open an issue.
Building the zip
python build-zip.py
Do not use PowerShell's Compress-Archive. It writes archive paths with
backslashes and WordPress rejects the result with "The plugin file does not
exist." The build script asserts forward slashes and refuses to ship a .env.
The folder inside the zip must match the installed slug. Packaging under a different name makes WordPress install a second copy beside the running one, and two active copies redeclaring the same classes take the site down.
Requirements
- WordPress 6.0+, PHP 7.4+
- Python 3.9+ with
requestsandpyyaml, for the publishing client
Companion skill
claude-wp-blog-skill is a Claude Code skill that writes the posts this plugin publishes: keyword selection that avoids cannibalisation, video research, and a local audit that mirrors this one. It also works standalone, without this plugin.
Licence
GPL-2.0-or-later, as WordPress requires.