> ## Documentation Index
> Fetch the complete documentation index at: https://developers.hubspot.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

---
id: 22ce2785-2b32-4b85-8be2-f1c147d26e2d
---

# CMS API | Blog Posts

> Use the blog posts API to create, manage, and publish blog posts on your website.

export const PostmanCard = ({title, collectionId}) => {
  return <div className="card-condensed-cta">
      <Card title={title ? title : "Postman collection"} href={`https://app.getpostman.com/run-collection/${collectionId}`} horizontal={true} cta="Run in Postman" icon={<svg xmlns="http://www.w3.org/2000/svg" width={25} height={25} preserveAspectRatio="xMidYMid" viewBox="0 0 256 256">
              <path fill="#FF6C37" d="M254.953 144.253c8.959-70.131-40.569-134.248-110.572-143.206C74.378-7.912 10.005 41.616 1.047 111.619c-8.959 70.003 40.569 134.248 110.572 143.334 70.131 8.959 134.248-40.569 143.334-110.7Z" />
              <path fill="#FFF" d="m174.2 82.184-54.007 54.007-15.229-15.23c53.11-53.11 58.358-48.503 69.236-38.777Z" />
              <path fill="#FF6C37" d="M120.193 137.47c-.384 0-.64-.128-.895-.384l-15.358-15.229a1.237 1.237 0 0 1 0-1.792c54.007-54.006 59.638-48.887 71.028-38.649.255.256.383.512.383.896s-.128.64-.383.896l-54.007 53.878c-.128.256-.512.384-.768.384Zm-13.437-16.509 13.437 13.438 52.087-52.087c-9.47-8.446-15.87-11.006-65.524 38.65Z" />
              <path fill="#FFF" d="m135.679 151.676-14.718-14.718 54.007-54.006c14.46 14.59-7.167 38.265-39.29 68.724Z" />
              <path fill="#FF6C37" d="M135.679 152.956c-.384 0-.64-.128-.896-.384l-14.718-14.718c-.256-.256-.256-.512-.256-.896s.128-.64.384-.895L174.2 82.056a1.237 1.237 0 0 1 1.791 0 15.58 15.58 0 0 1 4.991 11.902c-.256 14.206-16.38 32.25-44.28 58.614-.383.256-.767.384-1.023.384Zm-12.926-15.998c8.19 8.319 11.646 11.646 12.926 12.926 21.5-20.476 42.36-41.464 42.488-55.926.128-3.327-1.152-6.655-3.327-9.214l-52.087 52.214Z" />
              <path fill="#FFF" d="m105.22 121.345 10.878 10.878c.256.256.256.512 0 .768-.128.128-.128.128-.256.128l-22.524 4.863c-1.152.128-2.175-.64-2.431-1.791-.128-.64.128-1.28.512-1.664l13.053-13.054c.256-.256.64-.384.768-.128Z" />
              <path fill="#FF6C37" d="M92.934 139.262c-1.92 0-3.327-1.536-3.327-3.455 0-.896.384-1.792 1.024-2.432l13.053-13.054c.768-.64 1.792-.64 2.56 0l10.878 10.878c.768.64.768 1.792 0 2.56-.256.256-.512.384-.896.512l-22.524 4.863c-.256 0-.512.128-.768.128Zm11.902-16.51-12.542 12.543c-.256.256-.383.64-.128 1.024.128.383.512.511.896.383l21.116-4.607-9.342-9.342Z" />
              <path fill="#FFF" d="M202.739 52.238c-8.191-7.935-21.373-7.679-29.307.64-7.935 8.318-7.679 21.372.64 29.306A20.678 20.678 0 0 0 199.155 85l-14.59-14.59 18.174-18.172Z" />
              <path fill="#FF6C37" d="M188.405 89.223c-12.158 0-22.012-9.854-22.012-22.012 0-12.158 9.854-22.012 22.012-22.012 5.631 0 11.134 2.176 15.23 6.143.255.256.383.512.383.896s-.128.64-.384.895L186.357 70.41l13.566 13.566c.512.512.512 1.28 0 1.792l-.256.256c-3.327 2.047-7.295 3.199-11.262 3.199Zm0-41.337c-10.75 0-19.452 8.703-19.324 19.453 0 10.75 8.702 19.452 19.452 19.324 2.944 0 5.887-.64 8.575-2.047l-13.438-13.31c-.256-.256-.384-.512-.384-.896s.128-.64.384-.895l17.149-17.15c-3.456-2.943-7.807-4.479-12.414-4.479Z" />
              <path fill="#FFF" d="m203.122 52.622-.255-.256-18.301 18.044 14.461 14.462c1.408-.896 2.816-1.92 3.967-3.072a20.51 20.51 0 0 0 .128-29.178Z" />
              <path fill="#FF6C37" d="M199.155 86.28c-.384 0-.64-.128-.896-.384l-14.589-14.59c-.256-.256-.384-.512-.384-.896s.128-.64.384-.895l18.173-18.173a1.237 1.237 0 0 1 1.791 0l.384.256c8.575 8.574 8.575 22.396.128 31.098-1.28 1.28-2.687 2.432-4.223 3.328-.384.128-.64.256-.768.256Zm-12.798-15.87 12.926 12.926c1.024-.64 2.048-1.536 2.816-2.304 7.294-7.294 7.678-19.196.64-26.875L186.357 70.41Z" />
              <path fill="#FFF" d="M176.375 84.488a7.879 7.879 0 0 0-11.134 0l-48.247 48.247 8.063 8.063 51.062-44.792c3.328-2.816 3.584-7.807.768-11.134-.256-.128-.384-.256-.512-.384Z" />
              <path fill="#FF6C37" d="M124.929 142.077c-.384 0-.64-.128-.896-.383l-8.063-8.063a1.237 1.237 0 0 1 0-1.792l48.247-48.247a9.115 9.115 0 0 1 12.926 0 9.115 9.115 0 0 1 0 12.926l-.384.384-51.063 44.792c-.128.255-.384.383-.767.383Zm-6.143-9.342 6.27 6.271 50.167-44.024c2.816-2.304 3.072-6.527.768-9.342-2.303-2.816-6.526-3.072-9.342-.768-.128.128-.256.256-.512.384l-47.351 47.48Z" />
              <path fill="#FFF" d="M80.009 187.637c-.512.256-.768.768-.64 1.28l2.175 9.214c.512 1.28-.256 2.816-1.663 3.2-1.024.384-2.176 0-2.816-.768l-14.077-13.95 45.943-45.943 15.87.256 10.75 10.75c-2.56 2.175-18.045 17.149-55.542 35.961Z" />
              <path fill="#FF6C37" d="M78.985 202.61c-1.024 0-2.048-.383-2.688-1.151l-13.95-13.95c-.255-.256-.383-.512-.383-.896 0-.383.128-.64.384-.895l45.944-45.944c.256-.256.64-.384.895-.384l15.87.256c.383 0 .64.128.895.384l10.75 10.75c.256.256.384.64.384 1.024s-.128.64-.512.896l-.895.767c-13.566 11.902-31.995 23.804-54.902 35.194l2.175 9.086c.384 1.664-.384 3.456-1.92 4.352-.767.384-1.407.512-2.047.512Zm-14.078-15.997 13.182 13.054c.384.64 1.152.896 1.792.512.64-.384.896-1.152.512-1.792l-2.176-9.214c-.256-1.152.256-2.176 1.28-2.688 22.652-11.39 40.952-23.163 54.39-34.81l-9.47-9.47-14.718-.256-44.792 44.664Z" />
              <path fill="#FFF" d="m52.11 197.62 11.006-11.007 16.38 16.381-26.107-1.791c-1.151-.128-1.92-1.152-1.791-2.304 0-.512.128-1.024.512-1.28Z" />
              <path fill="#FF6C37" d="m79.497 204.146-26.236-1.791c-1.92-.128-3.199-1.792-3.071-3.712.128-.768.384-1.535 1.024-2.047L62.22 185.59a1.237 1.237 0 0 1 1.792 0l16.38 16.38c.385.385.512.897.257 1.408-.256.512-.64.768-1.152.768Zm-16.381-15.74-10.11 10.11c-.384.255-.384.895 0 1.151.127.128.255.256.511.256l22.652 1.536-13.053-13.054ZM104.452 146.557c-.768 0-1.28-.64-1.28-1.28 0-.384.128-.64.384-.896l12.414-12.414a1.237 1.237 0 0 1 1.792 0l8.062 8.063c.384.384.512.768.384 1.28-.128.384-.512.767-1.023.895l-20.477 4.352h-.256Zm12.414-11.902-8.446 8.446 13.821-2.943-5.375-5.503Z" />
              <path fill="#FFF" d="m124.8 140.926-14.077 3.071c-1.024.256-2.048-.384-2.303-1.408-.128-.64 0-1.28.511-1.791l7.807-7.807 8.063 7.935Z" />
              <path fill="#FF6C37" d="M110.467 145.277a3.168 3.168 0 0 1-3.2-3.2c0-.895.385-1.663.897-2.303l7.806-7.807a1.237 1.237 0 0 1 1.792 0l8.062 8.063c.384.384.512.768.384 1.28-.128.384-.512.767-1.023.895l-14.078 3.072h-.64Zm6.399-10.622-6.91 6.91c-.257.257-.257.512-.129.768s.384.384.768.384l11.774-2.56-5.503-5.502ZM203.25 64.907c-.256-.767-1.151-1.151-1.92-.895-.767.255-1.151 1.151-.895 1.92 0 .127.128.255.128.383.768 1.536.512 3.455-.512 4.863-.512.64-.384 1.536.128 2.048.64.512 1.536.384 2.048-.256 1.92-2.432 2.303-5.503 1.023-8.063Z" />
            </svg>} />
    </div>;
};

