Mike Head

July 14, 2017

The WordPress REST API


image conveying post content

Diving into the REST:

Once I had WordPress running on Docker, it was time to investigate the WordPress REST API. This API, available in WordPress core since release 4.7, is accessible right out of the box. To test it's presence:
http://localhost:8000/?rest_route=/

However trying to get read access to the sample posts:
http://localhost:8000/wp-json/wp/v2/posts
Fails with 404 not found.
To fix this enable pretty links, which allows a better way to access the api

And test via the browser:
localhost:8000/wp-json/wp/v2/posts ...show the hello world post
localhost:8000/wp-json/wp/v2/categories ...list the 'uncategorized' category
localhost:8000/wp-json/wp/v2/tags ...should be empty, no tags yet
localhost:8000/wp-json/wp/v2/users ...list the user added during installation

Testing with curl

curl GET test

curl -X GET localhost:8000/wp-json/wp/v2/categories/
[  
   {  
      "id":1,
      "count":1,
      "description":"",
      "link":"http:\/\/localhost:8000\/category\/uncategorized\/",
      "name":"Uncategorized",
      "slug":"uncategorized",
      "taxonomy":"category",
      "parent":0,
      "meta":[  

      ],
      "_links":{  
         "self":[  
            {  
               "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/categories\/1"
            }
         ],
         "collection":[  
            {  
               "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/categories"
            }
         ],
         "about":[  
            {  
               "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/taxonomies\/category"
            }
         ],
         "wp:post_type":[  
            {  
               "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/posts?categories=1"
            }
         ],
         "curies":[  
            {  
               "name":"wp",
               "href":"https:\/\/api.w.org\/{rel}",
               "templated":true
            }
         ]
      }
   }
]

OK that was successful, next up try to POST some data.

curl simple POST test

curl -X POST "localhost:8000/wp-json/wp/v2/tags?name=friends&slug=friends&description=friendly%20friends"
{  
   "code":"rest_cannot_create",
   "message":"Sorry, you are not allowed to create new terms.",
   "data":{  
      "status":401
   }
}

The simple POST test failed, essentially saying that the anonymous user is not allowed to update.

Add Basic Authentication

There is a basic-auth plug-in for Wordpress. Note this plugin should only be used for development purposes.

Here's a basic POST test, adding a new category. Note that your user/password needs to be substituted. Also note the id that is returned ("id":4 in this case), as that will be used later:

curl  --user user:password -X POST "localhost:8000/wp-json/wp/v2/categories?name=news&slug=news&description=News"
{  
   "id":4,
   "count":0,
   "description":"News",
   "link":"http:\/\/localhost:8000\/category\/news\/",
   "name":"news",
   "slug":"news",
   "taxonomy":"category",
   "parent":0,
   "meta":[  

   ],
   "_links":{  
      "self":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/categories\/4"
         }
      ],
      "collection":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/categories"
         }
      ],
      "about":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/taxonomies\/category"
         }
      ],
      "wp:post_type":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/posts?categories=4"
         }
      ],
      "curies":[  
         {  
            "name":"wp",
            "href":"https:\/\/api.w.org\/{rel}",
            "templated":true
         }
      ]
   }
}

Now that the basic authentication plugin is activated, it can be used for POST, PUT, DELETE transactions.

curl POST Add new user

Here's another test, adding a new user:

curl --user user:password -X POST "localhost:8000/wp-json/wp/v2/users/" -H "Content-Type: application/json" -d '{"username":"test","email":"[email protected]","password":"testing"}'
{  
   "id":3,
   "username":"test",
   "name":"test",
   "first_name":"",
   "last_name":"",
   "email":"[email protected]",
   "url":"",
   "description":"",
   "link":"http:\/\/localhost:8000\/author\/test\/",
   "locale":"en_US",
   "nickname":"test",
   "slug":"test",
   "roles":[  
      "subscriber"
   ],
   "registered_date":"2017-07-14T18:23:02+00:00",
   "capabilities":{  
      "read":true,
      "level_0":true,
      "subscriber":true
   },
   "extra_capabilities":{  
      "subscriber":true
   },
   "avatar_urls":{  
      "24":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=24&d=mm&r=g",
      "48":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=48&d=mm&r=g",
      "96":"http:\/\/2.gravatar.com\/avatar\/55502f40dc8b7c769880b10874abc9d0?s=96&d=mm&r=g"
   },
   "meta":[  

   ],
   "_links":{  
      "self":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/users\/3"
         }
      ],
      "collection":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/users"
         }
      ]
   }
}   

