WordPress AJAX API Overview
WordPress provides a structured AJAX system through admin-ajax.php, offering a standardized way for plugins and themes to handle asynchronous requests.
Architecture
Request Flow
Client JavaScript
│
▼
POST /wp-admin/admin-ajax.php?action=my_action
│
▼
┌─────────────────────────────────────────────┐
│ admin-ajax.php │
│ ┌─────────────────────────────────────────┐ │
│ │ 1. Define DOING_AJAX = true │ │
│ │ 2. Load WordPress (wp-load.php) │ │
│ │ 3. Load admin APIs │ │
│ │ 4. Load ajax-actions.php (core) │ │
│ │ 5. Fire admin_init action │ │
│ │ 6. Check user authentication │ │
│ │ 7. Fire wp_ajax_{action} or │ │
│ │ wp_ajax_nopriv_{action} │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
│
▼
Handler function (your callback)
│
▼
Response (JSON/XML/HTML)
Key Constants
// Defined automatically when processing AJAX
define('DOING_AJAX', true);
define('WP_ADMIN', true);
Endpoint URL
The AJAX endpoint is always admin-ajax.php:
// PHP - get the URL
admin_url('admin-ajax.php'); // https://example.com/wp-admin/admin-ajax.php
// JavaScript (admin pages) - already available
ajaxurl // Global variable set by WordPress
Setting Up AJAX
1. Register JavaScript
add_action('wp_enqueue_scripts', 'my_enqueue_scripts');
function my_enqueue_scripts() {
wp_enqueue_script(
'my-ajax-script',
plugin_dir_url(__FILE__) . 'js/ajax.js',
array('jquery'),
'1.0.0',
true
);
// Pass data to JavaScript
wp_localize_script('my-ajax-script', 'MyAjax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('my_ajax_nonce'),
));
}
2. Register AJAX Handlers
// For logged-in users
add_action('wp_ajax_my_action', 'handle_my_action');
// For logged-out users (public)
add_action('wp_ajax_nopriv_my_action', 'handle_my_action');
function handle_my_action() {
// Verify nonce
check_ajax_referer('my_ajax_nonce', 'nonce');
// Process request
$result = do_something($_POST['data']);
// Return response
wp_send_json_success($result);
}
3. Client-Side JavaScript
// Using jQuery
jQuery.ajax({
url: MyAjax.ajax_url,
type: 'POST',
data: {
action: 'my_action',
nonce: MyAjax.nonce,
data: 'my_data'
},
success: function(response) {
if (response.success) {
console.log(response.data);
}
}
});
// Using Fetch API
fetch(MyAjax.ajax_url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'my_action',
nonce: MyAjax.nonce,
data: 'my_data'
})
})
.then(response => response.json())
.then(data => console.log(data));
Authentication
WordPress automatically checks authentication and routes requests:
| User State | Hook Fired |
|---|---|
| Logged in | wp_ajax_{action} |
| Logged out | wp_ajax_nopriv_{action} |
If no handler is registered, WordPress returns 0 with HTTP 400.
Response Formats
JSON (Recommended)
// Success
wp_send_json_success(array('message' => 'Done'));
// Output: {"success":true,"data":{"message":"Done"}}
// Error
wp_send_json_error(array('message' => 'Failed'));
// Output: {"success":false,"data":{"message":"Failed"}}
XML (Legacy)
$response = new WP_Ajax_Response(array(
'what' => 'item',
'id' => 123,
'data' => 'Item content',
));
$response->send();
Plain Text
echo 'plain text response';
wp_die();
Security
Nonce Verification
Always verify nonces to prevent CSRF attacks:
// Create nonce in PHP
wp_create_nonce('my_action_nonce');
// Verify in AJAX handler
check_ajax_referer('my_action_nonce', 'security'); // Dies on failure
// OR
if (!wp_verify_nonce($_POST['security'], 'my_action_nonce')) {
wp_send_json_error('Invalid nonce');
}
Capability Checks
Always verify user capabilities:
function handle_admin_action() {
check_ajax_referer('admin_action_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions', 403);
}
// Proceed with admin action
}
Data Sanitization
$title = sanitize_text_field($_POST['title']);
$content = wp_kses_post($_POST['content']);
$id = absint($_POST['id']);
$email = sanitize_email($_POST['email']);
Core AJAX Actions
WordPress defines many core AJAX actions in wp-admin/includes/ajax-actions.php:
GET Actions
fetch-list– Fetch list table dataajax-tag-search– Search tagswp-compression-test– Test compression supportimgedit-preview– Image editor previewoembed-cache– Cache oEmbed dataautocomplete-user– User autocompletedashboard-widgets– Dashboard widget datalogged-in– Check login statusrest-nonce– Get REST API nonce
POST Actions
heartbeat– Heartbeat APIsave-widget– Save widgetdelete-comment,delete-post,delete-tag– Delete contentinline-save,inline-save-tax– Quick editupload-attachment,save-attachment– Media handlinginstall-plugin,update-plugin,delete-plugin– Plugin managementinstall-theme,update-theme,delete-theme– Theme management
Heartbeat API
WordPress includes a Heartbeat API that uses AJAX for periodic communication:
// Add data to heartbeat request
add_filter('heartbeat_send', function($response, $screen_id) {
$response['my_data'] = get_my_data();
return $response;
}, 10, 2);
// Process heartbeat data
add_filter('heartbeat_received', function($response, $data, $screen_id) {
if (isset($data['my_request'])) {
$response['my_response'] = process_request($data['my_request']);
}
return $response;
}, 10, 3);
Detection Functions
// Check if current request is AJAX
if (wp_doing_ajax()) {
// This is an AJAX request
}
// The constant (set by admin-ajax.php)
if (defined('DOING_AJAX') && DOING_AJAX) {
// This is an AJAX request
}
Error Handling
function my_ajax_handler() {
try {
check_ajax_referer('my_nonce', 'security');
$result = risky_operation();
wp_send_json_success($result);
} catch (Exception $e) {
wp_send_json_error(array(
'message' => $e->getMessage(),
'code' => $e->getCode(),
), 500);
}
}
Best Practices
- Always use nonces – Prevent CSRF attacks
- Check capabilities – Don’t rely on authentication alone
- Sanitize all input – Never trust user data
- Use
wp_send_json_*– Consistent JSON responses - Handle errors gracefully – Return meaningful error messages
- Use
wp_die()– Ensures proper termination - Consider REST API – For new projects, REST API may be preferable
AJAX vs REST API
| Feature | AJAX (admin-ajax.php) | REST API |
|---|---|---|
| URL | Single endpoint | Resource-based URLs |
| Auth | Cookie-based | Multiple methods |
| Format | Any | JSON |
| Discoverability | None | Self-documenting |
| Caching | Manual | HTTP caching |
For new development, consider using the REST API instead. The AJAX system is maintained for backward compatibility and simple use cases.