Browse: Home / REST API Handbook

REST API Handbook  Edit

TOPICS

- What Is A REST API?

- Using the WordPress REST API

- Next Steps

The WordPress REST API provides an interface for applications to interact with your WordPress site by sending and receiving data as JSON (JavaScript Object Notation) objects. It is the foundation of the WordPress Block Editor, and can likewise enable your theme, plugin or custom application to present new, powerful interfaces for managing and publishing your site content.

Using the WordPress REST API you can create a plugin to provide an entirely new admin experience for WordPress, build a brand new interactive front-end experience, or bring your WordPress content into completely separate applications.

The REST API is a developer-oriented feature of WordPress. It provides data access to the content of your site, and implements the same authentication restrictions — content that is public on your site is generally publicly accessible via the REST API, while private content, password-protected content, internal users, custom post types, and metadata is only available with authentication or if you specifically set it to be so. If you are not a developer, the most important thing to understand about the API is that it enables the block editor and modern plugin interfaces without compromising the security or privacy of your site.

What Is A REST API?

An API is an Application Programming Interface. REST, standing for “REpresentational State Transfer,” is a set of concepts for modeling and accessing your application’s data as interrelated objects and collections. The WordPress REST API provides REST endpoints (URLs) representing the posts, pages, taxonomies, and other built-in WordPress data types. Your application can send and receive JSON data to these endpoints to query, modify and create content on your site. JSON is an open standard ata format that is lightweight and human-readable, and looks like Objects do in JavaScript. When you request content from or send content to the API, the response will also be returned in JSON. Because JSON is widely supported in many programming languages, developers can build WordPress applications in client-side JavaScript (like the block editor), as mobile apps, or as desktop or command line tools.

Note:

The REST API is just one of many APIs provided by WordPress. You can find the documentation on these additional APIs here.

Top ↑

Using the WordPress REST API

WordPress already provides a rich set of tools and interfaces for building sites, and you should not feel pressured to use the REST API if your site is already working the way you expect. You do not need to use the REST API to build a WordPress theme or plugin.

However, if you do wish to write your theme, plugin, or external application as a client-side JavaScript application, or a standalone program in a language other than PHP, then your application will need a structured way to access content within your WordPress site. Any programming language which can make HTTP requests and interpret JSON can use the REST API to interact with WordPress, from PHP, Node.js, Go, and Java, to Swift, Kotlin, and beyond.

Even if you’re using vanilla JavaScript or jQuery within a theme or plugin the REST API provides a more predictable and structured way to interact with your site’s content than admin-ajax, enabling you to spend less time accessing the data you need and more time creating better user experiences.

If you want a structured, extensible, and simple way to get data in and out of WordPress, you probably want to use the REST API.

For all of its simplicity the REST API can feel quite complex at first, so in this handbook we will attempt to break it down into smaller components to explain each part of the full puzzle.

Top ↑

Next Steps

Familiarize yourself with the key technical concepts behind how the REST API functions.

Learn more about how to interact with API resources and query for specific data in the Using the REST API section.

Once you’re comfortable with the default workings of the default routes and methods, discover how to add new data to the API or enhance and manipulate existing response objects in the Extending the REST API section.

For a comprehensive overview of the resources and routes available by default, review the API reference.

Browse: Home / REST API Handbook / Key Concepts

Key Concepts  Edit

TOPICS

- Routes & Endpoints

- Requests

- Responses

- Schema

- Controller Classes

In this page we’ll break down some of the key concepts and terms associated with the REST API: Routes & Endpoints, Requests, Responses, Schema, and Controller Classes. Each of these concepts play a crucial role in understanding, using, and extending the WordPress REST API, and each is explored in greater depth within this handbook.

Routes & Endpoints

In the context of the WordPress REST API a route is a URI which can be mapped to different HTTP methods. The mapping of an individual HTTP method to a route is known as an endpoint.

As an example, if we make a GET request to the URI http://oursite.com/wp-json/ we are returned a JSON response showing what routes are available, and what endpoints are available within each route. /wp-json/ is a route, and when that route receives a GET request then that request is handled by the endpoint which displays what is known as the index for the WordPress REST API. The route wp-json/wp/v2/posts by contrast has a GET endpoint which returns a list of posts, but also a POST endpoint which accepts authenticated requests to create new posts.

We will learn how to register our own routes and endpoints in the following sections.

Note:

If you are using non-pretty permalinks, you should pass the REST API route as a query string parameter. The route http://oursite.com/wp-json/ in the example above would hence be http://oursite.com/?rest_route=/.

If you get a 404 error when trying to access http://oursite.com/wp-json/, consider enabling pretty permalinks or try using the rest_route parameter instead.

Top ↑

Requests

A REST API request is represented within WordPress by an instance of the WP_REST_Request class, which is used to store and retrieve information for the current request. A WP_REST_Request object is automatically generated when you make an HTTP request to a registered API route. The data specified in this object (derived from the route URI or the JSON payload sent as a part of the request) determines what response you will get back out of the API.

Requests are usually submitted remotely via HTTP but may also be made internally from PHP within WordPress plugin or theme code. There are a lot of neat things you can do using this class, detailed further elsewhere in the handbook.

Top ↑

Responses

Responses are the data you get back from the API. The WP_REST_Response class provides a way to interact with the response data returned by endpoints. Responses return the requested data, or can also be used to return errors if something goes wrong while fulfilling the request.

Top ↑

Schema

Each endpoint requires a particular structure of input data, and returns data using a defined and predictable structure. Those data structures are defined in the API Schema. The schema structures API data and provides a comprehensive list of all of the properties the API can return and which input parameters it can accept. Well-defined schema also provides one layer of security within the API, as it enables us to validate and sanitize the requests being made to the API. The Schema section explores this large topic further.

Top ↑

Controller Classes

Controller classes unify and coordinate all these various moving parts within a REST API response cycle. With a controller class you can manage the registration of routes & endpoints, handle requests, utilize schema, and generate API responses. A single class usually contains all of the logic for a given route, and a given route usually represents a specific type of data object within your WordPress site (like a custom post type or taxonomy).

This page provides solutions to some common questions and problems that may arise while using the API. If your question is not explained here it may have been answered in the WordPress support forums.

Can I disable the REST API?

You should not disable the REST API; doing so will break WordPress Admin functionality that depends on the API being active. However, you may use a filter to require that API consumers be authenticated, which effectively prevents anonymous external access. See below for more information.

Top ↑

Require Authentication for All Requests

You can require authentication for all REST API requests by adding an is_user_logged_in check to the rest_authentication_errors filter.

Note: The incoming callback parameter can be either null, a WP_Error, or a boolean. The type of the parameter indicates the state of authentication:

- null: no authentication check has yet been performed, and the hook callback may apply custom authentication logic.

- boolean: indicates a previous authentication method check was performed. Boolean true indicates the request was successfully authenticated, and boolean false indicates authentication failed.

- WP_Error: Some kind of error was encountered.

add_filter( 'rest_authentication_errors', function( $result ) {

// If a previous authentication check was applied,

// pass that result along without modification.

if ( true === $result || is_wp_error( $result ) ) {

return $result;

}

// No authentication has been performed yet.

// Return an error if user is not logged in.

if ( ! is_user_logged_in() ) {

return new WP_Error(

'rest_not_logged_in',

__( 'You are not currently logged in.' ),

array( 'status' => 401 )

);

}

// Our custom authentication check should have no effect

// on logged-in requests

return $result;

});

Top ↑

Can I make API requests from PHP within a plugin?

Yes, you can! Use rest_do_request to make API requests internally within other WordPress code:

$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );

// Set one or more request query parameters

$request->set_param( 'per_page', 20 );

$response = rest_do_request( $request );

Top ↑

How do I use the _embed parameter on internal requests?

Setting the _embed param on the request object won’t work.

$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );

$request->set_param( '_embed', 1 );

$response = rest_do_request( $request );

Instead, manually call the WP_REST_Server::response_to_data function.

$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );

$response = rest_do_request( $request );

$data = rest_get_server()->response_to_data( $response, true );

var_dump( $data['_embedded'] );

Top ↑

What happened to the ?filter= query parameter?

When the REST API was merged into WordPress core the ?filter query parameter was removed to prevent future compatibility and maintenance issues. The ability to pass arbitrary WP_Query arguments to the API using a ?filter query parameter was necessary at the genesis of the REST API project, but most API response filtering functionality has been superseded by more robust query parameters like ?categories=, ?slug= and ?per_page=.

