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

# Create Blog Post via Write API

> Create a new blog post via the Write API with support for draft status, author assignment, category tagging, and media integration.

Create a new blog post using the Write API with support for rich media, categorization, and automatic author assignment. This endpoint enables programmatic content creation for blogs, news sites, and content management workflows.

**Required Fields**: Every blog post must include a `title` (post title) and `slug` (URL-friendly identifier). The slug must be unique across all posts in your organization and will become part of the post's permanent URL.

**Author Management**: The author must already exist in your ButterCMS account before creating posts. You can specify the author by email address or slug. If no author is specified, the post will be automatically assigned to your organization owner, ensuring every post has proper attribution.

**Dynamic Categorization**: Categories and tags can be provided as string arrays in your request. If you specify categories or tags that don't exist yet, they will be automatically created in your account, making it easy to expand your content taxonomy organically.

**Media Integration**: Featured images are always uploaded to your ButterCMS media library for reliable hosting. For images within the post body content, set `upload_images_to_media_library=true` to automatically download and host external images through ButterCMS's CDN infrastructure.

**Localization**: Use the `locale` query parameter to assign a locale to the post (e.g. `?locale=es`). When omitted, the post is created in your organization's default locale. To create a translated version of an existing post, use the Update endpoint with the desired locale instead.

**Publication & Scheduling**: Blog posts default to `draft` unless `status=published` is provided. Scheduling future publication is not supported via the Write API; scheduling-related fields will result in validation errors.

**Asynchronous Processing**: This endpoint returns `202 Accepted` immediately to ensure fast response times for your application. The actual post creation, including media processing, category creation, and webhook notifications, happens in the background processing queue.


## OpenAPI

````yaml /api/openapi/write_api.yaml post /posts/
openapi: 3.1.0
info:
  title: ButterCMS Write API
  version: 2.0.0
  description: >
    Write endpoints for the ButterCMS API — create, update, and delete Pages,
    Collections, and Blog Posts.
  contact:
    email: support@buttercms.com
  license:
    name: ButterCMS Terms of Service
    url: https://buttercms.com/terms/
servers:
  - url: https://api.buttercms.com/v2
    description: ButterCMS API v2
security:
  - writeTokenAuth: []
tags:
  - name: Pages
    description: >
      Create new pages using ButterCMS Write API. Programmatically create
      content to enable powerful use cases and scale your content faster.


      Note: For Pages, PUT behaves like PATCH (partial update). Use either
      method to perform partial updates.
  - name: Collections
    description: >
      Create, update, and delete Collection items using ButterCMS Write API.
      Collections are user-defined content structures with completely
      customizable field schemas.
  - name: Blog posts
    description: >
      Create, update, and delete blog posts using ButterCMS Write API. Blog
      posts support rich content including HTML body, categories, tags, featured
      images, and SEO metadata.
