WordPress Cron API

Understanding WP-Cron

WP-Cron is WordPress’s task scheduling system used to run automated tasks such as:

  • publishing scheduled posts

  • checking for plugin/theme updates

  • sending scheduled emails

  • cleaning temporary data

  • running background jobs

Unlike a traditional Linux cron daemon, WP-Cron:

  • runs only when a page loads

  • does not run continuously

  • executes missed tasks immediately on the next page load

This system is often called pseudo-cron or false cron.

Key characteristics:

  • triggered by site traffic

  • scheduled events stored in the wp_options table

  • events executed using action hooks

Core file responsible for execution:

wp-cron.php

How WP-Cron Works Internally

Process flow:

  1. WordPress loads a page.

  2. WordPress checks scheduled tasks in wp_options.

  3. If an event time has passed:

    • WordPress calls wp-cron.php.
  4. WordPress triggers the hook associated with the event.

  5. Developers attach functions to that hook.

Example flow:

schedule event → event stored → time reached → page load → wp-cron triggered → action hook fired → function executed

Core Cron API Functions

Important functions used in plugin development.

Schedule a single event

wp_schedule_single_event()

Runs once at a specific timestamp.

Schedule recurring event

wp_schedule_event()

Runs repeatedly based on interval.

Check if event already exists

wp_next_scheduled()

Prevents duplicate events.

Remove scheduled event

wp_unschedule_event()

Deletes a specific scheduled event.

Clear scheduled hook

wp_clear_scheduled_hook()

Removes all events for a hook.


Scheduling Recurring Events in a Plugin

Typical pattern for plugins.

Step 1: Register cron event on plugin activation

register_activation_hook(__FILE__, 'myplugin_activate');

function myplugin_activate() {

    if (!wp_next_scheduled('myplugin_cron_event')) {

        wp_schedule_event(
            time(),
            'hourly',
            'myplugin_cron_event'
        );

    }

}

Explanation:

  • time() → start time

  • hourly → recurrence interval

  • myplugin_cron_event → hook name


Step 2: Attach function to the cron hook

add_action('myplugin_cron_event', 'myplugin_run_cron');

function myplugin_run_cron() {

    // Your scheduled task here

    error_log('My Plugin Cron Ran');

}

This function executes when WP-Cron runs the event.


Step 3: Clear cron event on plugin deactivation

Important to avoid orphan cron jobs.

register_deactivation_hook(__FILE__, 'myplugin_deactivate');

function myplugin_deactivate() {

    wp_clear_scheduled_hook('myplugin_cron_event');

}

Scheduling a One-Time Task

Useful for delayed background jobs.

Example: send email after 10 minutes.

wp_schedule_single_event(
    time() + 600,
    'myplugin_send_email'
);

Attach handler:

add_action('myplugin_send_email', 'myplugin_email_function');

function myplugin_email_function() {

    wp_mail(
        'user@example.com',
        'Notification',
        'Your scheduled email.'
    );

}

Checking if a Cron Event Exists

Avoid scheduling duplicates.

if (!wp_next_scheduled('myplugin_cron_event')) {

    wp_schedule_event(time(), 'daily', 'myplugin_cron_event');

}

Without this check, WordPress may schedule multiple identical events.


Removing Specific Cron Event

If you know timestamp.

$timestamp = wp_next_scheduled('myplugin_cron_event');

if ($timestamp) {

    wp_unschedule_event($timestamp, 'myplugin_cron_event');

}

WordPress Default Cron Intervals

Default intervals available:

IntervalTime
hourly3600 seconds
twicedaily12 hours
daily24 hours

Creating Custom Cron Intervals

Developers often need custom schedules.

Example: every 5 minutes.

add_filter('cron_schedules', 'myplugin_custom_cron_interval');

function myplugin_custom_cron_interval($schedules) {

    $schedules['five_minutes'] = array(
        'interval' => 300,
        'display'  => 'Every Five Minutes'
    );

    return $schedules;

}

Use it when scheduling:

wp_schedule_event(time(), 'five_minutes', 'myplugin_cron_event');

Viewing Scheduled Events

You can inspect cron events using:

Plugins:

  • WP Crontrol

  • Query Monitor

Or via WP-CLI:

wp cron event list

Common Cron API Use Cases

Automatic cleanup

delete_expired_transients();

Scheduled email sending

wp_mail()

Database maintenance

$wpdb->query("DELETE FROM table WHERE status='expired'");

Scheduled API synchronization

wp_remote_get()

Scheduled backups

Trigger backup scripts periodically.


Debugging WP-Cron

Check if WP-Cron is working.

define('DISABLE_WP_CRON', false);

Log cron execution:

error_log('Cron executed at: ' . current_time('mysql'));

Check scheduled tasks:

wp cron event list

Limitations of WP-Cron

Because WP-Cron depends on traffic:

Problems occur when:

  • low-traffic websites

  • high-precision scheduling required

  • background tasks need reliability

Possible issues:

  • delayed execution

  • tasks pile up

  • inconsistent timing


Using Real Linux Cron Instead of WP-Cron

Better solution for production sites.

Step 1: Disable WordPress pseudo cron

Edit wp-config.php.

define('DISABLE_WP_CRON', true);

Step 2: Add system cron job

Using crontab.

crontab -e

Add:

*/5 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

Meaning:

*/5 * * * *
run every 5 minutes

Alternative using PHP:

*/5 * * * * php /path/to/wordpress/wp-cron.php

Advantages:

  • runs on schedule

  • independent of site traffic

  • better for production servers


Best Practices for Plugin Cron Jobs

Always follow these principles:

Prevent duplicate scheduling

wp_next_scheduled()

Remove cron jobs on plugin deactivation

register_deactivation_hook()

Keep cron tasks lightweight

Heavy operations should be:

  • chunked

  • queued

  • processed gradually

Use transient or option locks

Prevent overlapping executions.

Example:

if (get_transient('myplugin_cron_lock')) {
    return;
}

set_transient('myplugin_cron_lock', true, 300);

Example – Real Plugin Cron Task

Complete working example.

add_action('init', 'myplugin_schedule_event');

function myplugin_schedule_event() {

    if (!wp_next_scheduled('myplugin_daily_cleanup')) {

        wp_schedule_event(
            time(),
            'daily',
            'myplugin_daily_cleanup'
        );

    }

}

add_action('myplugin_daily_cleanup', 'myplugin_cleanup_function');

function myplugin_cleanup_function() {

    global $wpdb;

    $wpdb->query(
        "DELETE FROM {$wpdb->prefix}my_table 
         WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)"
    );

}