Expose Post Metadata Safely With a Custom WordPress REST Endpoint
Register a namespaced, read-only WordPress REST route to expose selected post metadata to a decoupled frontend.
25 Jul 2026, 21:04 UTC

The problem: custom post meta is missing from the default REST response
You are building a frontend that reads WordPress content through the REST API. The standard /wp-json/wp/v2/posts endpoint is useful, but it does not return every custom field by default. WordPress hides protected meta unless the key was registered with show_in_rest. If your application needs one specific piece of post metadata, you face a choice: over-fetch everything and filter client-side, or expose a dedicated read-only route.
The practical takeaway is to register a namespaced REST endpoint that returns only the fields your frontend needs, with a permission callback that runs before any data is read.
Register a read-only route for post metadata
Add this code to a custom plugin or a child theme's functions.php. The namespace myplugin/v1 keeps the route separate from core and other plugins, and the v1 segment signals that you may version it later.
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/post-meta/(?P\d+)', [
'methods' => 'GET',
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
'callback' => function ( $request ) {
$post_id = (int) $request->get_param( 'id' );
if ( ! $post_id ) {
return new WP_Error( 'missing_id', 'A post ID is required.', [ 'status' => 400 ] );
}
$post = get_post( $post_id );
if ( ! $post || 'publish' !== $post->post_status ) {
return new WP_Error( 'not_found', 'Published post not found.', [ 'status' => 404 ] );
}
$meta_key = 'my_post_meta_key';
$meta = get_post_meta( $post_id, $meta_key, true );
return new WP_REST_Response( [
'post_id' => $post_id,
'meta' => $meta,
], 200 );
},
] );
} );
register_rest_route is the core WordPress function for adding custom REST API routes. The permission_callback runs first; if it returns false or a WP_Error, WordPress responds with 403 or 401 before the main callback executes. Returning a WP_REST_Response lets WordPress set the correct HTTP status and JSON headers automatically.
Check permissions and shape the response
The example above restricts access to users who can edit posts. For a truly public endpoint, replace that check with __return_true or is_user_logged_in, but only if the metadata is safe to expose. Always verify post status inside the callback so unpublished or private posts are not leaked.
Return only the keys the frontend requires. Dumping all post meta increases response size and may reveal values intended for the admin screen.
Verify the endpoint
- Install the code in
wp-content/plugins/my-plugin/my-plugin.phpor your child theme, then activate the plugin if you used one. You need filesystem write access and, for a plugin, administrator rights to activate it. - Request the route with a real post ID, replacing
example.comand123:
curl -i https://example.com/wp-json/myplugin/v1/post-meta/123
Expected results:
- Logged out or lacking
edit_posts:HTTP/1.1 403 Forbidden. - Authorized user with a valid published post:
HTTP/1.1 200 OKand a JSON body such as{"post_id":123,"meta":"your value"}. - Invalid or unpublished post ID:
404 Not Foundwith aWP_Errorbody.
You can also fetch /wp-json and inspect the routes key for the myplugin/v1 namespace to confirm registration.
Limitation: no built-in caching
WordPress does not automatically cache custom REST responses. A high-traffic frontend hitting this endpoint on every page load can strain the database. Consider a transient, object cache, or frontend-side caching for frequently requested posts. The endpoint is also unavailable if the REST API is disabled by a plugin or server configuration.
Rollback
Because adding this code changes site state, rollback means removing the rest_api_init block from your plugin or functions.php and saving the file. If you packaged it as a plugin, deactivate or delete the plugin.
After removal, confirm that /wp-json/myplugin/v1/post-meta/123 returns a 404 or a route-not-found response, and that the rest of the site still loads.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.