WordPress
Connect WordPress.com or a self-hosted WordPress site, then create, schedule, update and delete its articles through the Blogs API.
Connect a WordPress.com or self-hosted WordPress site and manage its articles with the Blogs API. A connected account uses platform: "wordpress" and represents one site with one account-scoped blog. WordPress is a blog integration: the account never appears as a target in POST /v1/posts and has no Zernio analytics or inbox.
Quick reference
| Property | Value |
|---|---|
| Platform value | wordpress |
| What it manages | One site's standard posts through one account-scoped blog |
| WordPress.com auth | OAuth 2.0 |
| Self-hosted auth | Username and WordPress Application Password |
| Social posting | No |
| Analytics | No |
| Scheduling | Yes, native (publishDate; no Zernio queue) |
| Drafts | Yes (isPublished: false) |
| Article body | HTML (bodyHtml) |
| Tags | Names; missing tags are created on the site |
| Author | Numeric WordPress user ID encoded as a string |
| Featured image | Public JPEG, PNG, WebP or GIF URL; up to 10 MB |
| SEO plugin fields | Not supported |
| Pagination | Cursor (limit 1 to 50, default 20, plus nextCursor) |
Before you start
Choose one connection path for each site:
- WordPress.com: the site owner approves OAuth and selects a site on WordPress.com.
- Self-hosted WordPress: the site owner supplies an HTTPS site URL, their WordPress login username and an Application Password created in WordPress admin.
The authenticated WordPress user must be able to publish posts and upload files. Zernio verifies both capabilities before it saves the connection. A role that can only write drafts is insufficient.
The account represents the selected site, not the person who authorized it. Connect another site as another account. Site and article data stays on WordPress; Zernio maps it into the common Blogs API response shape.
Connect WordPress.com
Call GET /v1/connect/wordpress with profileId and redirect_url, then send the site owner's browser to the returned authUrl:
curl "https://zernio.com/api/v1/connect/wordpress?profileId=66a1f0c2a4b9d3e8f1a2b3c4&redirect_url=https%3A%2F%2Fmyapp.com%2Fconnected" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Response (200):
{
"authUrl": "https://public-api.wordpress.com/oauth2/authorize?...",
"state": "..."
}Redirect the browser to authUrl. WordPress.com handles consent and site selection. Its callback connects the selected site and sends the browser to your redirect_url. The returned WordPress.com numeric site ID becomes that account's blogId; read it through List blogs instead of constructing it.
redirect_url must be a safe absolute destination. Keep the returned state opaque; Zernio validates it at the callback. Connecting the same profile to the same site again refreshes the connection in place rather than creating another account slot.
WordPress.com OAuth scopes
Zernio uses WordPress.com's default single-site authorization by omitting the scope parameter. The consent grants access to the selected site; Zernio uses that connection for articles, featured images and tags. It does not request the explicit global scope that grants access across all your sites. Granular scopes such as posts and media do not support the /wp/v2 API used by this integration. See the official WordPress.com OAuth documentation for the upstream scope model.
Connect self-hosted WordPress
In WordPress admin, the site owner opens their user profile and creates an Application Password for Zernio. Send the generated password with the login username to POST /v1/connect/wordpress/token:
curl -X POST "https://zernio.com/api/v1/connect/wordpress/token" \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "66a1f0c2a4b9d3e8f1a2b3c4",
"siteUrl": "https://example.com",
"username": "editor@example.com",
"applicationPassword": "xxxx xxxx xxxx xxxx xxxx xxxx"
}'Response (200):
{
"account": {
"_id": "66b2e19d8c3f5a7e9d0b1c2d",
"platform": "wordpress",
"username": "example.com",
"displayName": "Example Press",
"profileId": "66a1f0c2a4b9d3e8f1a2b3c4"
}
}siteUrl must use HTTPS and identify the public site root. Do not include /wp-json, a REST endpoint, a username, a password or other credentials in the URL. Zernio safely discovers the site's REST API, rejects private or local network targets, verifies the credentials and checks publish/upload capabilities before persisting the account.
An Application Password is separate from the user's interactive WordPress password. Treat it as a secret and revoke it from WordPress admin when it is no longer needed. Revoking it immediately stops API access; reconnect the account with a new Application Password to restore it.
Publish articles
WordPress uses the same article endpoints as Shopify. The differences are the single-blog model, WordPress author IDs and tag/media handling described below.
List the site blog
Always start with List blogs:
curl "https://zernio.com/api/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/blogs" \
-H "Authorization: Bearer $ZERNIO_API_KEY"A self-hosted account returns exactly one blog with id: "1":
{
"platform": "wordpress",
"blogs": [
{
"id": "1",
"platform": "wordpress",
"title": "Example Press",
"handle": "example.com"
}
],
"nextCursor": null
}The value "1" is scoped to the connected account. It does not claim that the WordPress database's internal blog ID is 1. A WordPress.com account uses its numeric WordPress.com site ID instead. Code that supports both connection types should use blogs[0].id from this response.
Fetch the same site metadata with Get a blog:
curl "https://zernio.com/api/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/blogs/1" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Create a draft
Call Create a blog article with a title. New WordPress articles default to draft, so this minimal request does not publish:
curl -X POST "https://zernio.com/api/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/blogs/1/articles" \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Autumn collection preview",
"bodyHtml": "<p>The first pieces land next month.</p>",
"handle": "autumn-collection-preview",
"excerpt": "An early look at what is arriving this September.",
"tags": ["autumn", "new arrivals"],
"author": "42",
"image": {
"url": "https://cdn.example.com/autumn.jpg",
"altText": "Wool coats on a rail"
},
"isPublished": false
}'WordPress resolves existing tags by name case-insensitively and creates a missing tag on the site. You can send up to 100 non-empty tag names. Creating a missing term depends on the connected user's WordPress permissions. author is the numeric ID of a WordPress user who can author the post, encoded as a JSON string. It is not a login, email or display name; assigning another user as author can require a higher WordPress role.
For image.url, WordPress downloads the file into that site's media library and sets the resulting attachment as the featured image. The URL must be publicly reachable and return a JPEG, PNG, WebP or GIF no larger than 10 MB. altText becomes the attachment's alternative text. Omitting image during a later PATCH preserves the existing featured image; this API has no featured-image removal sentinel.
Response (201):
{
"platform": "wordpress",
"article": {
"id": "1842",
"blogId": "1",
"platform": "wordpress",
"title": "Autumn collection preview",
"bodyHtml": "<p>The first pieces land next month.</p>",
"handle": "autumn-collection-preview",
"tags": ["autumn", "new arrivals"],
"author": "42",
"excerpt": "An early look at what is arriving this September.",
"image": {
"url": "https://example.com/wp-content/uploads/2026/09/autumn.jpg",
"altText": "Wool coats on a rail"
},
"status": "draft",
"isPublished": false,
"publishDate": null,
"publishedAt": null,
"createdAt": null,
"updatedAt": "2026-09-16T09:30:00Z"
}
}bodyHtml is raw article HTML. Zernio does not provide a visual editor or transform Markdown into HTML.
Publish now
Set isPublished: true without a future publishDate:
{
"title": "Autumn collection preview",
"bodyHtml": "<p>The first pieces are now available.</p>",
"isPublished": true
}The response reports status: "publish", isPublished: true and the WordPress publication time in publishedAt.
Schedule on WordPress
Send a future ISO 8601 publishDate to schedule publication natively:
{
"title": "Autumn collection preview",
"bodyHtml": "<p>The first pieces land next month.</p>",
"publishDate": "2027-01-01T12:00:00+01:00"
}The response reports status: "future", isPublished: false and the scheduled instant in publishDate. WordPress, not Zernio's queue, publishes the article. The site must run WordPress scheduled jobs correctly; Zernio does not add a second scheduler or force publication after the requested time.
isPublished: false explicitly requests a draft and wins when it is sent alongside a future publishDate. Omit isPublished when scheduling. You cannot write WordPress's raw status field through this API.
List and get articles
List blog articles includes every publish, future, draft, pending and private article visible to the connected user. It is cursor-paginated: pass limit from 1 to 50 and then reuse the opaque nextCursor exactly as returned.
curl "https://zernio.com/api/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/blogs/1/articles?limit=20" \
-H "Authorization: Bearer $ZERNIO_API_KEY"{
"platform": "wordpress",
"articles": [
{
"id": "1842",
"blogId": "1",
"platform": "wordpress",
"title": "Autumn collection preview",
"status": "draft",
"isPublished": false,
"publishDate": null
}
],
"nextCursor": "wp:2"
}Get one article with Get a blog article:
curl "https://zernio.com/api/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/blogs/1/articles/1842" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Article IDs are WordPress post IDs represented as strings. Read them from list/create responses rather than constructing them.
Update an article
Update a blog article is a partial update. Omitted fields keep their current value:
curl -X PATCH "https://zernio.com/api/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/blogs/1/articles/1842" \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Autumn collection is here",
"tags": ["autumn", "available now"],
"isPublished": true
}'tags replaces the full tag list; an empty array clears it. Empty bodyHtml and excerpt strings clear those fields. Sending isPublished: false moves a published or scheduled article back to draft. Omitting both isPublished and publishDate preserves its current WordPress status and publication date.
Delete an article
Delete a blog article permanently deletes the article on WordPress:
curl -X DELETE "https://zernio.com/api/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/blogs/1/articles/1842" \
-H "Authorization: Bearer $ZERNIO_API_KEY"A successful delete returns 204 with no response body. Zernio stores no copy to restore. WordPress retains the article's media attachments and tags so other content can keep using them.
Supported operations
| Operation | Endpoint |
|---|---|
| List blogs | GET /v1/accounts/{accountId}/blogs |
| Get a blog | GET /v1/accounts/{accountId}/blogs/{blogId} |
| List articles | GET /v1/accounts/{accountId}/blogs/{blogId}/articles |
| Create an article | POST /v1/accounts/{accountId}/blogs/{blogId}/articles |
| Get an article | GET /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId} |
| Update an article | PATCH /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId} |
| Delete an article | DELETE /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId} |
Article fields
There is no platformSpecificData for WordPress because a WordPress site is not a POST /v1/posts target.
| Field | Type | Behavior |
|---|---|---|
title | string | Required on create. |
bodyHtml | string | Raw article HTML. |
handle | string | WordPress post slug. Generated from the title when omitted. |
excerpt | string | Short summary stored in the WordPress excerpt field. |
tags | string[] | Tag names. The complete list replaces existing tags; missing names are created. |
author | string | Numeric WordPress user ID encoded as a string. |
image | {url, altText?} | Public image downloaded into the site's media library and set as the featured image. |
isPublished | boolean | false creates or restores a draft; true publishes now unless a future date schedules it. |
publishDate | datetime | ISO 8601 with offset or Z; a future value schedules publication on WordPress. |
Responses also include WordPress's normalized status: publish, future, draft, pending or private. This field is read-only. isPublished is true only for publish; publishDate reports a scheduled future time when applicable.
The shared Blogs API also exposes seo for Shopify. WordPress rejects seo explicitly because core WordPress has no portable SEO-write contract and plugin fields differ. Zernio does not silently discard it. Use a plugin's own API outside Zernio if you need Yoast, Rank Math or another plugin's metadata.
What you cannot do
The WordPress connection does not expose:
- Social posts through
POST /v1/posts - Analytics, comments, DMs or the Inbox API
- Ads or social account insights
- Creating a site or another blog
- Renaming or deleting the connected site/blog through the Blogs API
- SEO plugin fields
- Custom post types, pages, categories or taxonomies other than article tags
- A visual editor or Markdown conversion
- Removing an existing featured image through a PATCH sentinel
POST /v1/accounts/{accountId}/blogs, PATCH /v1/accounts/{accountId}/blogs/{blogId} and DELETE /v1/accounts/{accountId}/blogs/{blogId} return 405 for WordPress. Those operations remain available to platforms that support blog management.
Common errors
| Error | Cause | Fix |
|---|---|---|
400 on self-hosted connect | siteUrl is not HTTPS, includes credentials, resolves to a private/local address or does not expose a usable WordPress REST API | Pass the public HTTPS site root without credentials or /wp-json. |
400 on an article request | A field is invalid, author is not a numeric user ID, or seo was sent | Correct the named field. Remove seo; SEO plugin writes are unsupported. |
401 or 403 on self-hosted connect | The username/Application Password is invalid, revoked or lacks publish/upload capabilities | Create a new Application Password for a user that can publish posts and upload files. |
403 on an article request | The connected WordPress user cannot perform that action, such as assigning another author or creating a tag | Restore the user's WordPress role/capabilities or reconnect with another user. |
404 blog_not_found | blogId does not identify this account's single site blog | Call List blogs and use its only returned ID. Self-hosted sites use 1. |
404 blog_article_not_found | The post was deleted or belongs to another connected site | List articles on this account and blog to find a current ID. |
405 on a blog mutation | WordPress site/blog creation, rename and deletion are unsupported | Manage the site itself in WordPress; use article CRUD through Zernio. |
429 | Zernio or WordPress rate limited the request | Retry later. See rate limits. |
502 or 503 | WordPress failed or was unavailable | Check the site and retry only after the earlier request's result is known; a timed-out mutation may have reached WordPress. |
When a WordPress.com grant or self-hosted Application Password stops working, reconnect the same site on the same profile. The existing account is refreshed in place when the site identity matches.
Related
- Connecting accounts: both authentication paths.
- Blogs API: the generated reference for every blog and article endpoint.
- Get WordPress.com authorization URL: OAuth query and response fields.
- Connect self-hosted WordPress: Application Password request and response fields.
- Error handling: canonical error envelopes and retry guidance.
- Platforms overview: social-posting platforms and connect-only integrations.
- WordPress REST API authentication: upstream authentication and Application Password behavior.
- WordPress posts, media and tags: upstream resources behind the article mapping.