export const Tag = ({children, type = 'default', className = ''}) => {
  return <span className={`tag tag-${type} ${className}`.trim()}>
      {children}
    </span>;
};

export const ScopesList = ({scopes = [], description = "This API requires one of the following scopes:"}) => {
  if (!scopes || scopes.length === 0) {
    return null;
  }
  const sortedScopes = scopes.sort((a, b) => a.localeCompare(b));
  return <div>
      <div className="text-sm mb-2">{description}</div>
      <div>
        {sortedScopes.map((scope, index) => <div key={index}>
            <code>
              <span className="text-xs">{scope}</span>
            </code>
          </div>)}
      </div>
    </div>;
};

<PostmanCard collectionId="26126890-755629a0-c2b8-4e60-8af9-6c8ebec90e84" />

<Accordion title="Scope requirements">
  <ScopesList
    scopes={[
  'content'
]}
  />
</Accordion>

You can use the blog post API to publish and manage blog posts. Learn more about how to create and maintain your blog on the [HubSpot Knowledge Base.](https://knowledge.hubspot.com/web-content/topics#blog)

## Changes in V3

* The following properties are deprecated and will not be included in the response of any of the V3 endpoints:
  * `campaign_name`
  * `is_draft`
  * `keywords`
* The `topicIds` property has been renamed to `tagIds`.

## Retrieve blog posts

You can retrieve blog posts either individually by ID or by retrieving all blog posts:

* To retrieve all blog posts, make a `GET` request to `/cms/v3/blogs/posts`.
* To retrieve an individual blog post, make a `GET` request to `/cms/v3/blogs/posts/{postId}`.

### Retrieve all blog posts

When retrieving all blog posts, you can [filter](#filtering) and [sort](#sorting-and-paginating) the returned results using query parameters.

For example, the following request would retrieve the first 10 blog posts created after January 1, 2024:

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts?createdAfter=2024-01-01T00:00:00Z&limit=10 \
  --request GET \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

The following query parameters are available:

| Parameter       | Type    | Description                                                                                                                                                                                               |
| --------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `after`         | String  | The paging cursor token of the last successfully read resource. Available from `paging.next.after` in paginated responses.                                                                                |
| `archived`      | Boolean | Whether to return only results that have been archived. Defaults to `false`.                                                                                                                              |
| `createdAfter`  | String  | Only return blog posts created after this date (ISO 8601 format).                                                                                                                                         |
| `createdAt`     | String  | Only return blog posts created at exactly this date (ISO 8601 format).                                                                                                                                    |
| `createdBefore` | String  | Only return blog posts created before this date (ISO 8601 format).                                                                                                                                        |
| `limit`         | Integer | Maximum number of results per page. Default is `20`.                                                                                                                                                      |
| `property`      | String  | Specify specific properties to return from the posts.                                                                                                                                                     |
| `sort`          | Array   | Specify the order in which the blog posts are returned. Valid fields are `createdAt` (default), `name`, `updatedAt`, `createdBy`, `updatedBy`. Prefix with `-` to reverse the order (e.g., `-createdAt`). |
| `updatedAfter`  | String  | Only return blog posts last updated after this date (ISO 8601 format).                                                                                                                                    |
| `updatedAt`     | String  | Only return blog posts last updated at exactly this date (ISO 8601 format).                                                                                                                               |
| `updatedBefore` | String  | Only return blog posts last updated before this date (ISO 8601 format).                                                                                                                                   |

The response includes a `total` count and an array of blog post objects:

```json expandable theme={null}
{
  "total": 2,
  "results": [
    {
      "id": "184993428780",
      "slug": "example-blog-post",
      "contentGroupId": "184993428123",
      "campaign": "3b56cd5e-2c88-4b01-aed0-0d5a5e5e5e5e",
      "categoryId": 3,
      "state": "PUBLISHED",
      "name": "Example Blog Post",
      "mabExperimentId": "",
      "authorName": "John Doe",
      "abTestId": "",
      "createdById": "12345678",
      "updatedById": "12345678",
      "domain": "",
      "abStatus": "master",
      "folderId": "",
      "widgetContainers": {},
      "widgets": {},
      "language": "en",
      "translatedFromId": "",
      "translations": {},
      "dynamicPageHubDbTableId": "",
      "blogAuthorId": "4183274253",
      "tagIds": [12345678901],
      "htmlTitle": "Example Blog Post | My Company Blog",
      "enableGoogleAmpOutputOverride": false,
      "useFeaturedImage": true,
      "postBody": "<p>Welcome to my blog post! Neat, huh?</p>",
      "postSummary": "A summary of my blog post.",
      "rssBody": "",
      "rssSummary": "",
      "currentlyPublished": true,
      "archivedInDashboard": false,
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T14:45:00.000Z",
      "publishDate": "2024-01-15T12:00:00.000Z",
      "currentState": "PUBLISHED",
      "url": "https://www.example.com/blog/example-blog-post",
      "featuredImage": "https://www.example.com/hubfs/featured-image.jpg",
      "featuredImageAltText": "Featured image alt text",
      "metaDescription": "This is the meta description for the blog post."
    }
  ],
  "paging": {
    "next": {
      "after": "Mg%3D%3D",
      "link": "https://api.hubspot.com/cms/v3/blogs/posts?after=Mg%3D%3D"
    }
  }
}
```

<Warning>
  The `authorName` field returns the name of the user who most recently published the blog post, not the name of the blog author associated with the post. To get the blog author's information, use the `blogAuthorId` field to look up the author via the [blog authors API](/api-reference/legacy/cms/blogs/authors/guide).
</Warning>

### Retrieve a single blog post

To retrieve details for a specific blog post, make a `GET` request to `/cms/v3/blogs/posts/{postId}`.

For example, the request below would retrieve the details for the blog post with ID `184993428780`:

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780 \
  --request GET \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

You can optionally include the `property` query parameter to return only specific properties.

### Filtering

You can filter blog posts using query parameters. Provide the property name, followed by two underscore characters, then include the associated operator as a suffix. For example, you can filter the results to only include blog posts where the `name` property contains the word `marketing` using the parameter: `&name__contains=marketing`.

| Filter                      | Operator       |
| --------------------------- | -------------- |
| Equals                      | `eq` (or none) |
| Not equal                   | `ne`           |
| Contains                    | `contains`     |
| Contains (case insensitive) | `icontains`    |
| Less than                   | `lt`           |
| Less than or equal          | `lte`          |
| Greater than                | `gt`           |
| Greater than or equal       | `gte`          |
| Null                        | `is_null`      |
| Not null                    | `not_null`     |
| Like                        | `like`         |
| Not like                    | `not_like`     |
| Starts with                 | `startswith`   |
| In                          | `in`           |
| Not in                      | `nin`          |

The table below lists the properties that can be filtered on, along with their supported filter types.

| Property           | Supported filters                   |
| ------------------ | ----------------------------------- |
| `id`               | `eq`, `in`                          |
| `slug`             | `eq`, `in`, `nin`, `icontains`      |
| `campaign`         | `eq`, `in`                          |
| `state`            | `eq`, `ne`, `in`, `nin`, `contains` |
| `publishDate`      | `eq`, `gt`, `gte` , `lt` ,`lte`     |
| `createdAt`        | `eq`, `gt`, `gte` , `lt` ,`lte`     |
| `updatedAt`        | `eq`, `gt`, `gte` , `lt` ,`lte`     |
| `name`             | `eq`, `in`, `icontains`             |
| `archivedAt`       | `eq`, `gt`, `gte`, `lt` ,`lte`      |
| `createdById`      | `eq`                                |
| `updatedById`      | `eq`                                |
| `blogAuthorId`     | `eq`, `in`                          |
| `translatedFromId` | `is_null`, `not_null`               |
| `contentGroupId`   | `eq`, `in`                          |
| `tagId`            | `eq`, `in`                          |

#### Filtering by publish state

| Publish State | Query parameters  |
| ------------- | ----------------- |
| Draft         | `state=DRAFT`     |
| Scheduled     | `state=SCHEDULED` |
| Published     | `state=PUBLISHED` |

<Warning>
  The `currentState` field on the blog post object is a generated field which also reflects the blog's publish state, but you cannot use it as a property to filter against in your requests.
</Warning>

#### Filtering for multi-language posts

| Description                                   | Query parameters             |
| --------------------------------------------- | ---------------------------- |
| Primary blog post in a multi-language group   | `translatedFromId__is_null`  |
| Variation blog post in a multi-language group | `translatedFromId__not_null` |
| Blog post with specific language              | `contentGroupId__eq`         |

### Sorting and paginating

You can provide sorting and pagination options as query parameters. Specify the property name as the value to the `sort` query parameter to return the blog posts in the natural order of that property. You can reverse the sorting order by including a dash character before the property name (e.g., `sort=-createdAt`).

By combining query parameters for filtering, sorting, and pagination, you can retrieve blog posts that match more advanced search criteria. For example, the request below fetches blog posts that have a language assigned, ordered by the most recently updated, and returns the second page of results:

```shell wrap theme={null}
curl 'https://api.hubapi.com/cms/v3/blogs/posts?sort=-updatedAt&language__not_null&limit=10&after=MTA%3D' \
  --request GET \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

## Create a blog post

To create a new blog post, make a `POST` request to `/cms/v3/blogs/posts`.

For example, the request below would create a new draft blog post:

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Example blog post",
    "contentGroupId": "184993428780",
    "slug": "slug-at-the-end-of-the-url",
    "blogAuthorId": "4183274253",
    "metaDescription": "My meta description.",
    "useFeaturedImage": false,
    "postBody": "<p>Welcome to my blog post! Neat, huh?</p>"
  }'
```

The following request body parameters are available:

| Parameter                                         | Type    | Description                                                                                                                                                                                                                                                                                                   |
| ------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` <Tag type="error">Required</Tag>           | String  | The title of the blog post.                                                                                                                                                                                                                                                                                   |
| `contentGroupId` <Tag type="error">Required</Tag> | String  | The ID of the parent blog to publish the post to. You can retrieve the ID using the [blog settings API](guides/cms-api/blog-settings).                                                                                                                                                                        |
| `slug`                                            | String  | The URL slug of the blog post. HubSpot will automatically generate the full URL using this value, along with the parent blog domain and slug. If no value is provided, the `name` value will be used as a temporary slug (hyphenated). You will need to set a specific slug before the post can be published. |
| `blogAuthorId`                                    | String  | The ID of the blog author. You can retrieve this value using the [blog authors API](/api-reference/legacy/cms/blogs/authors/guide).                                                                                                                                                                           |
| `metaDescription`                                 | String  | The post's meta description.                                                                                                                                                                                                                                                                                  |
| `useFeaturedImage`                                | Boolean | Whether to include a featured image for the blog post. By default, this field is set to `true`. To include a featured image, include a value for the `featuredImage` parameter.                                                                                                                               |
| `featuredImage`                                   | String  | The URL of the featured image.                                                                                                                                                                                                                                                                                |
| `featuredImageAltText`                            | String  | Alt text for the featured image.                                                                                                                                                                                                                                                                              |
| `postBody`                                        | String  | The HTML content of the blog post.                                                                                                                                                                                                                                                                            |
| `postSummary`                                     | String  | The summary of the blog post that will appear on the main listing page.                                                                                                                                                                                                                                       |
| `htmlTitle`                                       | String  | The HTML title of the post.                                                                                                                                                                                                                                                                                   |
| `tagIds`                                          | Array   | An array of tag IDs to associate with the post.                                                                                                                                                                                                                                                               |
| `language`                                        | String  | The ISO 639 language code of the post (e.g., `en`, `de`, `fr`).                                                                                                                                                                                                                                               |
| `publishDate`                                     | String  | The date (ISO 8601 format) the blog post is to be published at.                                                                                                                                                                                                                                               |

By default, the post will be created as an unpublished draft. If needed, a blog post can be published at the time of creation as long as the [properties necessary for publishing](#publishing-blog-posts) are set.

<Warning>
  Editing blog post content directly in HubSpot using the content editor is the simplest way to modify content. While you can use the API to create and update blog post, it's not recommended over using the editor, especially for blogs that rely on more complex modules.
</Warning>

## Update a blog post

To update an existing blog post, make a `PATCH` request to `/cms/v3/blogs/posts/{postId}`.

For example, the request below would update the title and URL slug of a blog post:

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780 \
  --request PATCH \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Updated blog post title",
    "slug": "my-updated-post"
  }'
```

This request updates both the draft and live versions of the post. To update only the draft version without affecting the live content, make a `PATCH` request to `/cms/v3/blogs/posts/{postId}/draft` instead.

The following request body parameters are available:

| Parameter              | Type    | Description                                                                      |
| ---------------------- | ------- | -------------------------------------------------------------------------------- |
| `name`                 | String  | The title of the blog post.                                                      |
| `slug`                 | String  | The URL slug of the blog post.                                                   |
| `blogAuthorId`         | String  | The ID of the blog author.                                                       |
| `metaDescription`      | String  | The post's meta description.                                                     |
| `useFeaturedImage`     | Boolean | Whether to include a featured image for the blog post.                           |
| `featuredImage`        | String  | The URL of the featured image.                                                   |
| `featuredImageAltText` | String  | Alt text for the featured image.                                                 |
| `postBody`             | String  | The HTML content of the blog post.                                               |
| `postSummary`          | String  | The summary of the blog post.                                                    |
| `htmlTitle`            | String  | The HTML title of the post.                                                      |
| `tagIds`               | Array   | An array of tag IDs to associate with the post.                                  |
| `language`             | String  | The ISO 639 language code of the post.                                           |
| `publishDate`          | String  | The date (ISO 8601 format) the blog post is to be published at.                  |
| `archivedInDashboard`  | Boolean | Set to `true` to archive the post in the dashboard (the post may still be live). |

<Note>
  Properties you provide in the request payload will override existing draft properties without any complex merging logic. As a result, if you're updating nested properties, you should provide the full definition of the object. Partial updates are not supported for nested objects.
</Note>

## Delete a blog post

To delete an existing blog post, make a `DELETE` request to `/cms/v3/blogs/posts/{postId}`.

For example, the request below would delete the blog post with ID `184993428780`:

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780 \
  --request DELETE \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

A successful deletion returns a `204 No Content` response with no body.

<Note>
  This is not the same as the in-app archive function. To perform a dashboard archive, send a normal update with the `archivedInDashboard` field set to `true`.
</Note>

## Draft and live versions

Blog posts in HubSpot have both draft and live versions.

* **Draft blog posts** appear in HubSpot's editor, but are not live on the website. They can be reviewed and edited by users in HubSpot or via the API, and can be published when needed. After a blog post is published, the draft version can be updated as needed, then later published to update the live content.
* **Live blog posts** are blog posts that appear on the website. The draft version can be updated without affecting the live blog post content. Published posts can be unpublished to remove them from the website and return them to a draft version.

### Retrieve the draft version

To retrieve the draft version of a blog post, make a `GET` request to `/cms/v3/blogs/posts/{postId}/draft`.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780/draft \
  --request GET \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Update the draft version

To update only the draft version of a blog post (without affecting the live content), make a `PATCH` request to `/cms/v3/blogs/posts/{postId}/draft`.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780/draft \
  --request PATCH \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Updated draft title",
    "postBody": "<p>Updated draft content.</p>"
  }'
```

Because this request only updates the draft, you would need to make a second request to [publish the changes](#publishing-blog-posts) to the website.

### Reset a draft

To reset the draft version of a blog post back to its current live version, make a `POST` request to `/cms/v3/blogs/posts/{postId}/draft/reset`. This endpoint does not require a request body.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780/draft/reset \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

## Publishing blog posts

Depending on the state of the blog post, there are different endpoints you can use to publish it.

### Publish a draft

If the blog post is currently a draft (not yet published), make a `PATCH` request to the `/cms/v3/blogs/posts/{postId}` endpoint. In the request body, include a JSON payload that sets the `state` to `PUBLISHED`:

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780 \
  --request PATCH \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "state": "PUBLISHED"
  }'
```

The post must have the following properties set in order to be published:

| Property                                           | Type   | Description                                                                                                                                                                                                                              |
| -------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` <Tag type="error">Required</Tag>            | String | The title of the blog post.                                                                                                                                                                                                              |
| `contentGroupId` <Tag type="error">Required</Tag>  | String | The ID of the parent blog to publish the post to. You can retrieve the ID using the [blogs API](guides/cms-api/blog-settings).                                                                                                           |
| `slug` <Tag type="error">Required</Tag>            | String | The URL slug of the blog post. If no slug was specified at the time of creation, HubSpot will have assigned a temporary slug to the post. This temporary slug must be updated to a specific value in order for the post to be published. |
| `blogAuthorId` <Tag type="error">Required</Tag>    | String | The ID of the blog author. You can retrieve this value using the [blog authors API](/api-reference/legacy/cms/blogs/authors/guide).                                                                                                      |
| `metaDescription` <Tag type="error">Required</Tag> | String | The post's meta description.                                                                                                                                                                                                             |
| `featuredImage`                                    | String | An image to use as the post's featured image. Alternatively, you can opt to not include a featured image by omitting this field and instead setting `useFeaturedImage` to `false`.                                                       |
| `state` <Tag type="error">Required</Tag>           | String | The publish state of the post. Must be set to `PUBLISHED`.                                                                                                                                                                               |

### Push draft changes live

If the blog post is currently published, you can publish any content that's currently drafted by making a `POST` request to `/cms/v3/blogs/posts/{postId}/draft/push-live`. This endpoint does not require a request body.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780/draft/push-live \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Schedule a draft to be published

As an alternative to immediate publishing, you can schedule the draft version of your blog post to be published later by making a `POST` request to `/cms/v3/blogs/posts/schedule`. In the request body, include a JSON payload that contains the `id` of the target blog post and a `publishDate` (ISO 8601 format).

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/schedule \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "id": "184993428780",
    "publishDate": "2024-06-01T12:00:00Z"
  }'
```

## Clone a blog post

To create a copy of an existing blog post, make a `POST` request to `/cms/v3/blogs/posts/clone`.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/clone \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "id": "184993428780",
    "cloneName": "Copy of Example Blog Post"
  }'