First-party query parameters should be used whenever possible. However, the rest-filter plugin restores the ability to pass arbitrary ?filter values in API request if needed.

Top ↑

Query parameters are not working

If you find that query parameters such as ?page=2 or ?_embed are not having any effect, your server may not be properly configured to detect them. If you are using Nginx to serve your website, look for a try_files line in your site configuration. If it looks like this:

try_files $uri $uri/ /index.php$args;

change it to this:

try_files $uri $uri/ /index.php$is_args$args;

Adding $is_args (which will print a ? character if query arguments are found) will allow WordPress to properly receive and interpret the query parameters.

Top ↑

Why is Authentication not working?

If you’re finding that you are sending Authentication headers but the request is not being accepted, and you are using a CGI environment, your webserver may be stripping the headers. Please try adding the appropriate configuration below to remedy this.

Top ↑

Apache

Add the following to a configuration file or .htaccess:

<IfModule mod_setenvif>

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

</IfModule>

Top ↑

Nginx

Add the following to your server configurations fastcgi section:

fastcgi_pass_header Authorization;

Top ↑

Why is the REST API not verifying the incoming Origin header? Does this expose my site to CSRF attacks?

Cross-Origin Resource Sharing (CORS) is a mechanism which allows a website to control which Origins (originating external sites) are allowed to access your site’s data. CORS prevents against a particular type of attack known as Cross-Site Request Forgery, or CSRF. However, WordPress has an existing CSRF protection mechanism which uses nonces. Tightening CORS restrictions would prevent some authentication methods, so the WordPress REST API uses nonces for CSRF protection instead of CORS.

Because the WordPress REST API does not verify the Origin header of incoming requests, public REST API endpoints may therefore be accessed from any site.

This is an intentional design decision, but if you wish to prevent your site from being accessed from unknown origins you may unhook the default rest_send_cors_headers function from the rest_pre_serve_request filter hook, then hook in your own function to that same filter to specify stricter CORS headers.

Using the REST API  Edit

These articles explore the basic structure of the WordPress REST API.

Global Parameters: learn about the global REST API query parameters that apply to every endpoint

Pagination: work with large collections of resources & control how many records you receive from the REST API

Linking & Embedding: understand how to read and modify connections between different objects, and embed related resources such as author and media data in the REST API’s responses

Discovery: determine what REST API resources a site supports, and where they are located

Authentication: authorize your REST API requests so that you can create, update and delete your data

Frequently Asked Questions: see some of the most frequent inquiries about the REST API and learn how to solve common problems

Resources & Utilities

Backbone.js Client: interact with the API from within WP-Admin using Backbone models & collections

Client Libraries: utilities in a variety of programming languages that simplify interacting with the API

Global Parameters  Edit

TOPICS

- _fields

- _embed

- _method (or X-HTTP-Method-Override header)

- _envelope

- _jsonp

The API includes a number of global parameters (also called “meta-parameters”) which control how the API handles the request/response handling. These operate at a layer above the actual resources themselves, and are available on all resources.

_fields

A REST resource like a Post contains a large quantity of data: basic information such as content, title, and author ID, but also registered metadata and fields, media information, and links to other resources. Your application may not need all of this information on every request.

To instruct WordPress to return only a subset of the fields in a response, you may use the _fields query parameter. If for example you are building an archive view and only need the ID, title, permalink, author and excerpt for a collection of posts, you can restrict the response to only those properties with this fields query:

/wp/v2/posts?_fields=author,id,excerpt,title,link

You may alternatively provide that same list using query parameter array syntax instead of a comma-separated list:

/wp/v2/posts?_fields[]=author&_fields[]=id&_fields[]=excerpt&_fields[]=title&_fields[]=link

When _fields is provided then WordPress will skip unneeded fields when generating your response object, avoiding potentially expensive internal computation or queries for data you don’t need. This also means the JSON object returned from the REST API will be smaller, requiring less time to download and less time to parse on your client device.

Carefully design your queries to pull in only the needed properties from each resource to make your application faster to use and more efficient to run.

As of WordPress 5.3 the _fields parameter supports nested properties. This can be useful if you have registered many meta keys, permitting you to request the value for only one of the registered meta properties:

?_fields=meta.one-of-many-keys

Only the meta value with the key one-of-many-keys will be returned, and others will be excluded.

You can also request specific deeply-nested properties within a complex meta object:

?_fields=meta.key_name.nested_prop.deeply_nested_prop,meta.key_name.other_nested_prop

Top ↑

_embed

Most resources include links to related resources. For example, a post can link to the parent post, or to comments on the post. To reduce the number of HTTP requests required, clients may wish to fetch a resource as well as the linked resources. The _embed parameter indicates to the server that the response should include these embedded resources.

Embed mode is enabled if the _embed parameter is passed in the query string (GET parameter). This parameter does not require a value (i.e. ?_embed is valid), however can be passed “1” as a value if required by a client library.

As of WordPress 5.4, the resources to embed can be limited by passing a list of link relation names to the _embed parameter. For example, /wp/v2/posts?_embed=author,wp:term will only embed the post’s author and the lists of terms associated with the post.

Resources in embed mode will contain an additional _embedded key next to the _links key containing the linked resources. Only links with the embeddable parameter set to true will be embedded.

For more about linking and embedding, see the Linking and Embedding page.

Top ↑

_method (or X-HTTP-Method-Override header)

Some servers and clients cannot correctly process some HTTP methods that the API makes use of. For example, all deletion requests on resources use the DELETE method, but some clients do not provide the ability to send this method.

To ensure compatibility with these servers and clients, the API supports a method override. This can be passed either via a _method parameter or the X-HTTP-Method-Override header, with the value set to the HTTP method to use.

Alert:

Clients should only ever send a method override parameter or header with POST requests. Using the method override with GET requests may cause the request to be incorrectly cached.

A POST to /wp-json/wp/v2/posts/42?_method=DELETE would be translated to a DELETE to the wp/v2/posts/42 route.

Similarly, the following POST request would become a DELETE:

POST /wp-json/wp/v2/posts/42 HTTP/1.1

Host: example.com

X-HTTP-Method-Override: DELETE

Top ↑

_envelope

Similarly to _method, some servers, clients, and proxies do not support accessing the full response data. The API supports passing an _envelope parameter, which sends all response data in the body, including headers and status code.

Envelope mode is enabled if the _envelope parameter is passed in the query string (GET parameter). This parameter does not require a value (i.e. ?_envelope is valid), but can be passed “1” as a value if required by a client library.

Note:

For future compatibility, other values should not be passed.

Enveloped responses include a “fake” HTTP 200 response code with no additional headers (apart from Content-Type) that should ensure the response correctly passes through intermediaries.

For example, given the following response to a GET to wp/v2/users/me:

HTTP/1.1 302 Found

Location: http://example.com/wp-json/wp/v2/users/42

{

"id": 42,

...

}

The equivalent enveloped response (with a GET to wp/v2/users/me?_envelope) would be:

HTTP/1.1 200 OK

{

"status": 302,

"headers": {

"Location": "http://example.com/wp-json/wp/v2/users/42"

},

"body": {

"id": 42

}

}

Top ↑

_jsonp

The API natively supports JSONP responses to allow cross-domain requests for legacy browsers and clients. This parameter takes a JavaScript callback function which will be prepended to the data. This URL can then be loaded via a <script> tag.

The callback function can contain any alphanumeric, _ (underscore), or . (period) character. Callbacks which contain invalid characters will receive a HTTP 400 error response, and the callback will not be called.

Note:

Modern browsers can use Cross-Origin Resource Sharing (CORS) preflight requests for cross-domain requests, but JSONP can be used to ensure support with all browsers.

- Browser Support

- MDN Article on CORS

For example:

<script>

function receiveData( data ) {

// Do something with the data here.

// For demonstration purposes, we'll simply log it.

console.log( data );

}

</script>

<script src="https://example.com/wp-json/?_jsonp=receiveData"></script>

Pagination  Edit

TOPICS

- Pagination Parameters

- Ordering Results

WordPress sites can have a lot of content—far more than you’d want to pull down in a single request. The API endpoints default to providing a limited number of items per request, the same way that a WordPress site will default to 10 posts per page in archive views.

Pagination Parameters

Any API response which contains multiple resources supports several common query parameters to handle paging through the response data:

- ?page=: specify the page of results to return.

