Table of Contents

  1. What wp_insert_post() does
  2. Key $postarr fields
  3. Safe draft-first example
  4. Insert versus update
  5. Common mistakes
  6. Testing checklist
  7. Conclusion

What wp_insert_post() does

wp_insert_post() is the WordPress core function that inserts a new post into the database or updates an existing one. If the $postarr array contains a non-zero ID, WordPress updates that post. If it does not, WordPress creates a new post from the supplied values.

The current WordPress wp_insert_post() reference documents this signature:

wp_insert_post( array $postarr, bool $wp_error = false, bool $fire_after_hooks = true );

On success it returns the post ID. On failure it returns 0, or a WP_Error object when the second argument is true.

This article is the function reference. For the older how-to that walks through creating a post or page in PHP, see Programmatically Create a WordPress Post or Page with PHP.


Key $postarr fields

Paper-craft diagram of building an array of index cards, inserting a card through a wooden slot, and checking a numbered post ID ticket.
Build the array first, insert or update once, then treat only a real post ID as success.

You do not need every field. These are the ones that most custom code should set deliberately.

  • post_title and post_content are the required content fields. WordPress can reject an empty title, content, and excerpt combination as empty content.
  • post_status defaults to draft. That default is useful. Do not switch it to publish until the data is trusted and reviewed.
  • post_type defaults to post. Set page or a registered custom post type when that is what you intend to create.
  • post_author defaults to the current user ID.
  • post_name becomes the slug. If you omit it, WordPress sanitizes the title when the status is not a draft or pending post.
  • post_category must be an array of category IDs, even when you assign only one category.
  • tags_input accepts tag names, slugs, or IDs.
  • tax_input assigns custom taxonomy terms. Hierarchical taxonomies need term IDs. The current user also needs the assign_terms capability for that taxonomy, otherwise WordPress ignores the list.
  • meta_input writes post meta after the insert. It has been supported since WordPress 4.4.
  • ID turns the call into an update of that existing post.

If the incoming title came from a form, strip tags first. WordPress sanitizes post data through sanitize_post(), but the official security note still recommends wp_strip_all_tags() on titles and similar fields.


Safe draft-first example

The following snippet creates a draft post, returns a WP_Error on failure, and only continues when a real post ID comes back. Add it through Code Snippets or a small site-specific plugin and test it on staging.

/**
 * Create a draft WordPress post from trusted values.
 *
 * @param string $title   Post title.
 * @param string $content Post content.
 * @return int|WP_Error
 */
function yoohoo_insert_draft_post( $title, $content ) {
	$postarr = array(
		'post_title'   => wp_strip_all_tags( $title ),
		'post_content' => $content,
		'post_status'  => 'draft',
		'post_type'    => 'post',
		'post_author'  => get_current_user_id(),
		'meta_input'   => array(
			'_yoohoo_created_by' => 'wp_insert_post_example',
		),
	);

	$result = wp_insert_post( $postarr, true );

	if ( is_wp_error( $result ) ) {
		return $result;
	}

	return (int) $result;
}

Keep the first save as a draft unless the workflow has already validated the content, author, and destination. Publishing immediately from untrusted request data is the most common production mistake.

Pro Tip: Pass true as the second argument so failures return a WP_Error instead of a silent 0. Check that return value before you enqueue a webhook or send a notification.


Insert versus update

wp_insert_post() can update an existing post when you include ID. wp_update_post() is the clearer wrapper for that case: it loads the current post, merges your changes, and then calls wp_insert_post().

Use wp_update_post() when you are changing a known post. Use wp_insert_post() without an ID when you are creating one. Do not invent an ID and hope WordPress creates it.

Both functions fire save_post and wp_insert_post. If your own save_post callback calls either function again, remove the callback first or you can create a loop. That is the same verify-the-hook habit as our wpuf_profile_update guide.


Common mistakes

Publishing on the first save

The default status is draft. Keep it until the post has been checked. A scheduled future status also needs a valid post_date.

Ignoring the return value

A 0 is a failure when $wp_error is false. A WP_Error is a failure when $wp_error is true. Do not treat either as a post ID.

Passing category names instead of IDs

post_category expects an array of integers. Names and slugs belong in tags_input or, for custom taxonomies, in tax_input with the correct format.

Assuming tax_input always applies

If the current user cannot assign terms for that taxonomy, WordPress skips the list. In privileged or CLI code, set terms afterward with wp_set_object_terms().

Creating posts from raw $_POST data

Strip tags from titles and other plain-text fields. Confirm capabilities and a nonce before any front-end form reaches wp_insert_post().


Testing checklist

  1. Create a draft and confirm it appears in Posts with the expected title and author.
  2. Pass an empty title, content, and excerpt and confirm the function fails instead of inserting a blank post.
  3. Set $wp_error to true and confirm a WP_Error is returned on a forced failure.
  4. Assign one category by ID and confirm it is attached.
  5. Include an ID only when you intend to update an existing post.
  6. If a successful insert should notify another system, send that event through WP Zapier after you have a real post ID.

Conclusion

wp_insert_post() is a small API with a strict contract: build a complete $postarr, start as a draft unless the data is already trusted, check the return value, and keep updates on a known post ID.

That produces a post you can review, hook, or send onward without publishing incomplete or unsafe content.

What would you create first as a draft: a support note, a product announcement, or a membership update?

Stay in the loop!

15% off your next purchase, just for you 🎁

Sign up to receive your exclusive discount, and keep up to date on our latest news, products & offers!

We don’t spam, ever! Read our privacy policy for more info.