paths:
  /posts/:
    post:
      tags:
        - Blog posts
      summary: Create Blog Post via Write API
      description: >
        Create a new blog post using the Write API with support for rich media,
        categorization, and automatic author assignment. This endpoint enables
        programmatic content creation for blogs, news sites, and content
        management workflows.


        **Required Fields**: Every blog post must include a `title` (post title)
        and `slug` (URL-friendly identifier). The slug must be unique across all
        posts in your organization and will become part of the post's permanent
        URL.


        **Author Management**: The author must already exist in your ButterCMS
        account before creating posts. You can specify the author by email
        address or slug. If no author is specified, the post will be
        automatically assigned to your organization owner, ensuring every post
        has proper attribution.


        **Dynamic Categorization**: Categories and tags can be provided as
        string arrays in your request. If you specify categories or tags that
        don't exist yet, they will be automatically created in your account,
        making it easy to expand your content taxonomy organically.


        **Media Integration**: Featured images are always uploaded to your
        ButterCMS media library for reliable hosting. For images within the post
        body content, set `upload_images_to_media_library=true` to automatically
        download and host external images through ButterCMS's CDN
        infrastructure.


        **Localization**: Use the `locale` query parameter to assign a locale to
        the post (e.g. `?locale=es`). When omitted, the post is created in your
        organization's default locale. To create a translated version of an
        existing post, use the Update endpoint with the desired locale instead.


        **Publication & Scheduling**: Blog posts default to `draft` unless
        `status=published` is provided. Scheduling future publication is not
        supported via the Write API; scheduling-related fields will result in
        validation errors.


        **Asynchronous Processing**: This endpoint returns `202 Accepted`
        immediately to ensure fast response times for your application. The
        actual post creation, including media processing, category creation, and
        webhook notifications, happens in the background processing queue.
      operationId: createBlogPost
      parameters:
        - name: locale
          in: query
          description: >
            Assign a locale to the new post (e.g. `en`, `es`). When omitted, the
            post is created in your organization's default locale.
          required: false
          schema:
            type: string
            maxLength: 10
          example: es
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBlogPostRequest'
            examples:
              complete_blog_post:
                summary: Complete blog post with all fields
                value:
                  title: This is a blog post
                  slug: this-is-a-blog-post
                  status: published
                  author:
                    email: your@author.com
                  categories:
                    - Recipes
                    - Meals
                  tags:
                    - Butter
                    - Sushi
                    - Really Good Recipes
                  featured_image: >-
                    https://farm1.staticflickr.com/836/42903355654_8faa21171a_m_d.jpg
                  featured_image_alt: Featured image alt text example.
                  body: <h1>Butter</h1><p>I am so hungry!</p>
                  summary: This is a blog post summary.
                  seo_title: This is a blog post
                  meta_description: This is a blog post to test the API.
                  upload_images_to_media_library: false
              minimal_blog_post:
                summary: Minimal blog post with required fields only
                value:
                  title: My First Blog Post
                  slug: my-first-blog-post
              draft_with_author_slug:
                summary: Draft post with author specified by slug
                value:
                  title: Draft Post Example
                  slug: draft-post-example
                  author:
                    slug: firstname-lastname
                  body: <p>This is a draft post.</p>
                  summary: A draft post example.
      responses:
        '202':
          description: >
            **Accepted - Post Creation Initiated**


            A validated POST request will return a response with HTTP status
            code 202. Full post creation occurs asynchronously, meaning a
            successfully created post may not show immediately.


            Posts are validated prior to returning the API response but it is
            also possible that post creation may fail after returning a 202
            response. If this happens please contact support.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BlogPostCreateSuccessResponse'
              example:
                status: pending
        '400':
          description: >
            **Bad Request - Validation Error**


            The API will return HTTP status code 400 if your data does not pass
            the initial validation stage. This may happen if:


            - You are missing a required field (title or slug)

            - The slug is already in use

            - The remote URL for a media field returns a 404

            - You attempt to schedule a blog post (not supported)

            - Invalid author email or slug provided


            Error explanations will be returned in a single JSON array.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorResponse'
              examples:
                missing_title:
                  summary: Missing required title field
                  value:
                    title:
                      - This field is required.
                missing_slug:
                  summary: Missing required slug field
                  value:
                    slug:
                      - This field is required.
                duplicate_slug:
                  summary: Slug already exists
                  value:
                    slug:
                      - Blog post with this slug already exists.
                scheduling_not_supported:
                  summary: Scheduling attempt during creation
                  value:
                    scheduled:
                      - >-
                        Scheduling a blog post during creation is not supported
                        by the write API at this time.
                scheduling_status_not_supported:
                  summary: Scheduled status not supported
                  value:
                    status_field:
                      - >-
                        Scheduling a blog post during creation is not supported
                        by the write API at this time.
                invalid_featured_image:
                  summary: Invalid featured image URL
                  value:
                    featured_image:
                      - Could not retrieve image from URL.
                unsupported_file_type:
                  summary: Unsupported media file type
                  value:
                    featured_image:
                      - >-
                        We don't support 'application/x-unknown' files, please
                        contact us about possible support.
                video_enterprise_only:
                  summary: Video upload requires enterprise plan
                  value:
                    featured_image:
                      - >-
                        Video upload is not allowed with your current plan.
                        Please contact support.
                svg_security_violation:
                  summary: SVG contains security risk
                  value:
                    featured_image:
                      - We don't allow <script> tag in the SVG files.
                invalid_author_email:
                  summary: Author with email not found
                  value:
                    author:
                      - Author with email 'nonexistent@email.com' not found.
                invalid_author_slug:
                  summary: Author with slug not found
                  value:
                    author:
                      - Author with slug 'nonexistent-author' not found.
        '401':
          $ref: '#/components/responses/UnauthorizedWriteResponse'
        '403':
          description: |
            **Forbidden - Blog Post Limit Exceeded or Insufficient Permissions**

            This can happen when:
            - Your organization has reached the blog post limit for your plan
            - Your API token does not have write permissions
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ErrorResponse'
                  - $ref: '#/components/schemas/LimitErrorResponse'
              examples:
                blog_post_limit:
                  summary: Blog post limit exceeded
                  value:
                    error: Blog post limit is over
                write_permission_denied:
                  summary: API token lacks write permissions
                  value:
                    detail: Your API token does not allow writes.
        '404':
          description: >
            **Not Found - Organization Blog Missing or Invalid Pagination**


            - Could not find this organization's blog. This is a rare error that
            typically indicates a configuration issue.

            - Invalid pagination parameters (page number out of range).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                blog_missing:
                  summary: Organization blog missing
                  value:
                    detail: Could not find this organization's blog
                invalid_pagination:
                  summary: Invalid pagination
                  value:
                    detail: Invalid Page.
      security:
        - writeTokenAuth: []