- For example, /wp/v2/posts?page=2 is the second page of posts results

- By retrieving /wp/v2/posts, then /wp/v2/posts?page=2, and so on, you may access every available post through the API, one page at a time.

- ?per_page=: specify the number of records to return in one request, specified as an integer from 1 to 100.

- For example, /wp/v2/posts?per_page=1 will return only the first post in the collection

- ?offset=: specify an arbitrary offset at which to start retrieving posts

- For example, /wp/v2/posts?offset=6 will use the default number of posts per page, but start at the 6th post in the collection

- ?per_page=5&page=4 is equivalent to ?per_page=5&offset=15

Tip:

Large queries can hurt site performance, so per_page is capped at 100 records. If you wish to retrieve more than 100 records, for example to build a client-side list of all available categories, you may make multiple API requests and combine the results within your application.

To determine how many pages of data are available, the API returns two header fields with every paginated response:

- X-WP-Total: the total number of records in the collection

- X-WP-TotalPages: the total number of pages encompassing all available records

By inspecting these header fields you can determine how much more data is available within the API.

Top ↑

Ordering Results

In addition to the pagination query parameters detailed above, several other parameters control the order of the returned results:

- ?order=: control whether results are returned in ascending or descending order

- Valid values are ?order=asc (for ascending order) and ?order=desc (for descending order).

- All native collections are returned in descending order by default.

- ?orderby=: control the field by which the collection is sorted

- The valid values for orderby will vary depending on the queried resource; for the /wp/v2/posts collection, the valid values are “date,” “relevance,” “id,” “include,” “title,” and “slug”

- See the REST API reference for the values supported by other collections

- All collections with dated resources default to orderby=date

Linking and Embedding  Edit

TOPICS

- Links

- Example Response

- Embedding

- Example Response

The WP REST API incorporates hyperlinking throughout the API to allow discoverability and browsability, as well as embedding related resources together in one response. While the REST API does not completely conform to the entire HAL standard, it implements the ._links and ._embedded properties from that standard as described below.

Links

