WordPress REST API Best Practices - Complete Guide
Mahesh Waghmare The WordPress REST API provides a powerful way to interact with WordPress data. This guide covers best practices for building secure, performant, and maintainable REST API integrations.
Introduction to WordPress REST API
WordPress REST API enables headless WordPress, mobile apps, and external integrations. Understanding best practices ensures secure, efficient API usage.
API Capabilities:
- Read and write posts, pages, users
- Manage media files
- Handle comments and taxonomies
- Custom data endpoints
- Authentication and authorization
Common Use Cases:
- Headless WordPress frontends
- Mobile applications
- Third-party integrations
- Content management tools
- Data synchronization
Authentication Methods
Application Passwords
WordPress 5.6+ supports application passwords:
// Generate application password in user profile
// Use in Authorization header
$response = wp_remote_get('https://site.com/wp-json/wp/v2/posts', [
'headers' => [
'Authorization' => 'Basic ' . base64_encode('username:app-password'),
],
]);
OAuth 2.0
For third-party applications:
// Use OAuth plugin or implement OAuth flow
// More secure for public applications
Cookie Authentication
For logged-in users:
// Automatically handled when user is logged in
// Use nonce for CSRF protection
wp_localize_script('my-script', 'wpApiSettings', [
'root' => esc_url_raw(rest_url()),
'nonce' => wp_create_nonce('wp_rest'),
]);
Creating Custom Endpoints
Basic Endpoint Registration
add_action('rest_api_init', function() {
register_rest_route('my-plugin/v1', '/custom-data', [
'methods' => 'GET',
'callback' => 'get_custom_data',
'permission_callback' => '__return_true',
]);
});
function get_custom_data($request) {
return new WP_REST_Response([
'data' => 'value',
'timestamp' => current_time('mysql'),
], 200);
}
Endpoint with Parameters
register_rest_route('my-plugin/v1', '/posts/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => 'get_custom_post',
'args' => [
'id' => [
'required' => true,
'type' => 'integer',
'validate_callback' => function($param) {
return is_numeric($param);
},
],
],
'permission_callback' => '__return_true',
]);
function get_custom_post($request) {
$post_id = $request->get_param('id');
$post = get_post($post_id);
if (!$post) {
return new WP_Error('not_found', 'Post not found', ['status' => 404]);
}
return new WP_REST_Response([
'id' => $post->ID,
'title' => $post->post_title,
'content' => $post->post_content,
], 200);
}
POST Endpoint
register_rest_route('my-plugin/v1', '/data', [
'methods' => 'POST',
'callback' => 'create_custom_data',
'permission_callback' => function() {
return current_user_can('edit_posts');
},
'args' => [
'title' => [
'required' => true,
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
],
'content' => [
'required' => false,
'type' => 'string',
'sanitize_callback' => 'wp_kses_post',
],
],
]);
function create_custom_data($request) {
$title = $request->get_param('title');
$content = $request->get_param('content');
$post_id = wp_insert_post([
'post_title' => $title,
'post_content' => $content,
'post_status' => 'publish',
]);
if (is_wp_error($post_id)) {
return $post_id;
}
return new WP_REST_Response([
'id' => $post_id,
'message' => 'Post created successfully',
], 201);
}
Performance Optimization
Caching Responses
function get_cached_data($request) {
$cache_key = 'my_api_data';
$cached = get_transient($cache_key);
if ($cached !== false) {
return new WP_REST_Response($cached, 200);
}
$data = expensive_operation();
set_transient($cache_key, $data, HOUR_IN_SECONDS);
return new WP_REST_Response($data, 200);
}
Limit Response Size
function get_posts_limited($request) {
$per_page = min($request->get_param('per_page') ?: 10, 100);
$posts = get_posts([
'posts_per_page' => $per_page,
'fields' => 'ids', // Only return IDs for performance
]);
return new WP_REST_Response($posts, 200);
}
Security Best Practices
Input Validation and Sanitization
register_rest_route('my-plugin/v1', '/data', [
'methods' => 'POST',
'callback' => 'secure_endpoint',
'args' => [
'email' => [
'required' => true,
'type' => 'string',
'validate_callback' => function($param) {
return is_email($param);
},
'sanitize_callback' => 'sanitize_email',
],
'url' => [
'required' => false,
'type' => 'string',
'validate_callback' => function($param) {
return filter_var($param, FILTER_VALIDATE_URL);
},
'sanitize_callback' => 'esc_url_raw',
],
],
]);
Rate Limiting
function check_rate_limit($request) {
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'api_rate_limit_' . $ip;
$requests = get_transient($key) ?: 0;
if ($requests >= 100) {
return new WP_Error('rate_limit', 'Too many requests', ['status' => 429]);
}
set_transient($key, $requests + 1, MINUTE_IN_SECONDS);
return true;
}
Error Handling
Proper Error Responses
function handle_api_error($request) {
$id = $request->get_param('id');
if (!$id) {
return new WP_Error(
'missing_parameter',
'ID parameter is required',
['status' => 400]
);
}
$post = get_post($id);
if (!$post) {
return new WP_Error(
'not_found',
'Post not found',
['status' => 404]
);
}
if (!current_user_can('read_post', $id)) {
return new WP_Error(
'forbidden',
'You do not have permission to access this post',
['status' => 403]
);
}
return new WP_REST_Response($post, 200);
}
Conclusion
WordPress REST API best practices include:
- Proper authentication
- Input validation and sanitization
- Performance optimization
- Security measures
- Error handling
- Rate limiting
Key principles:
- Always validate and sanitize input
- Use appropriate permissions
- Implement caching where possible
- Handle errors gracefully
- Document your endpoints
Following these practices creates secure, performant WordPress REST APIs.
Written by Mahesh Waghmare
I bridge the gap between WordPress architecture and modern React frontends. Currently building tools for the AI era.
Follow on Twitter →