WordPress REST API Authentication and Create Posts

Reading public posts needs no login, but to create, change or delete anything, or to see private data, the API must know who you are. WordPress has two main ways to do it.

MethodUsed byHow it works
Cookie and nonceScripts running inside the logged in dashboard or theme (like the block editor)The login cookie plus the X-WP-Nonce header
Application passwordsOutside apps, scripts and other serversA special password per app, sent with Basic authentication

Without a login

Terminal output

$ curl -s -X POST "http://example.com/wp-json/wp/v2/posts" -d "title=Hello" | jq -c '{code, message, status: .data.status}'
{"code":"rest_cannot_create","message":"Sorry, you are not allowed to create posts as this user.","status":401}

The server answered 401. Nobody is logged in, so the request is refused.

Application passwords

Application passwords are built in since WordPress 5.6. Each user can create many of them, in Users > Profile > Application Passwords, give them a name and delete them at any time. They only work for the REST API (and XML-RPC), and not for the normal login. WordPress shows the password only once, in this style:

abcd EFgh 1234 ijkl 5678 mnop

You can also create one from the command line: wp user application-password create admin "My App" --porcelain. Then you send the username and this password with Basic authentication (curl -u). On a live site your site must use HTTPS for this to work (WordPress allows it on local and development sites only).

Create a post

Replace APP_PASSWORD with the password of your app:

Terminal output

$ curl -s -u "admin:APP_PASSWORD" -X POST "http://example.com/wp-json/wp/v2/posts" -d "title=Created with the API" -d "content=Hello from curl" -d "status=publish" | jq -c '{id, status, slug, author, type}'
{"id":5,"status":"publish","slug":"created-with-the-api","author":1,"type":"post"}

The answer has the code 201 and the new post (we show only a few fields). You can also send a JSON body, which is the usual way in applications:

Terminal output

$ curl -s -u "admin:APP_PASSWORD" -X POST "http://example.com/wp-json/wp/v2/posts" -H "Content-Type: application/json" -d '{"title":"A draft from JSON","status":"draft"}' | jq -c '{id, status}'
{"id":6,"status":"draft"}

Update a post

Send the new values to the route of that post (POST, PUT or PATCH all work):

Terminal output

$ curl -s -u "admin:APP_PASSWORD" -X POST "http://example.com/wp-json/wp/v2/posts/6" -H "Content-Type: application/json" -d '{"title":"Created with the API (edited)"}' | jq -c '{id, title: .title.rendered}'
{"id":6,"title":"Created with the API (edited)"}

Delete a post

A normal delete moves the post to the trash. With force=true it is removed forever:

Terminal output

$ curl -s -u "admin:APP_PASSWORD" -X DELETE "http://example.com/wp-json/wp/v2/posts/6" | jq -c '{status: .status}'
{"status":"trash"}
$ curl -s -u "admin:APP_PASSWORD" -X DELETE "http://example.com/wp-json/wp/v2/posts/6?force=true" | jq -c '{deleted, previous: .previous.status}'
{"deleted":true,"previous":"trash"}

Wrong password

If the password is wrong, WordPress does not say “wrong password”. It just treats you as a guest, so you get the same 401 error as before:

Terminal output

$ curl -s -u "admin:wrong password" -X POST "http://example.com/wp-json/wp/v2/posts" -d "title=Hello" | jq -c '{code, status: .data.status}'
{"code":"rest_cannot_create","status":401}

Cookie authentication with a nonce

When your JavaScript runs on a page of the logged in user, the browser already sends the cookie. WordPress also needs a nonce in the X-WP-Nonce header, to stop other sites from using your cookie. We log in like a browser and use the nonce:

Terminal output

$ NONCE=$(curl -s -b cookies.txt "http://example.com/wp-admin/admin-ajax.php?action=rest-nonce"); curl -s -b cookies.txt -H "X-WP-Nonce: $NONCE" -X POST "http://example.com/wp-json/wp/v2/posts" -H "Content-Type: application/json" -d '{"title":"Made with cookie and nonce","status":"draft"}' | jq -c '{id, status}'
{"id":8,"status":"draft"}
$ curl -s -b cookies.txt -X POST "http://example.com/wp-json/wp/v2/posts" -H "Content-Type: application/json" -d '{"title":"No nonce","status":"draft"}' | jq -c '{code, status: .data.status}'
{"code":"rest_cannot_create","status":401}

The second request has the cookie but no nonce, so WordPress treats the user as logged out. In your own theme or plugin script, you give the nonce to JavaScript with wp_create_nonce( 'wp_rest' ).

Which one to use?

  • Your own theme or plugin JavaScript: cookie and nonce.
  • A mobile app, a script or another server: application passwords over HTTPS.
  • Never put the admin username and password in a script. An application password is safer, because you can delete it at any time.
Note: Create the application password for a user with the lowest role which can do the job, not for the admin. If an app only has to add posts, use an author. Next: Custom REST Endpoint.

Congratulations! Chapter Finished. Learn more about the similar topics:
Exercises & Assignments
No Content Found.
Interview Questions & Answers
No Content Found.