The _links property of the response object contains a map of links to other API resources, grouped by “relation.” The relation specifies how the linked resource relates to the primary resource. (Examples include “author,” describing a relationship between a resource and its author, or “wp:term,” describing the relationship between a post and its tags or categories.) The relation is either a standardised relation, a URI relation (like https://api.w.org/term`) or a Compact URI relation (likewp:term). (Compact URI relations can be normalised to full URI relations to ensure full compatibility if required.) This is similar to HTMLtags, or` links.

The links are an object containing a href property with an absolute URL to the resource, as well as other optional properties. These include content types, disambiguation information, and data on actions that can be taken with the link.

For collection responses (those that return a list of objects rather than a top-level object), each item contains links, and the top-level response includes links via the Link header instead.

Note:

If your client library does not allow accessing headers, you can use the _envelope parameter to include the headers as body data instead.

Top ↑

Example Response

A typical single post request (/wp/v2/posts/42):

{

"id": 42,

"_links": {

"collection": [

{

"href": "https://example.com/wp-json/wp/v2/posts"

}

],

"author": [

{

"href": "https://example.com/wp-json/wp/v2/users/1",

"embeddable": true

}

]

}

}

Top ↑

Embedding

Optionally, some linked resources may be included in the response to reduce the number of HTTP requests required. These resources are “embedded” into the main response.

Embedding is triggered by setting the _embed query parameter on the request. This will then include embedded resources under the _embedded key adjacent to the _links key. The layout of this object mirrors the _links object, but includes the embedded resource in place of the link properties.

Only links with the embeddable flag set to true can be embedded, and _embed will cause all embeddable links to be embedded. Only relations containing embedded responses are included in _embedded, however relations with mixed embeddable and unembeddable links will contain dummy responses for the unembeddable links to ensure numeric indexes match those in _links.

When embedding a collection response, for instance /wp/v2/posts?author=1, the embeded collection will have the default pagination limits applied.

Top ↑

Example Response

{

"id": 42,

"_links": {

"collection": [

{

"href": "https://example.com/wp-json/wp/v2/posts"

}

],

"author": [

{

"href": "https://example.com/wp-json/wp/v2/users/1",

"embeddable": true

}

]

},

"_embedded": {

Discovery  Edit

TOPICS

- Discovering the API

- Link Header

- Element

- RSD (Really Simple Discovery)

- Authentication Discovery

- Extension Discovery

- Resource Discovery

When your client talks to an unknown site, you’ll need to discover what the site is capable of and how the site is configured. There are a couple of steps for this, depending on what you need to discover.

Discovering the API

The first step of connecting to a site is finding out whether the site has the API enabled. Typically, you’ll be working with URLs from user input, so the site you’re accessing could be anything. The discovery step lets you verify the API is available, as well as indicating how to access it.

Top ↑

Link Header

The preferred way to handle discovery is to send a HEAD request to the supplied address. The REST API automatically adds a Link header to all front-end pages that looks like the following:

Link: <http://example.com/wp-json/>; rel="https://api.w.org/"

This URL points to the root route (/) of the API, which is then used for further discovery steps.

For sites without “pretty permalinks” enabled, /wp-json/ isn’t automatically handled by WordPress. This means that normal/default WordPress permalinks will be used instead. These headers look more like this:

Link: <http://example.com/?rest_route=/>; rel="https://api.w.org/"

Clients should keep this variation in mind and ensure that both routes can be handled seamlessly.

This auto-discovery can be applied to any URL served by a WordPress installation, so no pre-processing on user input needs to be added. Since this is a HEAD request, the request should be safe to send blindly to servers without worrying about causing side-effects.

Top ↑

Element

For clients with a HTML parser, or running in the browser, the equivalent of the Link header is included in the <head> of front-end pages through a <link> element:

<link rel='https://api.w.org/' href='http://example.com/wp-json/' />

In-browser Javascript can access this via the DOM:

// jQuery method

var $link = jQuery( 'link[rel="https://api.w.org/"]' );

var api_root = $link.attr( 'href' );

// Native method

var links = document.getElementsByTagName( 'link' );

var link = Array.prototype.filter.call( links, function ( item ) {

return ( item.rel === 'https://api.w.org/' );

} );

var api_root = link[0].href;

For in-browser clients, or clients without access to HTTP headers, this may be a more usable way of discovering the API. This is similar to Atom/RSS feed discovery, so existing code for that purpose may also be automatically

adapted instead.

Top ↑

RSD (Really Simple Discovery)

For clients with support for XML-RPC discovery, the RSD method may be more applicable. This is an XML-based discovery format typically used for XML-RPC. RSD has two steps. The first step is to find the RSD endpoint, supplied as a <link> element:

<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://example.com/xmlrpc.php?rsd" />

The second step is to fetch the RSD document and parse the available endpoints. This involves using an XML parser on a document like the following:

<?xml version="1.0" encoding="utf-8"?>

<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">

<service>

<engineName>WordPress</engineName>

<engineLink>https://wordpress.org/</engineLink>

<homePageLink>http://example.com/</homePageLink>

<apis>

<api name="WordPress" blogID="1" preferred="true" apiLink="http://example.com/xmlrpc.php" />

<!-- ... -->

<api name="WP-API" blogID="1" preferred="false" apiLink="http://example.com/wp-json/" />

</apis>

</service>

</rsd>

The REST API always has a name attribute with the value equal to WP-API.

RSD is the least-preferred method of autodiscovery for a couple of reasons. The first step of RSD-based discovery involves parsing the HTML to first find the RSD document itself, which is equivalent to the <link> Element autodiscovery. It then involves another step to parse the RSD document, which requires a full XML parser.

Where possible, we suggest avoiding RSD-based discovery due to the complexity, but existing XML-RPC clients may prefer to use this method if they already have an RSD parser enabled. For XML-RPC clients which wish to use the REST API as a progressive enhancement to the codebase, this avoids needing to support different forms of discovery.

Top ↑

Authentication Discovery

Discovery is also available for authentication methods available via the API. The API root’s response is an object describing the API, which includes an authentication key:

{

"name": "Example WordPress Site",

"description": "YOLO",

"routes": { ... },

"authentication": {

"oauth1": {

"request": "http://example.com/oauth/request",

"authorize": "http://example.com/oauth/authorize",

"access": "http://example.com/oauth/access",

"version": "0.1"

}

}

}

The authentication value is a map (associative array) of authentication method ID to authentication options. The options available here are specific to the authentication method itself. See the authentication documentation for the options for specific authentication methods.

Top ↑

Extension Discovery

Once you’ve discovered the API, the next step is check what the API supports. The index of the API exposes the namespaces item, which contains the extensions to the API that are supported.

For WordPress sites using versions 4.4 through 4.6, only the base API infrastructure is available, not the full API with endpoints. This also includes the oEmbed endpoints:

{

"name": "Example WordPress Site",

"namespaces": [

"oembed/1.0/"

]

}

Sites with the full API available (i.e. with WordPress 4.7+ or the REST API plugin installed) will have the wp/v2 item in namespaces as well:

{

"name": "Example WordPress Site",

"namespaces": [

"wp/v2",

"oembed/1.0/"

]

}

Before attempting to use any of the core endpoints, you should be sure to check that the API is supported by checking for wp/v2 support. WordPress 4.4 enabled the API infrastructure for all sites, but did not include the core endpoints under wp/v2. Core endpoints were added in WordPress 4.7.

This same mechanism can be used for detecting support for any plugins that support the REST API. For example, take a plugin which registers the following route:

<?php

register_rest_route( 'testplugin/v1', '/testroute', array( /* ... */ ) );

This would add the testplugin/v1 namespace to the index:

{

"name": "Example WordPress Site",

"namespaces": [

"wp/v2",

"oembed/1.0/",

"testplugin/v1"

]

}

Top ↑

Resource Discovery

As of WordPress 5.5 the REST API also provides a discovery mechanism for identifying the REST API route equivalent of the current document. A link is added with a rel of alternate and a type of alternate/json that points to a REST API url. The link is added both as a Link header and a <link> element.

For instance, in the <head> section of this page, the following <link> appears.

<link rel="alternate" type="application/json" href="https://developer.wordpress.org/wp-json/wp/v2/rest-api-handbook/23085">

Links are added for post, pages, and other custom post types, as well as terms and author pages. Links are not currently output for post archives or search results.

Authentication  Edit

TOPICS

- Cookie Authentication

- Authentication Plugins

Cookie Authentication

Cookie authentication is the standard authentication method included with WordPress. When you log in to your dashboard, this sets up the cookies correctly for you, so plugin and theme developers need only to have a logged-in user.

However, the REST API includes a technique called nonces to avoid CSRF issues. This prevents other sites from forcing you to perform actions without explicitly intending to do so. This requires slightly special handling for the API.

For developers using the built-in Javascript API, this is handled automatically for you. This is the recommended way to use the API for plugins and themes. Custom data models can extend wp.api.models.Base to ensure this is sent correctly for any custom requests.

For developers making manual Ajax requests, the nonce will need to be passed with each request. The API uses nonces with the action set to wp_rest. These can then be passed to the API via the _wpnonce data parameter (either POST data or in the query for GET requests), or via the X-WP-Nonce header. If no nonce is provided the API will set the current user to 0, turning the request into an unauthenticated request, even if you’re logged into WordPress.

Note: Until recently, most software had spotty support for DELETE requests. For instance, PHP doesn’t transform the request body of a DELETE request into a super global. As such, supplying the nonce as a header is the most reliable approach.

It is important to keep in mind that this authentication method relies on WordPress cookies. As a result this method is only applicable when the REST API is used inside of WordPress and the current user is logged in. In addition, the current user must have the appropriate capability to perform the action being performed.

As an example, this is how the built-in Javascript client creates the nonce:

<?php

wp_localize_script( 'wp-api', 'wpApiSettings', array(

'root' => esc_url_raw( rest_url() ),

'nonce' => wp_create_nonce( 'wp_rest' )

) );

This is then used in the base model:

options.beforeSend = function(xhr) {

xhr.setRequestHeader('X-WP-Nonce', wpApiSettings.nonce);

if (beforeSend) {

return beforeSend.apply(this, arguments);

}

};

Here is an example of editing the title of a post, using jQuery AJAX:

$.ajax( {

url: wpApiSettings.root + 'wp/v2/posts/1',

method: 'POST',

beforeSend: function ( xhr ) {

xhr.setRequestHeader( 'X-WP-Nonce', wpApiSettings.nonce );

},

data:{

'title' : 'Hello Moon'

}

} ).done( function ( response ) {

console.log( response );

} );

Note that you do not need to verify that the nonce is valid inside your custom end point. This is automatically done for you in

rest_cookie_check_errors()

Top ↑

Authentication Plugins

While cookie authentication is the only authentication mechanism available natively within WordPress, plugins may be added to support alternative modes of authentication that will work from remote applications. Some example plugins are OAuth 1.0a Server, Application Passwords, and JSON Web Tokens.

Note:

There’s also a Basic Authentication plugin.

Note that this plugin requires sending your username and password with every request, and should only be used for development and testing i.e. not in a production environment.

Modifying Responses  Edit

TOPICS

- Important Note about Changing Responses

- Using register_rest_field vs register_meta

- Adding Custom Fields to API Responses

- Using register_rest_field

- Examples

- Read and write an extra field in comment responses

- How register_rest_field Works

- Working with registered meta in the REST API

- Read and write a post meta field in post responses

- Post Type Specific Meta

- Object Meta Type

- Additional Properties

- Array Meta Type

- Non-Single Metadata

- Invalid Stored Values

- Default Metadata Values

- Adding Links to the API Response

- Registering a CURIE

The default endpoints of the WordPress REST API are designed to return data by default that provides for the majority of sites and use cases, but there are often situations where you will need to access or expose additional data in the responses for various object types.

As with the rest of WordPress, the REST API is designed to be highly extensible to fit these needs. This guide details how to add additional data to the responses of default endpoints using the register_rest_field and register_meta functions. You can use these functions to add fields to any of the object types supported by the REST API. These custom fields can support both get and update operations.

Important Note about Changing Responses

Alert:

Changing or removing data from core REST API endpoint responses can break plugins or WordPress core behavior, and should be avoided wherever possible.

The API exposes many fields on API responses, including things you might not need, or which might not fit into how your site works. While it’s tempting to modify or remove fields from REST API responses, this will cause problems with API clients that expect standard responses. This includes mobile clients, third party tools to help you manage your site, or wp-admin itself.

You may only need a small amount of data, but it’s important to keep in mind that the API is about exposing an interface to all clients, not just the feature you’re working on. Changing responses is dangerous.

Adding fields is not dangerous, so if you need to modify data, it’s much better to duplicate the field instead with your modified data. Removing fields is never encouraged; if you need to get back a smaller subset of data, use the _fields parameter or work with contexts instead.

If you must remove fields from an existing context you should ensure that the behavior is opt-in, for example by providing a custom query parameter to trigger the field removal.

The API cannot prevent you from changing responses, but the code is structured to strongly discourage doing so. Internally, field registration is powered by filters, and these can be used if you absolutely have no other choice.

Top ↑

Using register_rest_field vs register_meta

There are two methods which can be used to add data to WordPress REST API responses, register_rest_field and register_meta.

register_rest_field may be used to add arbitrary fields to any REST API response, and can be used to both read and write data using the API. To register a new REST field you must provide your own callback functions to get or set the field’s value, as well as manually specify your own schema definition for the field.

register_meta is used to whitelist an existing custom meta value for access through the REST API. By setting a meta field’s show_in_rest parameter to true, that field’s value will be exposed on a .meta key in the endpoint response, and WordPress will handle setting up the callbacks for reading and writing to that meta key. This is much simpler than register_rest_field, with one caveat:

Alert:

Prior to WordPress 4.9.8, meta fields set to show_in_rest using register_meta are registered for all objects of a given type. If one custom post type shows a meta field, all custom post types will show that meta field. As of WordPress 4.9.8 it’s possible to use register_meta with the object_subtype argument that allows one to reduce the usage of the meta key to a particular post type.

Prior to WordPress 5.3, register_meta could only support scalar values (string, integer, number and boolean). WordPress 5.3 adds support for the object and array types.

Top ↑

Adding Custom Fields to API Responses

Top ↑

Using register_rest_field

The register_rest_field function is the most flexible way to add fields to REST API response objects. It accepts three parameters:

1. $object_type: The name of the object, as a string, or an array of the names of objects for which the field is being registered. This may be a core type like “post”, “terms”, “meta”, “user” or “comment”, but can also be the string name of a custom post type.

2. $attribute: The name of the field. This name will be used to define the key in the response object.

3. $args: An array with keys that define the callback functions used to retrieve the value of the field (‘get_callback’), to update the value of the field (‘update_callback’), and to define its schema (‘schema’).

Each of the keys of the $args array is optional, but if not used, that capability will not be added. This means that you may specify a callback function for reading the value and omit the update callback to make that field read-only if desired.

Fields should be registered at the rest_api_init action. Using this action rather than init will prevent the field registration from happening during requests to WordPress that do not use the REST API.

Top ↑

Examples

Top ↑

Read and write an extra field in comment responses

<?php

add_action( 'rest_api_init', function () {

register_rest_field( 'comment', 'karma', array(

'get_callback' => function( $comment_arr ) {

$comment_obj = get_comment( $comment_arr['id'] );

return (int) $comment_obj->comment_karma;

},

'update_callback' => function( $karma, $comment_obj ) {

$ret = wp_update_comment( array(

'comment_ID'    => $comment_obj->comment_ID,

'comment_karma' => $karma

) );

if ( false === $ret ) {

return new WP_Error(

'rest_comment_karma_failed',

__( 'Failed to update comment karma.' ),

array( 'status' => 500 )

);

}

return true;

},

'schema' => array(

'description' => __( 'Comment karma.' ),

'type'        => 'integer'

),

) );

} );

This example illustrates adding a field called karma to the response for posts. It works because the comment_karma field exists, but is unused by core. Note that an actual implementation of comment karma would need to use a separate endpoint.

This is a basic example; carefully consider what permissions checks or error handling may be required for your specific field.

Top ↑

How register_rest_field Works

The global variable $wp_rest_additional_fields is used internally by the REST API infrastructure to hold the response fields to be added to each object type. The REST API provides register_rest_field as a utility function for adding to this global variable. Adding to the global variable directly should be avoided to ensure forward-compatibility.

For each object type — posts, or users, terms, comments, etc. — $wp_rest_additional_fields contains an array of field definitions containing the callbacks used to retrieve or update the field’s value.

Top ↑

Working with registered meta in the REST API

The register_meta function simplifies the process of defining a meta field for a particular object type. By setting 'show_in_rest' => true when registering a new meta key, that key will be accessible through the REST API.

Top ↑

Read and write a post meta field in post responses

<?php

// The object type. For custom post types, this is 'post';

// for custom comment types, this is 'comment'. For user meta,

// this is 'user'.

$object_type = 'post';

$meta_args = array( // Validate and sanitize the meta value.

// Note: currently (4.7) one of 'string', 'boolean', 'integer',

// 'number' must be used as 'type'. The default is 'string'.

'type'         => 'string',

// Shown in the schema for the meta key.

'description'  => 'A meta key associated with a string meta value.',

// Return a single value of the type.

'single'       => true,

// Show in the WP REST API response. Default: false.

'show_in_rest' => true,

);

register_meta( $object_type, 'my_meta_key', $meta_args );

This example shows how to allow reading and writing of a post meta field. This will allow that field to be updated via a POST request to wp-json/wp/v2/posts/<post-id> or created along with a post via a POST request to wp-json/wp/v2/posts/.

Note that for meta fields registered on custom post types, the post type must have custom-fields support. Otherwise the meta fields will not appear in the REST API.

Top ↑

Post Type Specific Meta

WordPress 4.9.8 adds support for registering meta for a specific post type or taxonomy by using the register_post_meta and register_term_meta functions. They follow the same rules as register_meta but accept a post type or taxonomy as their first parameter instead of an object type. The following code would register the my_meta_key example above, but only for the page custom post type.

$meta_args = array(

'type'         => 'string',

'description'  => 'A meta key associated with a string meta value.',

'single'       => true,

'show_in_rest' => true,

);

register_post_meta( 'page', 'my_meta_key', $meta_args );

Top ↑

Object Meta Type

WordPress 5.3 adds support for using the object type when registering meta. Importantly object refers to a JSON object, this is equivalent to an associative array in PHP.

When registering object meta, setting the type to object is not sufficient, you also need to inform WordPress of what properties to allow. This is done by writing a JSON Schema when registering the metadata.

For instance, the following code sample registers a post meta field called “release” that accepts the given JSON data.

{

"meta": {

"release": {

"version": "5.2",

"artist": "Jaco"

}

}

}

register_post_meta(

'post',

'release',

array(

'single'       => true,

'type'         => 'object',

'show_in_rest' => array(

'schema' => array(

'type'       => 'object',

'properties' => array(

'version' => array(

'type' => 'string',

),

'artist'  => array(

'type' => 'string',

),

),

),

),

)

);

Notice that show_in_rest becomes an array instead of true and a Json Schema is specified for the schema key. Each property is then listed in the properties array. At a minimum, each property must specify a type, but any JSON Schema keyword that rest_validate_value_from_schema understands can be used as well.

Top ↑

Additional Properties

By default, the list of properties is a strict whitelist. If a property is sent in the request that is not listed, the REST API will return an error: your_property is not a valid property of Object.. If you don’t know the property names ahead of time the additionalProperties keyword can be used. additionalProperties accepts a JSON Schema that is used to validate the unknown properties. For instance, to enforce that any additional properties are numbers, the following code can be used.

{

"meta": {

"release": {

"version": "5.2",

"artist": "Jaco",

"unknown_field": 5.3

}

}

}

register_post_meta(

'post',

'version',

array(

'single'       => true,

'type'         => 'object',

'show_in_rest' => array(

'schema' => array(

'type'                 => 'object',

'properties'           => array(

'version' => array(

'type' => 'string',

),

'artist'  => array(

'type' => 'string',

),

),

'additionalProperties' => array(

'type' => 'number',

),

),

),

)

);

additionalProperties can be set to true to allow unknown properties of any format, but this is not recommended.

Top ↑

Array Meta Type

WordPress 5.3 also adds support for using the array type. Importantly array refers to a JSON array, this is equivalent to a numeric array in PHP.

When registering array meta, setting the type to array is not sufficient, you need to inform WordPress of what the expected format is of the items in the array. This is done by writing a JSON Schema entry when registering the metadata.

If you do not provide this value, register_meta will return false and issue the following warning: When registering an "array" meta type to show in the REST API, you must specify the schema for each array item in "show_in_rest.schema.items".

The following code sample registers a post meta field called “projects” that contains a list of project names. It accepts the given JSON data.

{

"meta": {

"projects": [

"WordPress",

"BuddyPress"

]

}

}

register_post_meta(

'post',

'projects',

array(

'single'       => true,

'type'         => 'array',

'show_in_rest' => array(

'schema' => array(

'type'  => 'array',

'items' => array(

'type' => 'string',

),

),

),

)

);

Notice that again show_in_rest becomes an array instead of true and a JSON Schema is specified for the schema key.

The items keyword is used to define the JSON schema to validate each array member against. It can be a scalar type like string or a complex type like object.

For instance, to accept the given JSON data, the following meta registration would be used.

{

"meta": {

"projects": [

{

"name": "WordPress",

"website": "https://wordpress.org"

},

{

"name": "BuddyPress",

"website": "https://buddypress.org"

}

]

}

}

register_post_meta(

'post',

'projects',

array(

'single'       => true,

'type'         => 'array',

'show_in_rest' => array(

'schema' => array(

'items' => array(

'type'       => 'object',

'properties' => array(

'name'    => array(

'type' => 'string',

),

'website' => array(

'type'   => 'string',

'format' => 'uri',

),

),

),

),

),

)

);

Alert:

The array type enforces that the array keys are sequential integers starting from 0. The array will be reindexed using array_values.

Top ↑

Non-Single Metadata

Non-single meta fields have an array of values per object, instead of one value per object. Each of those values is stored in a separate row in the meta table.

The array and object types can be used with non-single meta fields as well. For example, if the “release” meta key from earlier had single set to false, the following JSON data could be accepted.

{

"meta": {

"release": [

{

"version": "5.2",

"artist": "Jaco"

},

{

"version": "5.1",

"artist": "Betty"

}

]

}

}

This would result in two meta database rows. The first containing { "version": "5.2", "artist": "Jaco" } and the second containing { "version": "5.1", "artist": "Betty" }.

Similarly, the following data would be accepted for the “projects” example if it had set single to false.

{

"meta": {

"projects": [

[

"WordPress",

"BuddyPress"

],

[

"bbPress"

]

]

}

}

This would result in two meta database rows. The first containing [ "WordPress", "BuddyPress" ] and the second containing [ "bbPress" ].

Top ↑

Invalid Stored Values

If the existing value for a meta field does not validate against the registered type and schema, the value for that meta field will be returned as null. If that null value is passed back to the API when doing an update request, you’ll receive a rest_invalid_stored_value error: The %s property has an invalid stored value, and cannot be updated to null.. To fix this, either update the meta key with a valid value or omit that property from your request.

Top ↑

Default Metadata Values

WordPress 5.5 adds official support for specifying a default value for metadata if no value is defined. For example, with this code snippet the price meta field will be set to 0.00 in the REST API response if a value is not yet.

register_post_meta(

'product',

'price',

array(

'single'  => true,

'type'    => 'string',

'default' => '0.00',

)

);

Top ↑

Adding Links to the API Response

WordPress generates a list of links associated with the queried resource to make it easier to navigate to related resources.

{

"_links": {

"self": [

{

"href": "https://make.wordpress.org/core/wp-json/wp/v2/posts/28312"

}

],

"collection": [

{

"href": "https://make.wordpress.org/core/wp-json/wp/v2/posts"

}

],

"author": [

{

"embeddable": true,

"href": "https://make.wordpress.org/core/wp-json/wp/v2/users/8670591"

}

],

"replies": [

{

"embeddable": true,

"href": "https://make.wordpress.org/core/wp-json/wp/v2/comments?post=28312"

}

],

"wp:term": [

{

"taxonomy": "category",

"embeddable": true,

"href": "https://make.wordpress.org/core/wp-json/wp/v2/categories?post=28312"

},

{

"taxonomy": "post_tag",

"embeddable": true,

"href": "https://make.wordpress.org/core/wp-json/wp/v2/tags?post=28312"

}

]

}

}

A sample of links from a Make.WordPress.org post

While these links will appear under the _links property in the JSON response object, it is not stored in WP_REST_Response::$data or accessible via WP_REST_Response::get_data(). Instead, the server will append the link data to the response right before echoing the response data.

Custom links can be added to the response by using the WP_REST_Response::add_link() method. This method accepts three parameters, the link relation, the URL and optionally a list of link attributes. For example, to add the author and wp:term link.

<?php

$response->add_link( 'author', rest_url( "/wp/v2/users/{$post->post_author}" ) );

$response->add_link( 'https://api.w.org/term', add_query_arg( 'post', $post->ID, rest_url( "/wp/v2/{$tax_base}" ) ) );

The link relation MUST be either a registered link relation from the IANA or a URI that is under your control.

author is a registered link relation described as “the context’s author”, we use that to point to the WordPress user who wrote the post. No link relation exists that describes the terms associated with a post, so WordPress uses the https://api.w.org/term URL. This is transformed to wp:term when generating the response by using a CURIE.

The third parameter of add_link() is a list of custom attributes. The embeddable attribute can be used to include the linked resource appears in the _embedded section of the repsonse when using the _embed query parameter. If multiple links are added with the same relation, the embedded responses will be in the same order the links were added in.

<?php

$response->add_link( 'author', rest_url( "/wp/v2/users/{$post->post_author}" ), array(

'embeddable' => true,

) );

$response->add_link( 'author', rest_url( "/wp/v2/users/{$additional_author}" ), array(

'embeddable' => true,

) );

A sample implementation of linking to multi-author posts.

{

"_links": {

"author": [

{

"embeddable": true,

"href": "https://yourwebsite.com/wp-json/wp/v2/users/1"

},

{

"embeddable": true,

"href": "https://yourwebsite.com/wp-json/wp/v2/users/2"

}

]

},

"_embedded": {

"author": [

{

"id": 1,

"name": "Primary Author"

},

{

"id": 2,

"name": "Secondary Author"

}

]

}

}

The order links are added is maintained.

Top ↑

Registering a CURIE

WordPress version 4.5 introduced support for Compact URIs, or CURIEs. This makes it possible to reference links by a much simpler identifier than the full URL which could easily be quite lengthy.

A CURIE is registered using the rest_response_link_curies filter. For example.

<?php

function my_plugin_prefix_register_curie( $curies ) {

$curies[] = array(

'name'      => 'my_plugin',

'href'      => 'https://api.mypluginurl.com/{rel}',

'templated' => true,

);

return $curies;

}

This will convert link URLs from https://api.mypluginurl.com/my_link to my_plugin:my_link in the API response. The full URL must still be used when adding links using WP_REST_Response::add_link.

Adding Custom Endpoints  Edit

TOPICS

- Bare Basics

- Namespacing

- Arguments

- Return Value

- Permissions Callback

- Discovery

- The Controller Pattern

- Examples

The WordPress REST API is more than just a set of default routes. It is also a tool for creating custom routes and endpoints. The WordPress front-end provides a default set of URL mappings, but the tools used to create them (e.g. the Rewrites API, as well as the query classes: WP_Query, WP_User, etc) are also available for creating your own URL mappings, or custom queries.

This document details how to create a totally custom route, with its own endpoints. We’ll first work through a short example, then expand it out to the full controller pattern as used internally.

Bare Basics

So you want to add custom endpoints to the API? Fantastic! Let’s get started with a simple example.

Let’s start with a simple function that looks like this:

<?php

/**

* Grab latest post title by an author!

*

* @param array $data Options for the function.

* @return string|null Post title for the latest,
* or null if none.

*/

function my_awesome_func( $data ) {

$posts = get_posts( array(

'author' => $data['id'],

) );

if ( empty( $posts ) ) {

return null;

}

return $posts[0]->post_title;

}

To make this available via the API, we need to register a route. This tells the API to respond to a given request with our function. We do this through a function called register_rest_route, which should be called in a callback on rest_api_init to avoid doing extra work when the API isn’t loaded.

We need to pass in three things to register_rest_route: the namespace, the route we want, and the options. We’ll come back to the namespace in a bit, but for now, let’s pick myplugin/v1. We’re going to have the route match anything with /author/{id}, where {id} is an integer.

<?php

add_action( 'rest_api_init', function () {

register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(

'methods' => 'GET',

'callback' => 'my_awesome_func',

) );

} );

Right now, we’re only registering the one endpoint for the route. The term “route” refers to the URL, whereas “endpoint” refers to the function behind it that corresponds to a method and a URL (for more information, see the Glossary).

For example, if your site domain is example.com and you’ve kept the API path of wp-json, then the full URL would be http://example.com/wp-json/myplugin/v1/author/(?P\d+).

Each route can have any number of endpoints, and for each endpoint, you can define the HTTP methods allowed, a callback function for responding to the request and a permissions callback for creating custom permissions. In addition you can define allowed fields in the request and for each field specify a default value, a sanitization callback, a validation callback, and whether the field is required.

Top ↑

Namespacing

Namespaces are the first part of the URL for the endpoint. They should be used as a vendor/package prefix to prevent clashes between custom routes. Namespaces allows for two plugins to add a route of the same name, with different functionality.

Namespaces in general should follow the pattern of vendor/v1, where vendor is typically your plugin or theme slug, and v1 represents the first version of the API. If you ever need to break compatibility with new endpoints, you can then bump this to v2.

The above scenario, two routes with the same name, from two different plugins, requires all vendors to use a unique namespace. Failing to do so is analogous to a failure to use a vendor function prefix, class prefix and/ or class namespace in a theme or plugin, which is very very bad.

An added benefit of using namespaces is that clients can detect support for your custom API. The API index lists out the available namespaces on a site:

{

"name": "WordPress Site",

"description": "Just another WordPress site",

"url": "http://example.com/",

"namespaces": [

"wp/v2",

"vendor/v1",

"myplugin/v1",

"myplugin/v2",

]

}

If a client wants to check that your API exists on a site, they can check against this list. (For more information, see the Discovery guide.)

Top ↑

Arguments

By default, routes receive all arguments passed in from the request. These are merged into a single set of parameters, then added to the Request object, which is passed in as the first parameter to your endpoint:

<?php

function my_awesome_func( WP_REST_Request $request ) {

// You can access parameters via direct array access on the object:

$param = $request['some_param'];

// Or via the helper method:

$param = $request->get_param( 'some_param' );

// You can get the combined, merged set of parameters:

$parameters = $request->get_params();

// The individual sets of parameters are also available, if needed:

$parameters = $request->get_url_params();

$parameters = $request->get_query_params();

$parameters = $request->get_body_params();

$parameters = $request->get_json_params();

$parameters = $request->get_default_params();

// Uploads aren't merged in, but can be accessed separately:

$parameters = $request->get_file_params();

}

(To find out exactly how parameters are merged, check the source of WP_REST_Request::get_parameter_order(); the basic order is body, query, URL, then defaults.)

Normally, you’ll get every parameter brought in unaltered. However, you can register your arguments when registering your route, which allows you to run sanitization and validation on these.

If the request has the Content-type: application/json header set and valid JSON in the body, get_json_params() will return the parsed JSON body as an associative array.

Arguments are defined as a map in the key args for each endpoint (next to your callback option). This map uses the name of the argument of the key, with the value being a map of options for that argument. This array can contain a key for default, required, sanitize_callback and validate_callback.

- default: Used as the default value for the argument, if none is supplied.

- required: If defined as true, and no value is passed for that argument, an error will be returned. No effect if a default value is set, as the argument will always have a value.

- validate_callback: Used to pass a function that will be passed the value of the argument. That function should return true if the value is valid, and false if not.

- sanitize_callback: Used to pass a function that is used to sanitize the value of the argument before passing it to the main callback.

Using sanitize_callback and validate_callback allows the main callback to act only to process the request, and prepare data to be returned using the WP_REST_Response class. By using these two callbacks, you will be able to safely assume your inputs are valid and safe when processing.

Taking our previous example, we can ensure the parameter passed in is always a number:

<?php

add_action( 'rest_api_init', function () {

register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(

'methods' => 'GET',

'callback' => 'my_awesome_func',

'args' => array(

'id' => array(

'validate_callback' => function($param, $request, $key) {

return is_numeric( $param );

}

),

),

) );

} );

You could also pass in a function name to validate_callback, but passing certain functions like is_numeric directly will not only throw a warning about having extra parameters passed to it, but will also return NULL causing the callback function to be called with invalid data. We hope to eventually solve this problem in WordPress core.

We could also use something like 'sanitize_callback' => 'absint' instead, but validation will throw an error, allowing clients to understand what they’re doing wrong. Sanitization is useful when you would rather change the data being input rather than throwing an error (such as invalid HTML).

Top ↑

Return Value

After your callback is called, the return value is then converted to JSON, and returned to the client. This allows you to return basically any form of data. In our example above, we’re returning either a string or null, which are automatically handled by the API and converted to JSON.

Like any other WordPress function, you can also return a WP_Error instance. This error information will be passed along to the client, along with a 500 Internal Service Error status code. You can further customise your error by setting the status option in the WP_Error instance data to a code, such as 400 for bad input data.

Taking our example from before, we can now return an error instance:

<?php

/**

* Grab latest post title by an author!

*

* @param array $data Options for the function.

* @return string|null Post title for the latest,

* or null if none.

*/

function my_awesome_func( $data ) {

$posts = get_posts( array(

'author' => $data['id'],

) );

if ( empty( $posts ) ) {

return new WP_Error( 'no_author', 'Invalid author', array( 'status' => 404 ) );

}

return $posts[0]->post_title;

}

When an author doesn’t have any posts belonging to them, this will return a 404 Not Found error to the client:

HTTP/1.1 404 Not Found

[{

"code": "no_author",

"message": "Invalid author",

"data": { "status": 404 }

}]

For more advanced usage, you can return a WP_REST_Response object. This object “wraps” normal body data, but allows you to return a custom status code, or custom headers. You can also add links to your response. The quickest way to use this is via the constructor:

<?php

$data = array( 'some', 'response', 'data' );

// Create the response object

$response = new WP_REST_Response( $data );

// Add a custom status code

$response->set_status( 201 );

// Add a custom header

$response->header( 'Location', 'http://example.com/' );

When wrapping existing callbacks, you should always use rest_ensure_response() on the return value. This will take raw data returned from an endpoint and automatically turn it into a WP_REST_Response for you. (Note that WP_Error is not converted to a WP_REST_Response to allow proper error handling.)

Importantly, a REST API route’s callback should always return data; it shouldn’t attempt to send the response body itself. This ensures that the additional processing that the REST API server does, like handling linking/embedding, sending headers, etc… takes place. In other words, don’t call die( wp_json_encode( $data ) ); or wp_send_json( $data ). As of WordPress 5.5, a _doing_it_wrong notice is issued if the wp_send_json() family of functions is used during a REST API request.

Return a WP_REST_Response or WP_Error object from your callback when using the REST API.

Top ↑

Permissions Callback

You must also register a permissions callback for the endpoint. This is a function that checks if the user can perform the action (reading, updating, etc) before the real callback is called. This allows the API to tell the client what actions they can perform on a given URL without needing to attempt the request first.

This callback can be registered as permission_callback, again in the endpoint options next to your callback option. This callback should return a boolean or a WP_Error instance. If this function returns true, the response will be processed. If it returns false, a default error message will be returned and the request will not proceed with processing. If it returns a WP_Error, that error will be returned to the client.

The permissions callback is run after remote authentication, which sets the current user. This means you can use current_user_can to check if the user that has been authenticated has the appropriate capability for the action, or any other check based on current user ID. Where possible, you should always use current_user_can; instead of checking if the user is logged in (authentication), check whether they can perform the action (authorization).

Once you register a permission_callback, you will need to authenticate your requests (for example by including a nonce parameter) or you will receive a rest_forbidden error. See Authentication for more details.

Continuing with our previous example, we can make it so that only editors or above can view this author data. We can check a number of different capabilities here, but the best is edit_others_posts, which is really the core of what an editor is. To do this, we just need a callback here:

<?php

add_action( 'rest_api_init', function () {

register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(

'methods' => 'GET',

'callback' => 'my_awesome_func',

'args' => array(

'id' => array(

'validate_callback' => 'is_numeric'

),

),

'permission_callback' => function () {

return current_user_can( 'edit_others_posts' );

}

) );

} );