components:
  schemas:
    CreateBlogPostRequest:
      type: object
      description: Request schema for creating a new blog post
      required:
        - title
      properties:
        title:
          type: string
          description: The title of the blog post
          maxLength: 200
          example: This is a blog post
        slug:
          type: string
          description: >
            The slug of the blog post (must be unique).


            **Auto-Generation**: If not provided, the slug will be automatically
            generated from the title using unicode slugification. You can
            provide a custom slug or let the API generate one for you.
          maxLength: 100
          example: this-is-a-blog-post
        status:
          type: string
          description: The status of the post. Defaults to 'draft'. Cannot be 'scheduled'.
          enum:
            - draft
            - published
          default: draft
          example: published
        author:
          type: object
          description: >
            Author reference for blog posts. Author must exist in your account
            prior to POST.


            You can specify the author either by email or by slug. If no author
            is specified, defaults to the organization owner.
          oneOf:
            - type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                  description: Email address of the existing author
                  example: your@author.com
            - type: object
              required:
                - slug
              properties:
                slug:
                  type: string
                  description: Slug of the existing author
                  example: firstname-lastname
        categories:
          type: array
          description: >-
            Array of category names. Categories will be created if they don't
            exist.
          items:
            type: string
          example:
            - Recipes
            - Meals
        tags:
          type: array
          description: Array of tag names. Tags will be created if they don't exist.
          items:
            type: string
          example:
            - Butter
            - Sushi
            - Really Good Recipes
        featured_image:
          type: string
          format: uri
          description: >
            Can be a remote URL to an image.


            The image will always be uploaded to the media library (the value of
            `upload_images_to_media_library` is ignored for featured images).
          example: https://farm1.staticflickr.com/836/42903355654_8faa21171a_m_d.jpg
        featured_image_alt:
          type: string
          description: Alt text for the featured image
          maxLength: 250
          example: Featured image alt text example.
        body:
          type: string
          description: >
            Should be a string of escaped HTML.


            In case it contains any remote URLs to images, the
            `upload_images_to_media_library` flag controls the behavior.
          example: <h1>Butter</h1><p>I am so hungry!</p>
        summary:
          type: string
          description: Plain-text summary of the blog post
          maxLength: 2000
          example: This is a blog post summary.
        seo_title:
          type: string
          description: SEO title for HTML title tag
          maxLength: 100
          example: This is a blog post
        meta_description:
          type: string
          description: Meta description for SEO and social sharing
          example: This is a blog post to test the API.
        upload_images_to_media_library:
          type: boolean
          description: >
            Defaults to `false`.


            If set to `true`, any image URLs in the `body` property will be
            uploaded to the Butter Media Library and the original URL will be
            replaced with the media library URL.


            **Note**: Featured images are always uploaded regardless of this
            setting.
          default: false
          example: false
    BlogPostCreateSuccessResponse:
      type: object
      description: Success response for blog post creation (202 Accepted)
      properties:
        status:
          type: string
          enum:
            - pending
          description: >-
            Indicates that the blog post creation is being processed
            asynchronously
          example: pending
    ValidationErrorResponse:
      type: object
      description: >-
        Response for validation errors (400 Bad Request) - supports both
        field-specific errors and general error messages
      oneOf:
        - type: object
          description: >-
            Field-specific validation errors (used by Pages, Blog Posts, and
            Collections)
          additionalProperties:
            type: array
            items:
              type: string
          example:
            title:
              - You must specify a page title.
            slug:
              - You must specify a page slug.
        - type: object
          description: General error message (used for organization limits, etc.)
          properties:
            error:
              type: string
              description: Error message
          example:
            error: >-
              Cannot create a new Collection Item. Collection Item Limit
              Reached.
    ErrorResponse:
      type: object
      properties:
        detail:
          type: string
          description: Error message describing what went wrong
          example: Authentication credentials were not provided
    LimitErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Error message when subscription plan limits are exceeded
          example: Page creation limit is over
  responses:
    UnauthorizedWriteResponse:
      description: >
        **Unauthorized - Invalid or Missing Write API Token**


        No valid write API key provided. The API token you use for reading from
        the ButterCMS API

        will not allow you to create content. You need a different write-enabled
        token.

        Contact support@buttercms.com to get yours.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            detail: Authentication credentials were not provided
  securitySchemes:
    writeTokenAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >
        Write-enabled API token required for creating content.


        The API token you use for reading from the ButterCMS API will not allow
        you to create content in the API. For this you will need to use a
        different write-enabled token. Chat or email support@buttercms.com to
        get yours.


        Set the `Authorization` header to `Token your_write_api_token`.


        Example: `Authorization: Token abc123def456`


        Your write-enabled token should never be used anywhere it would be
        exposed, e.g. in client-side JavaScript.

````