# Publish blog articles

Create, schedule, update and delete blog articles on Shopify, WordPress, Wix and Webflow with one request shape.

The Blogs API publishes long-form articles to the sites your users already run. One request shape works on Shopify blogs, WordPress posts, Wix Blog and Webflow CMS collections. Scheduling is native: the site itself publishes at the time you set, so an article goes out even if your servers are down. Blogs are separate from [social posts](https://pozy.ai/docs/publishing): a blog account is never a target of `POST /v1/posts`.

## How blogs map on each platform

| | Shopify | WordPress | Wix | Webflow |
| --- | --- | --- | --- | --- |
| A "blog" is | One of the store's blogs ("News" by default) | The site (one blog per account) | The site (blog id `"1"`) | A CMS collection with a rich text field |
| Blog id | Shopify's numeric id | `"1"` self-hosted, site id on WordPress.com | `"1"` | The collection id |
| Create or delete blogs | Yes | No (`405`) | No (`405`) | No (`405`) |
| Native scheduling | Yes | Yes | Yes | No (`400`) |
| Drafts | Yes | Yes (default) | Yes | Yes (default) |
| `seo` | Yes | No (`400`) | Yes | No (`400`) |
| Featured image | Yes | Yes, imported into the media library | Yes, imported into the Media Manager | Yes, an image field |
| Extra collection fields | | | | `fieldData` |

## Find the blog

Always list blogs first and take the id from the response:

```bash cURL
curl https://api.pozy.ai/v1/accounts/6710c4b2e4b0d2f1a8c95e3a/blogs \
  -H "Authorization: Bearer $POZY_API_KEY"
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/accounts/6710c4b2e4b0d2f1a8c95e3a/blogs", {
  headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
const blogId = data.blogs[0].id;
```
```python Python
import os, requests

blogs = requests.get(
    "https://api.pozy.ai/v1/accounts/6710c4b2e4b0d2f1a8c95e3a/blogs",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
).json()["blogs"]
blog_id = blogs[0]["id"]
```

```json
{
  "platform": "shopify",
  "blogs": [{ "id": "121793282419", "platform": "shopify", "title": "News", "handle": "news" }],
  "nextCursor": null
}
```

## Create an article

`title` is required. `bodyHtml` is raw HTML: Pozy does not convert Markdown, so render it first if that is what you store.

```bash cURL
curl -X POST https://api.pozy.ai/v1/accounts/6710c4b2e4b0d2f1a8c95e3a/blogs/121793282419/articles \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: article-winter-guide-2026" \
  -d '{
    "title": "How to care for merino wool",
    "bodyHtml": "<p>Wash cold, dry flat, and skip the softener.</p>",
    "handle": "merino-care-guide",
    "excerpt": "Three rules that keep merino soft for years.",
    "tags": ["guides", "merino"],
    "author": "Maya Lindqvist",
    "image": { "url": "https://cdn.example.com/merino-care.jpg", "altText": "Folded merino jumpers" },
    "seo": { "title": "Merino care guide", "description": "How to wash and dry merino wool." },
    "publishDate": "2026-11-15T08:00:00Z"
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/accounts/6710c4b2e4b0d2f1a8c95e3a/blogs/121793282419/articles", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Idempotency-Key": "article-winter-guide-2026",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "How to care for merino wool",
    bodyHtml: "<p>Wash cold, dry flat, and skip the softener.</p>",
    tags: ["guides", "merino"],
    image: { url: "https://cdn.example.com/merino-care.jpg", altText: "Folded merino jumpers" },
    publishDate: "2026-11-15T08:00:00Z",
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.article.id, data.article.status);
```
```python Python
resp = requests.post(
    "https://api.pozy.ai/v1/accounts/6710c4b2e4b0d2f1a8c95e3a/blogs/121793282419/articles",
    headers={
        "Authorization": f"Bearer {os.environ['POZY_API_KEY']}",
        "Idempotency-Key": "article-winter-guide-2026",
    },
    json={
        "title": "How to care for merino wool",
        "bodyHtml": "<p>Wash cold, dry flat, and skip the softener.</p>",
        "tags": ["guides", "merino"],
        "publishDate": "2026-11-15T08:00:00Z",
    },
)
article = resp.json()["article"]
```

The response returns `201` with the article as the site stored it, including the `handle` it really got and its `status`.

## Article fields

| Field | Type | Notes |
| --- | --- | --- |
| `title` | string | Required on create |
| `bodyHtml` | string | Raw HTML. Wix converts it to rich content; Webflow drops code blocks |
| `handle` | string | URL slug. Generated from the title when left out. Shopify sets it once at creation, so set it yourself when the URL matters |
| `excerpt` | string | Summary shown in listings |
| `tags` | string[] | Matched by name and created when missing (WordPress up to 100, Wix up to 30). Replaces the full list on update |
| `author` | string | Shopify: a display name. WordPress: the numeric user id. Wix: a site member id. Webflow: the referenced author item id |
| `image` | `{ url, altText? }` | Featured image, downloaded by the site from a public URL. JPEG, PNG, WebP or GIF up to 10 MB on WordPress and Wix |
| `seo` | `{ title?, description? }` | Shopify and Wix only |
| `isPublished` | boolean | `false` keeps a draft and wins over any date. `true` publishes now |
| `publishDate` | ISO 8601 | A future date schedules natively. Use an offset or `Z` |
| `fieldData` | object | Webflow only: other collection fields by slug |

## Publish, draft or schedule

| You want | Send |
| --- | --- |
| A draft | `isPublished: false` |
| Live now | `isPublished: true` (no future `publishDate`) |
| Scheduled | A future `publishDate`, and leave `isPublished` out |

Scheduled articles read back with `isPublished: false` and `publishDate` set. WordPress reports `status: "future"`. On Webflow a future date returns `400` because Webflow's API cannot schedule; if you need a date there, hold the article in your own scheduler and publish it at the right time.

> **`isPublished: false` beats the date:**
> 
> Sending `isPublished: false` together with a future `publishDate` creates a draft, not a scheduled article. Omit `isPublished` when you schedule.

## Update and delete

`PATCH .../articles/{articleId}` is a partial update: fields you leave out keep their value.

- `tags` replaces the list. An empty array clears it.
- An empty `bodyHtml` or `excerpt` clears it (WordPress).
- `isPublished: false` moves a published or scheduled article back to draft (on Webflow, it unpublishes the item).
- Leaving out `image` keeps the current featured image. There is no way to remove one.
- Edits to a published article go live straight away.

`DELETE .../articles/{articleId}` returns `204` and deletes the article on the site. It is permanent on every platform and Pozy keeps no copy. On Shopify, deleting a blog deletes every article in it.

## List and read articles

`GET .../articles` returns articles a page at a time (`limit` 1 to 50, default 20, then `cursor` from `nextCursor`). WordPress includes `publish`, `future`, `draft`, `pending` and `private` posts the connected user can see. Wix lists return `bodyHtml: null`: get the single article for its body. Article ids are the site's own and come only from these responses.

## Gotchas

- The site downloads images from your URL, so it must be publicly reachable without auth or redirect chains.
- Use an `Idempotency-Key` on create. A timed-out request may have reached the site, and the key stops a retry from publishing twice. See [Idempotency](https://pozy.ai/docs/getting-started/idempotency).
- WordPress scheduling relies on the site's own cron. A site whose scheduled jobs do not run publishes after the set time, or never; Pozy does not force publication.
- `url` is set only while an article is published.

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/accounts/{accountId}/blogs`](https://pozy.ai/docs/api/listBlogs) | List blogs |
| [`POST /v1/accounts/{accountId}/blogs`](https://pozy.ai/docs/api/createBlog) | Create a blog |
| [`GET /v1/accounts/{accountId}/blogs/{blogId}`](https://pozy.ai/docs/api/getBlog) | Get a blog |
| [`PATCH /v1/accounts/{accountId}/blogs/{blogId}`](https://pozy.ai/docs/api/updateBlog) | Update a blog |
| [`DELETE /v1/accounts/{accountId}/blogs/{blogId}`](https://pozy.ai/docs/api/deleteBlog) | Delete a blog |
| [`GET /v1/accounts/{accountId}/blogs/{blogId}/articles`](https://pozy.ai/docs/api/listBlogArticles) | List blog articles |
| [`POST /v1/accounts/{accountId}/blogs/{blogId}/articles`](https://pozy.ai/docs/api/createBlogArticle) | Create a blog article |
| [`GET /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId}`](https://pozy.ai/docs/api/getBlogArticle) | Get a blog article |
| [`PATCH /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId}`](https://pozy.ai/docs/api/updateBlogArticle) | Update a blog article |
| [`DELETE /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId}`](https://pozy.ai/docs/api/deleteBlogArticle) | Delete a blog article |