Note that the permission callback also receives the Request object as the first parameter, so you can do checks based on request arguments if you need to.

As of WordPress 5.5, if a permission_callback is not provided, the REST API will issue a _doing_it_wrong notice.

The REST API route definition for myplugin/v1/author is missing the required permission_callback argument. For REST API routes that are intended to be public, use __return_true as the permission callback.

If your REST API endpoint is public, you can use __return_true as the permission callback.

<?php

add_action( 'rest_api_init', function () {

register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(

'methods' => 'GET',

'callback' => 'my_awesome_func',

'permission_callback' => '__return_true',

) );

} );

Top ↑

Discovery

If you’d like to enable Resource Discovery for your custom endpoint, you can do so using the rest_queried_resource_route filter. For example, consider a custom query var my-route that contains the id of your custom resource. The following code snippet would add a discovery link whenever the my-route query var is used.

function my_plugin_rest_queried_resource_route( $route ) {

$id = get_query_var( 'my-route' );

if ( ! $route && $id ) {

$route = '/my-ns/v1/items/' . $id;

}

return $route;

}

add_filter( 'rest_queried_resource_route', 'my_plugin_rest_queried_resource_route' );

Note: If your endpoint is describing a custom post type or custom taxonomy you will most likely want to be using the rest_route_for_post or rest_route_for_term filters instead.

