How to Upload an Image using WordPress REST API

In this tutorial, I am going to show three ways of how you can upload an image to a remote WordPress site using the REST API.

  • In the first way, we’re going to do it using WordPress HTTP API functions: wp_remote_post() or wp_remote_request(),
  • In the second way, I will show you how to do the same with cURL; why not?
  • Finally, in the third way, I will show you an example of how to upload media with the WordPress REST API using JavaScript.

The methods here are also used by my Simple WP Crossposting plugin, because, you know, when you crosspost a post to another site with the REST API, you definitely need to copy its images as well, at least the featured image.

All right, let’s just dive into it.

Upload an Image using REST API and WordPress HTTP API

WordPress has a bunch of convenient functions that happen to be a part of its HTTP API: wp_remote_get(), wp_remote_post(), wp_remote_request(), etc, so why not use them when sending our REST API requests?

Let’s start with the example when we know only an image URL:

/*
 * @snippet: WordPress API upload image with the wp_remote_post() function
 * @author: Misha Rudrastyh
 * @url: https://rudrastyh.com/wordpress/upload-featured-image-rest-api.html#using-http-api-functions
 */
$url = 'FILE URL';
$file = file_get_contents( $url );

$response = wp_remote_post(
	'WEBSITE-DOMAIN/wp-json/wp/v2/media',
	array(
		'headers' => array(
			'Authorization' => 'Basic ' . base64_encode( "$login:$password" ),
			'Content-Disposition' => 'attachment; filename="' . basename( $url ) . '"',
			'Content-Type' => wp_get_image_mime( $url ), // or mime_content_type()
		),
		'body' => $file,
	)
);

if( 'Created' === wp_remote_retrieve_response_message( $response ) ) {
	$body = json_decode( wp_remote_retrieve_body( $response ) );
	$featured_image_id = $body->id;
}

However, when you’re working with images on the same site from which you’re sending REST API requests, I’d recommend that you better use fopen() instead of file_get_contents(), because it happens to be incredibly faster. But in that case, we would need to work with an image ID.

Below are the code lines we need to replace:

$image_id = 'IMAGE ID HERE';
$path = get_attached_file( $image_id );
$handle = fopen( $path, "r" );
$file = stream_get_contents( $handle );
fclose( $handle );

Probably it would also be better to replace these lines as well:

'Content-Disposition' => 'attachment; filename="' . basename( $path ) . '"',
'Content-Type' => get_post_mime_type( $image_id ),

Common errors when uploading an image with the REST API

Sometimes you might face errors, mentioned below, however, they can be fixed quite easily:

  • “Sorry, you are not allowed to upload this file type” (Response code 500) – It means that you didn’t provide the file inside the request. In the example above, we get the file by the URL with the file_get_contents() function or by the ID with the fopen() function, and then we pass this whole thing into the body of the request. Yes, in the official REST API documentation, it is mentioned that you can pass image title, caption, etc, but it didn’t work out for me, so it seems like if you would like to add a media title, you have to do a second API request. Please don’t add anything in wp-config.php to fix this error message.
  • “No Content-Disposition supplied” (Response code 400) – This one is simple to fix; you just have to pass a Content-Disposition header in the following format: attachment; filename="image.jpg".

Updating image information (alt, title, caption text)

Well, uploading an image with the API is one thing, but in order to provide some of its information, you will need to send an additional REST API request. I mean these fields:

WordPress API upload image and update its alt text and caption

Let’s try an example now – just to update an image alt text. So we need to send a POST request to this endpoint /wp/v2/media/{$image_id}.

wp_remote_post(
		"WEBSITE-DOMAIN/wp-json/wp/v2/media/{$image_id}",
		array(
			'headers' => array(
				'Authorization' => 'Basic ' . base64_encode( "$login:$password" ),
			),
			'body' => array(
				'alt_text' => 'looks like a mountain',
				//'caption'  => '',
				//'description' => '',
				//'title' => '',
			),
		)
);

Set a Post Featured Image

In this blog before, we’ve already covered post creation with REST API below is just a simple example of how you can set our uploaded image as a featured image to a post. Yes, that’s going to be plus one REST API request.

wp_remote_post(
		"WEBSITE-DOMAIN/wp-json/wp/v2/posts/{$post_id}",
		array(
			'headers' => array(
				'Authorization' => 'Basic ' . base64_encode( "$login:$password" ),
			),
			'body' => array(
				'featured_media' => $featured_image_id,
			),
		)
);

That’s pretty much it.

upload featured image to a post with WordPress REST API

Upload an Image using WordPress API and cURL

If, for some reason, you can not use WordPress HTTP API functions to send REST API requests, then probably cURL is the way for you.

In that case, you need to check out the example below:

/*
 * @snippet: WordPress API upload image using cURL
 * @author: Misha Rudrastyh
 * @url: https://rudrastyh.com/wordpress/upload-featured-image-rest-api.html#using-curl
 */
$url = 'FILE URL';

$curl = curl_init();
curl_setopt_array(
	$curl,
	array(
		CURLOPT_URL => "WEBSITE-DOMAIN/wp-json/wp/v2/media",
		CURLOPT_RETURNTRANSFER => true,
		CURLOPT_CUSTOMREQUEST => "POST",
		CURLOPT_HTTPHEADER => array(
			"Authorization: Basic " . base64_encode( "$login:$password" ),
			"Content-Disposition: attachment; filename=" . basename( $url ),
			"Content-Type: " . wp_get_image_mime( $url ),
		),
		CURLOPT_POSTFIELDS => file_get_contents( $url ),
	)
);
// run the cURL and get its reponse
$response = curl_exec( $curl );
// check for errors
$error = curl_error( $curl );
// close the cURL connection
curl_close( $curl );
// if an error, print it, otherwise just print the JSON response from REST API
if( $error ) {
	echo 'cURL Error #:' . $error;
} else {
	echo( $response );
}

Upload an Image with WordPress REST API and JavaScript

The JavaScript way is a little bit tricky because we need to make sure to keep our username and application password secure. In the example below, I hardcoded them straight into the source code. Should I remind you that you can not do that for public web applications?

First of all, let’s create an HTML form. We can start small, just by adding a file field, <input type="file" />, and a button.

<form id="rudr_form">
	<input type="file" name="rudr_file" />
	<button>Upload with REST API</button>
</form>

Now, it is time for JavaScript:

const form = document.getElementById( 'rudr_form' )

form.addEventListener( 'submit', ( event ) => {
	// prevent the standard form sending
	event.preventDefault()

	const fileInput = form.rudr_file
	const formData = new FormData()
	
	// add the uploaded file to the form data
	formData.append( 'file', fileInput.files[0] )
	
	const endpoint = 'DOMAIN HERE/wp-json/wp/v2/media'
	const username = 'misha'
	const pwd = '' // Application password
	
	// sending a REST API request in JS
	fetch( endpoint, {
		method : "POST",
		headers : {
			Authorization: "Basic " + window.btoa( username + ':' + pwd )
		},
		body: formData
	})
	.then( res => res.json() )
	.then( data => {
		console.log( data )
	} )
	.catch( err => {
		console.log( err )
	} )
} )
Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 15 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