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()orwp_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 thefopen()function, and then we pass this whole thing into thebodyof 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 inwp-config.phpto 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:

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 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
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
great help Misha, thanks!
you’re always welcome, Casey!
Hi Misha!
Great post – you really simplified the whole thing – thank you!
A great future post might be how to host those images on say aws s3.
Do you have knowledge of that? I’ve read several articles, but all seem to require a plug in. I’m guessing they’re changing some php scripts to accommodate the external url.
Thanks again, Tony
hi, thanks for this helpful post!
I was wondering, how do I add images to the body of a post, and specify where those images are placed inside the post using the REST API?
do you found the way to do it?
Please forgive my ignorance here…still very much in the learning phase. I can successfully create a WP post by API, even working with pre-built post template in Elementor. For this I make a HTTP Post request, authentication all good, etc.
I’d like to also populate the post’s Featured Image with an image from a URL.
I know how to make some HTTP requests via low-code automation platforms like n8n. And I’ve tried to make a POST request with Headers:
Content-Type: image/jpeg
Content-Disposition: attachment; filename=tmp
cache-control: no-cache
Accept: application/json
But for the Body of the request I tried pushing in the binary (confirmed the binary is the content of the file). I got a 500 Sorry you aren’t allowed to upload error, so somehow the binary data isn’t going into the body of the request.
I’ll admit I’m a fish out of the water here. I can make HTTP requests via low-code platforms. I can get myself around WP for all things web design. I can even read and understand PHP but writing it and any hard coding HTTP requests I’ve got no experience…
The more I think about it, the less I even know what question to ask here.
Hi, many thanks for your tutorial.
With GUZZLE I will receive every time an Error 500 but I have passed all headers and file into the body.
Any ideas?
It worked for me, thank you!