```

| Parameter                             | Type   | Description                        |
| ------------------------------------- | ------ | ---------------------------------- |
| `id` <Tag type="error">Required</Tag> | String | The ID of the blog post to clone.  |
| `cloneName`                           | String | The name for the cloned blog post. |

## Revisions

You can access previous versions of a blog post and restore them if needed.

### Get all revisions

To retrieve all previous versions of a blog post, make a `GET` request to `/cms/v3/blogs/posts/{postId}/revisions`.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/184993428780/revisions \
  --request GET \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Get a specific revision

To retrieve a specific previous version, make a `GET` request to `/cms/v3/blogs/posts/{postId}/revisions/{revisionId}`.

### Restore a revision

To restore a blog post to a previous version, make a `POST` request to `/cms/v3/blogs/posts/{postId}/revisions/{revisionId}/restore`. This will update both the draft and live versions.

To restore a previous version to the draft only (without affecting the live content), make a `POST` request to `/cms/v3/blogs/posts/{postId}/revisions/{revisionId}/restore-to-draft`.

## Multi-language management

To help you maintain blog posts across multiple languages, HubSpot's CMS allows you to group together language variants of the same content. You can learn more about working with multi-language blog posts in [on HubSpot's Knowledge Base](https://knowledge.hubspot.com/blog/create-a-multi-language-blog#create-a-blog-post-in-multiple-languages).

### Create a new language variant

You can create a new language variant for an existing blog post by making a `POST` request to the `/cms/v3/blogs/posts/multi-language/create-language-variation` endpoint.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/multi-language/create-language-variation \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "id": "184993428780",
    "language": "de"
  }'
```