Top ↑

The Controller Pattern

The controller pattern is a best practice for working with complex endpoints with the API.

It is recommended that you read “Extending Internal Classes” before reading this section. Doing so will familiarize you with the patterns used by the default routes, which is the best practice. While it is not required that the class you use to process your request extends the WP_REST_Controller class or a class that extends it, doing so allows you to inherit work done in those classes. In addition, you can rest assured that you’re following best practices based on the controller methods you’re using.

At their core, controllers are nothing more than a set of commonly named methods to match up with REST conventions, along with some handy helpers. Controllers register their routes in a register_routes method, respond to requests with get_items, get_item, create_item, update_item and delete_item, and have similarly named permission check methods. Following this pattern will ensure you don’t miss any steps or functionality in your endpoints.

To use controllers, you first need to subclass the base controller. This gives you a base set of methods, ready for you to add your own behaviour into.

Once we’ve subclassed the controller, we need to instantiate the class to get it working. This should be done inside of a callback hooked into rest_api_init, which ensures we only instantiate the class when we need to. The normal controller pattern is to call $controller->register_routes() inside this callback, where the class can then register its endpoints.

Top ↑

Examples

The following is a “starter” custom route:

<?php

class Slug_Custom_Route extends WP_REST_Controller {

/**

* Register the routes for the objects of the controller.

*/

public function register_routes() {

$version = '1';

$namespace = 'vendor/v' . $version;

$base = 'route';

register_rest_route( $namespace, '/' . $base, array(

array(

'methods'             => WP_REST_Server::READABLE,

'callback'            => array( $this, 'get_items' ),

'permission_callback' => array( $this, 'get_items_permissions_check' ),

'args'                => array(

),

),

array(

'methods'             => WP_REST_Server::CREATABLE,

'callback'            => array( $this, 'create_item' ),

'permission_callback' => array( $this, 'create_item_permissions_check' ),

'args'                => $this->get_endpoint_args_for_item_schema( true ),

),

) );

register_rest_route( $namespace, '/' . $base . '/(?P<id>[\d]+)', array(

array(

'methods'             => WP_REST_Server::READABLE,

'callback'            => array( $this, 'get_item' ),

'permission_callback' => array( $this, 'get_item_permissions_check' ),

'args'                => array(

'context' => array(

'default' => 'view',

),

),

),

array(

'methods'             => WP_REST_Server::EDITABLE,

'callback'            => array( $this, 'update_item' ),

'permission_callback' => array( $this, 'update_item_permissions_check' ),

'args'                => $this->get_endpoint_args_for_item_schema( false ),

),

array(

'methods'             => WP_REST_Server::DELETABLE,

'callback'            => array( $this, 'delete_item' ),

'permission_callback' => array( $this, 'delete_item_permissions_check' ),

'args'                => array(

'force' => array(

'default' => false,

),

),

),

) );

register_rest_route( $namespace, '/' . $base . '/schema', array(

'methods'  => WP_REST_Server::READABLE,

'callback' => array( $this, 'get_public_item_schema' ),

) );

}

/**

* Get a collection of items

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|WP_REST_Response

*/

public function get_items( $request ) {

$items = array(); //do a query, call another class, etc

$data = array();

foreach( $items as $item ) {

$itemdata = $this->prepare_item_for_response( $item, $request );

$data[] = $this->prepare_response_for_collection( $itemdata );

}

return new WP_REST_Response( $data, 200 );

}

/**

* Get one item from the collection

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|WP_REST_Response

*/

public function get_item( $request ) {

//get parameters from request

$params = $request->get_params();

$item = array();//do a query, call another class, etc

$data = $this->prepare_item_for_response( $item, $request );

//return a response or error based on some conditional

if ( 1 == 1 ) {

return new WP_REST_Response( $data, 200 );

} else {

return new WP_Error( 'code', __( 'message', 'text-domain' ) );

}

}

/**

* Create one item from the collection

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|WP_REST_Response

*/

public function create_item( $request ) {

$item = $this->prepare_item_for_database( $request );

if ( function_exists( 'slug_some_function_to_create_item' ) ) {

$data = slug_some_function_to_create_item( $item );

if ( is_array( $data ) ) {

return new WP_REST_Response( $data, 200 );

}

}

return new WP_Error( 'cant-create', __( 'message', 'text-domain' ), array( 'status' => 500 ) );

}

/**

* Update one item from the collection

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|WP_REST_Response

*/

public function update_item( $request ) {

$item = $this->prepare_item_for_database( $request );

if ( function_exists( 'slug_some_function_to_update_item' ) ) {

$data = slug_some_function_to_update_item( $item );

if ( is_array( $data ) ) {

return new WP_REST_Response( $data, 200 );

}

}

return new WP_Error( 'cant-update', __( 'message', 'text-domain' ), array( 'status' => 500 ) );

}

/**

* Delete one item from the collection

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|WP_REST_Response

*/

public function delete_item( $request ) {

$item = $this->prepare_item_for_database( $request );

if ( function_exists( 'slug_some_function_to_delete_item' ) ) {

$deleted = slug_some_function_to_delete_item( $item );

if ( $deleted ) {

return new WP_REST_Response( true, 200 );

}

}

return new WP_Error( 'cant-delete', __( 'message', 'text-domain' ), array( 'status' => 500 ) );

}

/**

* Check if a given request has access to get items

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|bool

*/

public function get_items_permissions_check( $request ) {

//return true; <--use to make readable by all

return current_user_can( 'edit_something' );

}

/**

* Check if a given request has access to get a specific item

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|bool

*/

public function get_item_permissions_check( $request ) {

return $this->get_items_permissions_check( $request );

}

/**

* Check if a given request has access to create items

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|bool

*/

public function create_item_permissions_check( $request ) {

return current_user_can( 'edit_something' );

}

/**

* Check if a given request has access to update a specific item

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|bool

*/

public function update_item_permissions_check( $request ) {

return $this->create_item_permissions_check( $request );

}

/**

* Check if a given request has access to delete a specific item

*

* @param WP_REST_Request $request Full data about the request.

* @return WP_Error|bool

*/

public function delete_item_permissions_check( $request ) {

return $this->create_item_permissions_check( $request );

}

/**

* Prepare the item for create or update operation

*

* @param WP_REST_Request $request Request object

* @return WP_Error|object $prepared_item

*/

protected function prepare_item_for_database( $request ) {

return array();

}

/**

* Prepare the item for the REST response

*

* @param mixed $item WordPress representation of the item.

* @param WP_REST_Request $request Request object.

* @return mixed

*/

public function prepare_item_for_response( $item, $request ) {

return array();

}

/**

* Get the query params for collections

*

* @return array

*/

public function get_collection_params() {

return array(

'page'     => array(

'description'       => 'Current page of the collection.',

'type'              => 'integer',

'default'           => 1,

'sanitize_callback' => 'absint',

),

'per_page' => array(

'description'       => 'Maximum number of items to be returned in result set.',

'type'              => 'integer',

'default'           => 10,

'sanitize_callback' => 'absint',

),

'search'   => array(

'description'       => 'Limit results to those matching a string.',

'type'              => 'string',

'sanitize_callback' => 'sanitize_text_field',

),

);

}

}

