Create a Post on WordPress.com Site using REST API

I already have a lot of tutorials about working with REST API on regular WordPress websites, but recently I have updated my Simple WordPress Crossposting plugin to make it compatible with websites hosted on wordpress.com as well.

Unexpectedly there is a huge difference between wordpress.org and wordpress.com APIs, so here is the tutorial where we are about to dive into the example of how create a post with a featured image.

OAuth2 Authentication

So, here is the first difference – while regular WordPress websites work with application passwords (it is like API public and secret keys), for wordpress.com you would need to create an application and obtain an access token for it (it is not that difficult as it sounds).

Create an App

In order to do so please proceed to My Applications page on wordpress.com developers website and click the button Create New Application.

create an application wordpress.com
As you can see I added localhost to Redirect URLs, so I can create an OAuth flow from my localhost.

The thing to keep in mind here is what you are about to put into Redirect URLs field – you have to specify the website domains you are going to work with API from.

And yes, once the app is created you can find its Client ID and Client Secret if you just scroll down a little bit.

wordpress.com app client ID and client secret
We will need it in a while. Or, if you are using my plugin, you can use this data to add a site in settings.

Obtain Access Token

Here we need to do two things, the first one is to redirect users to wordpress.com API dialog where they can Approve or Deny application access.

$redirect = add_query_arg(
	array(
		'client_id' => 12345,
		'redirect_uri' => 'http://localhost',
		'response_type' => 'code',
		'scope' => 'posts media',
	),
	'https://public-api.wordpress.com/oauth2/authorize'
);
wp_redirect( $redirect );
exit;
  • redirect_uri is where the users will be redirected when they Approve or Deny application access (it should be added in your app settings as well, don’t forget).
  • scope is what we allow the application to do on our website. You can find the full list of available options in the official docs. In this specific case I allowed the application to manage posts and media.
approve or deny wordpress.com application access
The application Approve/Deny dialog.

Ok, once you Approve/Deny you will be redirected to redirect_uri but with some additional query parameters – you will have $_GET[ 'code' ] if everything went well or $_GET[ 'error_description' ] if you didn’t approve for example. And we are going to use the code in another API request in order to get an access token.

Here it is:

$request = wp_remote_post(
	'https://public-api.wordpress.com/oauth2/token',
	array(
		'body' => array(
			'client_id' => 12345,
			'client_secret' => 'CLIENT SECRET',
			'redirect_uri' => 'http://localhost',
			'code' => $_GET[ 'code' ],
			'grant_type' => 'authorization_code'
		)
	)
);

if( 'OK' === wp_remote_retrieve_response_message( $request ) ) {
	$site = json_decode( wp_remote_retrieve_body( $request ) );		
	echo $site->access_token;
}

You will also have $site->blog_id, $site->blog_url as a result of this API request, but not a site name unfortunately, but you can get it easily – just send an unauthorized GET request to /rest/v1.1/sites/{$site->blog_id}.

Well, finally we have the access token and can actually work with WordPress.com REST API.

Create a Post

I am going to create a simple draft article (post type post).

create a new post using WordPress.com REST API

Once we got an access token creating a new post becomes quite easy.

$site_id = 12345; // Site ID or domain
$access_token = ''; // We got it before

$request = wp_remote_post(
	"https://public-api.wordpress.com/rest/v1.1/sites/$site_id/posts/new",
	array(
		'headers' => array(
			'Authorization' => "BEARER $access_token"
		),
		'body' => array(
			'title' => 'My test article',
			'status' => 'draft',
			'type' => 'post', // it is the default anyway
			'content' => 'Hello, it is the content',
			'tags' => 'travel,tips'
		)
	)
);

if( 'OK' === wp_remote_retrieve_response_message( $request ) ) {
	$post = json_decode( wp_remote_retrieve_body( $request ) );
	echo "New post ID: {$post->ID}";
}
  • Please take a look at type parameter – post types besides post and page need to be whitelisted with rest_api_allowed_post_types.
  • Also when working with metadata parameter (I didn’t use it here) keep in mind that all public meta keys are available by default but protected ones should be also whitelisted with the rest_api_allowed_public_metadata filter hook.
  • Featured image can be added to a post with the help of featured_image body argument but be aware that you need to pass an attachment ID there, in order to do so, upload it first.
  • More parameters can be found in the official docs.

But I also promised you to explain how to upload a featured image to this post. Easy enough – just specify featured_image parameter in the body of the request.

$request = wp_remote_request(
	"https://public-api.wordpress.com/rest/v1.1/sites/$site_id/media/new",
	array(
		'method' => 'POST',
		'timeout' => 30, // we need it in case of slow connection
		'headers' => array(
			'Authorization' => "BEARER $access_token",
			'Content-Type' => 'application/x-www-form-urlencoded'
		),
		'body' => array(
			'media_urls' => array( 
				$url, // you can upload multiple media files as you might've guessed
			),
			'attrs' => array(
				array(
					'alt' => get_post_meta( $id, '_wp_attachment_image_alt', true ),
					// you can pass 'title' and 'caption' here as well
				),
			)
		)
	)
);

if( 'OK' === wp_remote_retrieve_response_message( $request ) ) {
	$media = json_decode( wp_remote_retrieve_body( $request ) );
	echo "Image ID: {$media->media[0]->ID}, URL: {$media->media[0]->URL}";
}

It is not necessary should be a featured image, using /media/new API endpoint you can upload multiple media files as well, just pass an array of URLs in media_urls argument and an array of arrays with media attributes like alt text, caption etc in attrs body argument.

And yes, your image shouldn’t be on localhost.

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 Twitter