curl POST Add new post

Here's another test, adding a minimal new post. In this case, the post created also has the "id":4

curl --user user:password -X POST "localhost:8000/wp-json/wp/v2/posts/" -H "Content-Type: application/json" -d '{"title":"My 1st Post","content":"My Content","excerpt":"My Excerpt"}'
{  
   "id":4,
   "date":"2017-07-14T18:48:20",
   "date_gmt":"2017-07-14T18:48:20",
   "guid":{  
      "rendered":"http:\/\/localhost:8000\/?p=4",
      "raw":"http:\/\/localhost:8000\/?p=4"
   },
   "modified":"2017-07-14T18:48:20",
   "modified_gmt":"2017-07-14T18:48:20",
   "password":"",
   "slug":"",
   "status":"draft",
   "type":"post",
   "link":"http:\/\/localhost:8000\/?p=4",
   "title":{  
      "raw":"My 1st Post",
      "rendered":"My 1st Post"
   },
   "content":{  
      "raw":"My Content",
      "rendered":"<p>My Content<\/p>\n",
      "protected":false
   },
   "excerpt":{  
      "raw":"My Excerpt",
      "rendered":"<p>My Excerpt<\/p>\n",
      "protected":false
   },
   "author":4,
   "featured_media":0,
   "comment_status":"open",
   "ping_status":"open",
   "sticky":false,
   "template":"",
   "format":"standard",
   "meta":[  

   ],
   "categories":[  
      1
   ],
   "tags":[  

   ],
   "_links":{  
      "self":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/posts\/4"
         }
      ],
      "collection":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/posts"
         }
      ],
      "about":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/types\/post"
         }
      ],
      "author":[  
         {  
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/users\/4"
         }
      ],
      "replies":[  
         {  
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/comments?post=4"
         }
      ],
      "version-history":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/posts\/4\/revisions"
         }
      ],
      "wp:attachment":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/media?parent=4"
         }
      ],
      "wp:term":[  
         {  
            "taxonomy":"category",
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/categories?post=4"
         },
         {  
            "taxonomy":"post_tag",
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/tags?post=4"
         }
      ],
      "curies":[  
         {  
            "name":"wp",
            "href":"https:\/\/api.w.org\/{rel}",
            "templated":true
         }
      ]
   }
}

curl PUT Update and existing post

Here's another test, updating the created post (id 4) with the new category (id 4).

curl  --user user:password -X PUT "localhost:8000/wp-json/wp/v2/posts/4?categories=4"
{  
   "id":4,
   "date":"2017-07-14T19:08:06",
   "date_gmt":"2017-07-14T19:08:06",
   "guid":{  
      "rendered":"http:\/\/localhost:8000\/?p=4",
      "raw":"http:\/\/localhost:8000\/?p=4"
   },
   "modified":"2017-07-14T19:08:06",
   "modified_gmt":"2017-07-14T19:08:06",
   "password":"",
   "slug":"",
   "status":"draft",
   "type":"post",
   "link":"http:\/\/localhost:8000\/?p=4",
   "title":{  
      "raw":"My 1st Post",
      "rendered":"My 1st Post"
   },
   "content":{  
      "raw":"My Content",
      "rendered":"<p>My Content<\/p>\n",
      "protected":false
   },
   "excerpt":{  
      "raw":"My Excerpt",
      "rendered":"<p>My Excerpt<\/p>\n",
      "protected":false
   },
   "author":4,
   "featured_media":0,
   "comment_status":"open",
   "ping_status":"open",
   "sticky":false,
   "template":"",
   "format":"standard",
   "meta":[  

   ],
   "categories":[  
      4
   ],
   "tags":[  

   ],
   "_links":{  
      "self":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/posts\/4"
         }
      ],
      "collection":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/posts"
         }
      ],
      "about":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/types\/post"
         }
      ],
      "author":[  
         {  
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/users\/4"
         }
      ],
      "replies":[  
         {  
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/comments?post=4"
         }
      ],
      "version-history":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/posts\/4\/revisions"
         }
      ],
      "wp:attachment":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/media?parent=4"
         }
      ],
      "wp:term":[  
         {  
            "taxonomy":"category",
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/categories?post=4"
         },
         {  
            "taxonomy":"post_tag",
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/tags?post=4"
         }
      ],
      "curies":[  
         {  
            "name":"wp",
            "href":"https:\/\/api.w.org\/{rel}",
            "templated":true
         }
      ]
   }
}

