A plugin created lots of posts that I now need to delete. They are targetable by category (e.g. Tweet). Many also have attached images.

I wrote a php script for it*, but without pagination it gets hung up, as it does a lot of work (deletes the posts one by one). - And with pagination (say 1000 per page) it still takes multiple seconds and I'd have to refresh >50 times...

I figure a good database query could select and delete multiple posts at the same time - however I also want to delete all attached media, both in the database and the actual files.

Alternatively, handling the deletions in batches (i.e. using pagination) and maybe flush output in between to show update on whether it's working could work.

*here's what I have so far:

$twitter = get_cat_ID( 'Tweet' );
$instagram = get_cat_ID( 'Instagram' );
$args = array(
    'category__in' => array(
    'post_status' => 'any',
    'posts_per_page' => 1000
$social_posts = new WP_Query( $args );

if ( $social_posts->have_posts() ) : ?>

    <h1>Deleting the following:</h1>
    <?php while ( $social_posts->have_posts() ) :
            <h5><?php the_title() ?></h5>
            <div class='post-content'><?php the_content(); ?></div>
        $post_id = get_the_ID();
        $attachments = get_attached_media( '', $post_id ); // '' => all attachment Mime types
        foreach ( $attachments as $attachment ) {
            wp_delete_attachment( $attachment->ID, true );
        wp_delete_post( $post_id, true );
else: ?>
    <p>Oops, there are no posts.</p>

(Testing this locally, put it in page-uninstall-social.php and created a page for it so it runs when I visit that page)


If you know how to find the posts, then we can use WP CLI to do a 2 step process.

First, grab all the post IDs in those categories using wp post list


posts=$(wp post list --field="ID" --category__in="1,2,etc")
wp post delete $posts --force

Then we can plug the values in posts into wp post delete.

We can also use the same trick to delete the media, via the post_parent__in parameter to fetch their IDs, then deleting them.

If you can't run WP CLI directly on the server, pull a copy down to you computer, run the commands, then replace production with the result


The below code is untested | use are your own risk.

The non-CLI approach could be to just query the IDs, loop the ids, delete media associated with the ids, redirect back unto itself to go through the next iteration.

This is browser based and you would need to navigate to a specific url of your website. It's looking specifically for a delposts query var.


add_action( 'template_redirect', function() {

    if( ! isset( $_GET['delposts'] ) ) {

    $page       = ( isset( $_GET['page'] ) ) ? intval( $_GET['page'] ) : 1;
    $twitter    = get_cat_ID( 'Tweet' );
    $instagram  = get_cat_ID( 'Instagram' );

    $social_posts = get_posts( array(
        'category__in'      => array(
        'post_status'       => 'any',
        'posts_per_page'    => 200,
        'fields'            => 'ids',
    ) );

    if( ! empty( $social_posts ) ) {

        foreach( $social_posts as $post_id ) {

            $media = get_posts( array(
                'post_parent'   => $post_id,
                'post_type'     => 'attachment'
                'posts_per_page'=> 500,
                'fields'        => 'ids',
            ) );

            if( ! empty( $media ) ) {
                foreach( $media as $media_id ) {
                    wp_delete_attachment( $media_id );

            wp_delete_post( $post_id, true );


        wp_safe_redirect( add_query_arg( array(
            'delposts'  => true,
            'page'      => $page++,
        ), home_url() ) );


} );
  • So, similar in approach to mine (i.e. browser based) - but with the addition of redirecting to self after handling 200 of them. Neat. – Julix Nov 14 at 21:26
  • 1
    Yup, but by only fetching IDs the queries are faster and less likely to time on when getting them in bulk. The redirect should make it run in the background until it's completed ( hopefully the page will prevent browser timeouts ). – Howdy_McGee Nov 14 at 21:26
  • 1
    Cheers, oh and - hey it's you! - Thanks again for your input last time! :) – Julix Nov 14 at 21:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.