
Action Hook since 4.7.0
rest_insert_post
Fires after a post is created or updated through the REST API
Description
rest_insert_post is the REST-specific counterpart to save_post — it fires inside the Posts controller with the actual REST request object available, which save_post alone doesn't give you.
When it runs
Inside WP_REST_Posts_Controller, after the post is saved via the REST API — save_post also fires for the same request, but without direct access to the REST request object.
Signature
do_action( 'rest_insert_post', WP_Post $post, WP_REST_Request $request, bool $creating );Parameters
postWP_Post — The post object that was inserted or updated.requestWP_REST_Request — The REST request that triggered the insert.creatingbool — True when a new post was created, false when an existing post was updated.
Examples
Basic
add_action( 'rest_insert_post', function( $post, $request, $creating ) {
error_log( ( $creating ? 'Created' : 'Updated' ) . ' post via REST: ' . $post->ID );
}, 10, 3 );Log whether a REST call created or updated a post.
Real case
add_action( 'rest_insert_post', function( $post, $request, $creating ) {
$client_id = $request->get_header( 'X-Client-App' );
if ( $client_id ) {
update_post_meta( $post->ID, '_created_via', sanitize_text_field( $client_id ) );
}
}, 10, 3 );Tag posts with which headless client created them, using a custom request header.
Edge case
add_action( 'rest_insert_post', function( $post, $request, $creating ) {
// save_post fires too for this same request —
// guard against running duplicate logic in both places.
}, 10, 3 );save_post and rest_insert_post both fire for the same REST-created post — avoid double-running side effects.
Common Use Cases
- Read the REST request (headers, client info) at save time
- Tag content created by a headless front-end or external app
- Run REST-specific validation after the write
- Trigger a webhook only for API-originated changes
Common mistakes
- Duplicating logic that already runs on save_post — both fire for the same REST write, so pick one or guard against running twice
- Assuming $creating always reflects the intent of the API client — it reflects whether the post row was inserted or updated, not the client's stated intent
Related hooks
FAQ
Do I need this if I already hook save_post?
Only if you need the REST request object itself — headers, route, or client-specific data that save_post doesn't expose.
Does rest_insert_post replace save_post for REST-created posts?
No — both fire. rest_insert_post adds the request context on top of what save_post already gives you.
Source: wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php