And the result showing in the list of posts, with the newly assigned "news" category:
Image showing WordPress Post list with new category assigned

curl POST Add new image file

This test adds an image to the Wordpress media collection:

curl --request POST \
  --url http://localhost:8000/wp-json/wp/v2/media \
  --user user:password \
  --header "cache-control: no-cache" \
  --header "content-disposition: attachment; filename=george.png" \
  --header "content-type: image/png" \
  --data-binary "@$HOME/Desktop/george.png" \
  --location
{  
   "id":6,
   "date":"2017-07-14T22:32:54",
   "date_gmt":"2017-07-14T22:32:54",
   "guid":{  
      "rendered":"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george.png",
      "raw":"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george.png"
   },
   "modified":"2017-07-14T22:32:54",
   "modified_gmt":"2017-07-14T22:32:54",
   "slug":"george",
   "status":"inherit",
   "type":"attachment",
   "link":"http:\/\/localhost:8000\/george\/",
   "title":{  
      "raw":"george",
      "rendered":"george"
   },
   "author":4,
   "comment_status":"open",
   "ping_status":"closed",
   "template":"",
   "meta":[  

   ],
   "description":{  
      "raw":"",
      "rendered":"<p class=\"attachment\"><a href='http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george.png'><img width=\"300\" height=\"300\" src=\"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george-300x300.png\" class=\"attachment-medium size-medium\" alt=\"\" srcset=\"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george-300x300.png 300w, http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george-150x150.png 150w, http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george-100x100.png 100w, http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george.png 500w\" sizes=\"100vw\" \/><\/a><\/p>\n"
   },
   "caption":{  
      "raw":"",
      "rendered":""
   },
   "alt_text":"",
   "media_type":"image",
   "mime_type":"image\/png",
   "media_details":{  
      "width":500,
      "height":500,
      "file":"2017\/07\/george.png",
      "sizes":{  
         "thumbnail":{  
            "file":"george-150x150.png",
            "width":150,
            "height":150,
            "mime_type":"image\/png",
            "source_url":"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george-150x150.png"
         },
         "medium":{  
            "file":"george-300x300.png",
            "width":300,
            "height":300,
            "mime_type":"image\/png",
            "source_url":"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george-300x300.png"
         },
         "twentyseventeen-thumbnail-avatar":{  
            "file":"george-100x100.png",
            "width":100,
            "height":100,
            "mime_type":"image\/png",
            "source_url":"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george-100x100.png"
         },
         "full":{  
            "file":"george.png",
            "width":500,
            "height":500,
            "mime_type":"image\/png",
            "source_url":"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george.png"
         }
      },
      "image_meta":{  
         "aperture":"0",
         "credit":"",
         "camera":"",
         "caption":"",
         "created_timestamp":"0",
         "copyright":"",
         "focal_length":"0",
         "iso":"0",
         "shutter_speed":"0",
         "title":"",
         "orientation":"0",
         "keywords":[  

         ]
      }
   },
   "post":null,
   "source_url":"http:\/\/localhost:8000\/wp-content\/uploads\/2017\/07\/george.png",
   "_links":{  
      "self":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/media\/6"
         }
      ],
      "collection":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/media"
         }
      ],
      "about":[  
         {  
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/types\/attachment"
         }
      ],
      "author":[  
         {  
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/users\/4"
         }
      ],
      "replies":[  
         {  
            "embeddable":true,
            "href":"http:\/\/localhost:8000\/wp-json\/wp\/v2\/comments?post=6"
         }
      ]
   }
}

And the result showing in the list of media items, with the newly uploaded "george" image:
Image showing WordPress Image list with newly uploaded image