3 variants of WordPress Ajax endpoint implementation

There are at least three ways to implement Ajax endpoints in WordPress plugins or themes. Admin-Ajax is something that has been around forever and WP_REST is the “new” way. Rewrite rules are another approach. I will explain all three variants with code examples and try to work out advantages and disadvantages.


The admin-ajax.php file is located in the wp-admin directory since WordPress version 2.1.0. Its name suggests admin only usage, but it can be used for unauthenticated and authenticated requests.

A request to this file will launch the WordPress core, theme (and child theme) functions.php and plugins, but will not end up in a global WP_Query execution or template resolution. It can be used for any type of request method (GET, POST, PUT, PATCH, DELETE) as long as $_REQUEST['action'] value is provided. This action value is used to trigger an action. If no action handler is registered, a 0 string is returned.

Basic unauthenticated Admin-Ajax example

With these few lines of code, we can call our brand new Ajax endpoint with the URL /wp-admin/admin-ajax.php?action=my_action. It’s important to note that the wp_ajax_noproiv_{action} action will only work if your request is not authenticated. If you have a running login session in the browser, the response will be 0. Therefore, in most cases, you should also add the authenticated action as well.

Basic authenticated Admin-Ajax example

If you want to provide data that is restricted to authenticated users, you need to register a wp_ajax_{action} action handler. Unauthenticated requests receive a 0 string response.

The Admin-Ajax method is very simple to implement but it is not suitable for building large apis.


WP_REST 1.0 came with WordPress version 4.4. When Gutenberg was released with WordPress 5.0, things became much more asynchronous on the backend. There was a need for a more structured way of implementing Ajax calls, which lead to WP_REST 2.0.

Basic unauthenticated WP_REST example

Use the rest_api_init action to register your own api endpoints. You can register new routes with the register_rest_route function.

We can now call our Ajax route with the URL /wp-json/code-samples/v1/basic .

The first parameter is a namespace, which usually consists of your plugin’s slug or a sluggish string describing the content, and a version string. The second parameter is the route within this namespace.

The third parameter is a configuration array. The methods argument allows you to select the request methods that will be accepted for this route. Normally, only allow one method per route is provided, but you can also specify multiple methods as a coma-separated list or array of methods. Constants for valid method strings can be found in the WP_REST_Server class. If you do not explicitly specify a method, GET is used.
You must also define a permission_callback. Since our route accepts any GET request, we can use a core function called __return_true that grants permission to proceed for all requests by always returning true.
Last but not least the callback argument. It expects some callable value that returns the response data. The response format is JSON.

Basic authenticated WP_REST example

It is pretty much identical to the unauthenticated code sample, except for the permission_callback argument.

This endpoint accepts only authorized requests because we check the login status with the is_user_logged_in() function. However, if you have a valid login session in your browser and call this url, you will receive an unauthorized error response. This is because the WP_REST api uses nonce tokens for authorization checking. You can use the @wordpress/api-fetch package, which automatically does the nonce token handling if you are logged in.

Ajax endpoints are still fairly easy to implement, but you are forced to specify namespaces, request methods and permission handling, which will help you on larger projects.

Rewrite rules

Adding rewrite rules is the most complex of these three variants. We need to use two actions and one filter to make it working.

First, we need to add a query variable so that we can identify our Ajax requests. Second, we add a custom rewrite rule to the system and map it to our query variable. In the last step, we intercept the request parser und check if our variable is present. If it is, we can assume that our route has been called and provide the response.


Use Admin-Ajax only in very simple cases with one or two very simple endpoints.

In my opinion, WP_REST is the way to go for most cases. It forces you to think about namespacing your routes and handling permissions, and has a lot more nice features for validating and cleaning up request variables. You can design proper RESTful entities, but also use it for simple Ajax routes.

Only if you have strict URL path or response format requirements that cannot be met with Admin-Ajax or WP_REST, then fall back to the Rewrite-Rule variant.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store