# Automatically Expire Listings After Their Closing Date

> Adds an "Expired" status and an hourly check that moves job listings (or any post type) out of public view once their closing date has passed.

- Type: PHP
- Runs: Run everywhere
- Category: [Custom Post Types & Fields](https://wpide.com/library/category/post-types/)
- Tags: custom-status, cron, jobs
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/auto-expire-by-date/

## Code

```php
// Post type and the meta key holding the closing date (Y-m-d or Y-m-d\TH:i).
$wpide_expire = array(
    'post_type' => 'job',
    'meta_key'  => '_job_valid_through',
);

add_action( 'init', function () use ( $wpide_expire ) {
    register_post_status( 'expired', array(
        'label'                     => 'Expired',
        'public'                    => false,
        'protected'                 => true,
        'exclude_from_search'       => true,
        'show_in_admin_all_list'    => true,
        'show_in_admin_status_list' => true,
        /* translators: %s: number of items */
        'label_count'               => _n_noop( 'Expired <span class="count">(%s)</span>', 'Expired <span class="count">(%s)</span>' ),
    ) );

    if ( ! wp_next_scheduled( 'wpide_expire_listings' ) ) {
        wp_schedule_event( time() + 300, 'hourly', 'wpide_expire_listings' );
    }
} );

add_action( 'wpide_expire_listings', function () use ( $wpide_expire ) {
    $ids = get_posts( array(
        'post_type'      => $wpide_expire['post_type'],
        'post_status'    => 'publish',
        'posts_per_page' => 100,
        'fields'         => 'ids',
        'no_found_rows'  => true,
        'meta_query'     => array(
            array( 'key' => $wpide_expire['meta_key'], 'value' => '', 'compare' => '!=' ),
            array( 'key' => $wpide_expire['meta_key'], 'value' => wp_date( 'Y-m-d' ), 'compare' => '<' ),
        ),
    ) );

    foreach ( $ids as $id ) {
        wp_update_post( array( 'ID' => $id, 'post_status' => 'expired' ) );
    }
} );

add_filter( 'display_post_states', function ( $states, $post ) {
    if ( 'expired' === $post->post_status ) {
        $states['expired'] = 'Expired';
    }

    return $states;
}, 10, 2 );
```

## Notes

Defaults to jobs and the _job_valid_through date from the Job Details snippet; change the post type and meta key for events, offers or adverts. Expired items stay in the admin under "Expired".

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Automatically Expire Listings After Their Closing Date" 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/
