Bulk Create Posts Using REST API

Not so far time ago I published a similar tutorial for WooCommerce, actually I wasn’t sure that WordPress REST API itself accepts batch requests as well, because of lack of its documentation. But now I have figured it out and would like to share it with you.

In this tutorial we are going to create a bulk action like this one for posts and pages:

Publishing multiple posts at once to the other site using batch requests in WordPress REST API
When we select “Publish to other site” the posts are going to be published to a completely separate WordPress website with the help of REST API. And it is going to be just a single batch request.

Actually if you don’t want to deal with the code, you can check my Simple WP Crossposting plugin which allows to do exactly the same.

Now let’s get started.

First things first we need to display our custom option in bulk actions. It can be done easily with both bulk_actions-edit-{post type} and handle_bulk_actions-edit-{post type} hooks. Because we are about to do it for two post types – post and page, there are going to be 4 hooks in total:

// for Posts
add_filter( 'bulk_actions-edit-post', 'rudr_bulk_option' );
add_filter( 'handle_bulk_actions-edit-post', 'rudr_do_bulk_action', 10, 3 );
// for Pages
add_filter( 'bulk_actions-edit-page', 'rudr_bulk_option' );
add_filter( 'handle_bulk_actions-edit-page', 'rudr_do_bulk_action', 10, 3 );

If you have plans about using it for more post types, please consider using a loop like this:

$post_types = array( 'post', 'page', 'misha_custom_post_type' );
foreach( $post_types as $post_type ) {
	add_filter( 'bulk_actions-edit-' . $post_type, 'rudr_bulk_option' );
	add_filter( 'handle_bulk_actions-edit-' . $post_type, 'rudr_do_bulk_action', 10, 3 );

We have two callback functions so far:

  1. rudr_bulk_option() – adds a custom bulk action into a list of bulk actions,
  2. rudr_do_bulk_action() – processes it (to bulk publish posts to the other sites in other words).

Let’s begin with the first callback function which is the simplest one:

function rudr_bulk_option( $bulk_actions ) {
	$bulk_actions[ 'publish_to_other_site' ] = 'Publish to the other site';
	return $bulk_actions;


Yes, it is super simple, even after using this code we already have something:

custom bulk action in WordPress

Of course if you select this new bulk action and try to execute it, nothing will happen. And actually the main part of the code is below:

function rudr_do_bulk_action( $redirect, $doaction, $object_ids ) {

	// exit the function if it is not our custom bulk action
	if( 'publish_to_other_site' !== $doaction ) {
		return $redirect;

	// we are going to use it to get a post type name
	$screen = get_current_screen();
	// all the batch requests are going to be in this array
	$requests = array();

	foreach( $object_ids as $object_id ) {

		$post = get_post( $object_id );

		// add a part to our batch request
		$requests[] = array(
			'method' => 'POST',
			'path' => ( 'page' === $screen->post_type ? '/wp/v2/pages' : '/wp/v2/posts' ),
			'body' => array(
				'status' => $post->post_status,
				'title' => $post->post_title,
				'type' => $post->post_type,
				'content' => $post->post_content,
				'excerpt' => $post->post_excerpt,
	$request = wp_remote_post(
			'headers' => array(
				'Authorization' => 'Basic ' . base64_encode( "{$login}:{$pwd}" )
			'body' => array(
				'requests' => $requests
	if( 207 === wp_remote_retrieve_response_code( $request ) ) {
		$redirect = add_query_arg(
				'sync_done' => count( $object_ids ),
	return $redirect;

Let me talk a little bit more about this piece of code

And of course let’s don’t forget about admin notices:

add_action( 'admin_notices', 'rudr_notices' );

function rudr_notices() {
	// do nothing if we're on other admin pages
	$screen = get_current_screen();
	if( 'edit' !== $screen->base ) {
	if( ! empty( $_REQUEST[ 'sync_done' ] ) ) {
		printf( '<div class="updated notice is-dismissible"><p>' . _n( '%s item has been successfully published.', '%s items have been successfully published.', absint( $_REQUEST[ 'sync_done' ] ) ) . '</p></div>', absint( $_REQUEST[ 'sync_done' ] ) );


Final result:

Publishing multiple posts at once to the other site using batch requests in WordPress REST API

Updating posts or pages which have already been published

This part actually can be tricky.

In order to apply the bulk action to the posts that have already been published and to avoid duplicates at the same time, we somehow need to connect posts between Site 1 and Site 2. In my opinion it can be implemented two ways – you can either send 1 more API request to search the selected posts by slugs or you can just store an ID of a post copy in custom fields. I prefer the second way for sure because it is faster and allows to have different slugs for posts.

A small hint how to implement it:

if( 207 === wp_remote_retrieve_response_code( $request ) ) {
	$body = json_decode( wp_remote_retrieve_body( $request ), true );
	if( $body[ 'responses' ] ) {
		for( $i = 0; $i < count( $body[ 'responses' ] ); $i++ ) {
			// do something to connect posts, for example this:
			$id = isset( $body[ 'responses' ][ $i ][ 'body' ][ 'id' ] ) ? absint( $body[ 'responses' ][ $i ][ 'body' ][ 'id' ] ) : 0;
			update_post_meta( $object_ids[ $i ], 'post_copy_id', $id );

Oh, and don’t forget that we should change a REST API endpoint when we are updating a post.

Or you can just use my plugin for that 🙂

Maximum requests within a single batch

By default WordPress REST API allows to use up to 25 requests within a single batch, but guess what, you can change it with the help of a filter hook rest_get_max_batch_size.

For example like this:

add_filter( 'rest_get_max_batch_size', 'rudr_max_batch_size' );

function rudr_max_batch_size() {
	return 50;

Of course I recommend you to be careful with this hook.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X