Browse: Home / REST API Handbook / Reference

Reference  Edit

TOPICS

- REST API Developer Endpoint Reference

- A Distributed API

The WordPress REST API is organized around REST, and is designed to have predictable, resource-oriented URLs and to use HTTP response codes to indicate API errors. The API uses built-in HTTP features, like HTTP authentication and HTTP verbs, which can be understood by off-the-shelf HTTP clients, and supports cross-origin resource sharing to allow you to interact securely with the API from a client-side web application.

The REST API uses JSON exclusively as the request and response format, including error responses. While the REST API does not completely conform to the HAL standard, it does implement HAL’s ._links and ._embedded properties for linking API resources, and is fully discoverable via hyperlinks in the responses.

The REST API provides public data accessible to any client anonymously, as well as private data only available after authentication. Once authenticated the REST API supports most content management actions, allowing you to build alternative dashboards for a site, enhance your plugins with more responsive management tools, or build complex single-page applications.

This API reference provides information on the specific endpoints available through the API, their parameters, and their response data format.

REST API Developer Endpoint Reference

Resource

Base Route

Posts

/wp/v2/posts

Post Revisions

/wp/v2/posts/<id>/revisions

Categories

/wp/v2/categories

Tags

/wp/v2/tags

Pages

/wp/v2/pages

Page Revisions

/wp/v2/pages/<id>/revisions

Comments

/wp/v2/comments

Taxonomies

/wp/v2/taxonomies

Media

/wp/v2/media

Users

/wp/v2/users

Post Types

/wp/v2/types

Post Statuses

/wp/v2/statuses

Settings

/wp/v2/settings

Themes

/wp/v2/themes

Search

/wp/v2/search

Block Types

/wp/v2/block-types

Blocks

/wp/v2/blocks

Block Revisions

/wp/v2/blocks/<id>/autosaves/

Block Renderer

/wp/v2/block-renderer

Block Directory Items

/wp/v2/block-directory/search

Plugins

/wp/v2/plugins

Top ↑

A Distributed API

Unlike many other REST APIs, the WordPress REST API is distributed and available individually on each site that supports it. This means there is no singular API root or base to contact; instead, we have a discovery process that allows interacting with sites without prior contact. The API also exposes self-documentation at the index endpoint, or via an OPTIONS request to any endpoint, allowing human- or machine-discovery of endpoint capabilities.

A demo installation of the API for testing purposes is available at https://demo.wp-api.org/wp-json/; this site supports auto-discovery, and provides read-only demo data.

Table of Contents

REST API Handbook Edit