| Parameter                                   | Type   | Description                                                    |
| ------------------------------------------- | ------ | -------------------------------------------------------------- |
| `id` <Tag type="error">Required</Tag>       | String | The ID of the blog post to clone.                              |
| `language` <Tag type="error">Required</Tag> | String | The language code of the new variant (e.g., `de`, `fr`, `es`). |

### Attach a blog post to an existing multi-language group

You can add a blog post to an existing multi-language group by making a `POST` request to the `/cms/v3/blogs/posts/multi-language/attach-to-lang-group` endpoint.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/multi-language/attach-to-lang-group \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "id": "184993428781",
    "language": "de",
    "primaryId": "184993428780"
  }'
```

| Parameter                                    | Type   | Description                                                                                     |
| -------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------- |
| `id` <Tag type="error">Required</Tag>        | String | The ID of the target blog post to attach.                                                       |
| `language` <Tag type="error">Required</Tag>  | String | The language identifier of the blog post being added.                                           |
| `primaryId` <Tag type="error">Required</Tag> | String | The ID of the blog post designated as the primary blog post in the target multi-language group. |

### Detach a blog post from a multi-language group

To detach a blog post from a multi-language group, make a `POST` request to the `/cms/v3/blogs/posts/multi-language/detach-from-lang-group` endpoint.

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/multi-language/detach-from-lang-group \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "id": "184993428781"
  }'
```

### Set a new primary language

To change the primary language of a multi-language group, make a `PUT` request to `/cms/v3/blogs/posts/multi-language/set-new-lang-primary`:

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/multi-language/set-new-lang-primary \
  --request PUT \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "id": "184993428781"
  }'
```

### Update languages in a multi-language group

To explicitly set new languages for each post in a multi-language group, make a `POST` request to `/cms/v3/blogs/posts/multi-language/update-languages`:

```shell wrap theme={null}
curl https://api.hubapi.com/cms/v3/blogs/posts/multi-language/update-languages \
  --request POST \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "primaryId": "184993428780",
    "languages": {
      "184993428780": "en",
      "184993428781": "de",
      "184993428782": "fr"
    }
  }'
```

| Parameter                                    | Type   | Description                                                  |
| -------------------------------------------- | ------ | ------------------------------------------------------------ |
| `primaryId` <Tag type="error">Required</Tag> | String | The ID of the primary blog post in the multi-language group. |
| `languages` <Tag type="error">Required</Tag> | Object | A map of blog post IDs to their associated language codes.   |
