# Find and Delete Unused Tags and Categories (WP-CLI)

> Adds a "wp empty-terms" command that lists the terms of a taxonomy that no post uses, and deletes them when you add --delete. The default category is always kept.

- Type: PHP
- Runs: Run everywhere
- Category: [Developer](https://wpide.com/library/category/developer/)
- Tags: wp-cli, taxonomies, cleanup
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/cli-empty-terms/

## Code

```php
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
    return;
}

WP_CLI::add_command( 'empty-terms', function ( $args, $assoc_args ) {
    $taxonomy = sanitize_key( $assoc_args['taxonomy'] ?? 'post_tag' );
    if ( ! taxonomy_exists( $taxonomy ) ) {
        WP_CLI::error( sprintf( 'Taxonomy "%s" does not exist.', $taxonomy ) );
    }

    // Recount first so the "count" column is trustworthy.
    $all = get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => false, 'fields' => 'tt_ids' ) );
    wp_update_term_count_now( $all, $taxonomy );

    $keep  = 'category' === $taxonomy ? (int) get_option( 'default_category' ) : 0;
    $terms = array_filter( get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => false ) ), function ( $term ) use ( $keep ) {
        return 0 === (int) $term->count && (int) $term->term_id !== $keep;
    } );

    if ( ! $terms ) {
        WP_CLI::success( 'No unused terms.' );
        return;
    }

    if ( empty( $assoc_args['delete'] ) ) {
        WP_CLI\Utils\format_items( 'table', array_map( function ( $term ) {
            return array( 'term_id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug );
        }, $terms ), array( 'term_id', 'name', 'slug' ) );
        WP_CLI::log( 'Add --delete to remove them.' );
        return;
    }

    foreach ( $terms as $term ) {
        wp_delete_term( $term->term_id, $taxonomy );
    }
    WP_CLI::success( sprintf( 'Deleted %d unused terms.', count( $terms ) ) );
}, array(
    'shortdesc' => 'Lists or deletes terms with no posts.',
    'synopsis'  => array(
        array( 'type' => 'assoc', 'name' => 'taxonomy', 'optional' => true, 'default' => 'post_tag' ),
        array( 'type' => 'flag', 'name' => 'delete', 'optional' => true ),
    ),
) );
```

## Notes

Example "wp empty-terms --taxonomy=post_tag --delete".

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Find and Delete Unused Tags and Categories (WP-CLI)" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
