Subtle diagonal pinstripe pattern in light grey and cream, giving an impression of printed editorial paper

Schedule WordPress cron jobs with wp_schedule_event

WordPress can run recurring background work without a separate application or continuous server process. Functions such as wp_schedule_event() register an action at a future time, then execute it whenever WordPress processes scheduled events. Common examples include generating reports, refreshing caches, sending webhooks, checking stock and maintaining WooCommerce order data.

WordPress cron is request-driven, so its execution time depends partly on website traffic. A busy store may trigger an event almost immediately, while a low-traffic site could leave it waiting until the next visit. For important work, a server scheduler can invoke WordPress at regular intervals and provide more dependable timing.

Australian timing needs careful handling. Sydney and Melbourne move between Australian Eastern Standard Time and Australian Eastern Daylight Time, Brisbane remains on Eastern Standard Time, and Perth uses Australian Western Standard Time. Because WordPress stores event timestamps as Unix values, the application should calculate those timestamps consistently rather than relying on a display timezone.

A plugin is the safest home for recurring tasks because it can contain the callback, activation logic and cleanup routine together. The implementation should also prevent duplicate events, support useful intervals, test callbacks before activation and follow Australian privacy and email-marketing obligations when customer information is involved.

Understand WordPress cron and choose a trigger

WordPress provides several ways to run scheduled work. The right choice depends on how precise the timing must be and how much control the hosting environment provides.

Execution method Trigger Timing reliability Best suited to
WordPress cron through wp_schedule_event() A WordPress request Depends on site traffic Routine, low-risk maintenance
Server scheduler calling wp-cron.php Operating system cron Runs at regular intervals Important recurring jobs
WP-CLI command Administrator or server cron Controlled execution Testing and managed hosting

The main function follows a simple structure: wp_schedule_event( $timestamp, $recurrence, $hook, $args ). The timestamp is the first future execution time, the recurrence defines the interval, and the hook identifies the action that WordPress will call. An optional arguments array passes values to the callback.

wp_schedule_event(
    time() + 300,
    'daily',
    'acme_daily_report'
);

WordPress includes the hourly, twicedaily, daily and weekly recurrences. The function returns false if the event cannot be scheduled, while passing true as its fifth argument enables a WP_Error object with more useful diagnostics. Event creation also depends on WordPress being able to save its cron option in the database.

Build a recurring job as a plugin

Place the action callback and scheduling functions in a small plugin rather than an active theme. A theme can disappear or change during a redesign, which may leave a scheduled hook pointing to a callback that no longer exists.

<?php
/**
 * Plugin Name: ACME Scheduled Report
 */

add_action( 'acme_daily_report', 'acme_generate_daily_report' );

function acme_generate_daily_report() {
    $report = get_option( 'acme_latest_report', array() );
    $report['generated_at'] = current_time( 'mysql' );

    update_option( 'acme_latest_report', $report, false );
}

function acme_schedule_daily_report() {
    if ( wp_next_scheduled( 'acme_daily_report' ) ) {
        return;
    }

    wp_schedule_event(
        time() + ( 5 * MINUTE_IN_SECONDS ),
        'daily',
        'acme_daily_report'
    );
}

function acme_ensure_daily_report() {
    if ( ! wp_next_scheduled( 'acme_daily_report' ) ) {
        acme_schedule_daily_report();
    }
}

function acme_unschedule_daily_report() {
    wp_clear_scheduled_hook( 'acme_daily_report' );
}

register_activation_hook( __FILE__, 'acme_schedule_daily_report' );
register_deactivation_hook( __FILE__, 'acme_unschedule_daily_report' );

add_action( 'init', 'acme_ensure_daily_report' );

The activation hook creates the event, while the deactivation hook removes it. The init check covers situations where the database was migrated, a plugin was enabled through a deployment process or an existing event was accidentally deleted. Both paths call wp_next_scheduled() first, preventing the same hook from being registered repeatedly.

Use a unique hook prefix such as acme_ because WordPress, WooCommerce and other plugins already maintain their own scheduled actions. A callback should also be safe to run more than once. Updating an option is naturally repeatable, but an operation that charges a payment, sends stock to a warehouse or imports orders should record a transaction identifier and skip work that has already completed.

Add custom intervals and event arguments

WordPress can register an additional recurrence through the cron_schedules filter. Add this filter while the plugin loads, before any code attempts to schedule an event with the new interval.

add_filter( 'cron_schedules', 'acme_cron_schedules' );

function acme_cron_schedules( $schedules ) {
    $schedules['every_thirty_minutes'] = array(
        'interval' => 30 * MINUTE_IN_SECONDS,
        'display' => __( 'Every 30 minutes', 'acme' ),
    );

    return $schedules;
}

A short interval should only be used when the task is worthwhile. Repeated database queries, API calls and WooCommerce imports can become expensive on a busy Australian store, while a low-traffic site may not generate enough requests to process them regularly. Thirty minutes is often suitable for a lightweight stock check; payment reconciliation and large imports usually need longer intervals.

Arguments allow one action to handle several stores, feeds or accounts. The same argument values must be supplied when checking whether the event exists.

$store_id = 'store-sydney';

wp_schedule_event(
    time() + 300,
    'daily',
    'acme_reconcile_orders',
    array( $store_id )
);

function acme_reconcile_orders( $store_id ) {
    // Reconcile orders for the supplied store.
}

The matching lookup is wp_next_scheduled( 'acme_reconcile_orders', array( $store_id ) ). WordPress treats different arguments as different events, so passing store-melbourne requires a separate lookup and event. For a delayed task that should run only once, use wp_schedule_single_event() with the same timestamp, hook and argument structure.

Use a server scheduler for dependable timing

Automatic WordPress cron runs when a request reaches the site and triggers the cron process. That behaviour is acceptable for many maintenance tasks, but it does not guarantee a job will run at a particular minute. Hosting plans with almost no traffic can leave scheduled work idle for hours, and some Australian managed WordPress hosts disable or restrict WordPress cron by default.

On a server with shell access, WP-CLI offers a direct way to process events that are due:

*/5 * * * * /usr/bin/wp --path=/var/www/example cron event run --due-now --quiet >/dev/null 2>&1

If WP-CLI is unavailable, the server scheduler can request WordPress’s cron endpoint instead:

*/5 * * * * curl -fsS 'https://example.com.au/wp-cron.php?doing_wp_cron' >/dev/null 2>&1

To prevent WordPress from also attempting automatic background requests, add the following near the top of wp-config.php:

define( 'DISABLE_WP_CRON', true );

The constant disables automatic spawning but leaves wp-cron.php available for the external scheduler. Shared hosting customers may be able to configure this through cPanel rather than a shell command, while managed plans often provide their own scheduling screen. The hosting provider’s Australia-based support team can confirm whether cron expressions, WP-CLI or outbound requests are permitted.

Server cron expressions follow the server’s configured timezone, whereas WordPress event timestamps use Unix time. A daily event based on a fixed 86,400-second interval can therefore appear to shift by an hour when Sydney or Melbourne enters daylight saving. Time-sensitive tasks should use wp_timezone() when deciding whether a local business condition is met, or calculate each desired execution timestamp explicitly.

Test, monitor, and comply in Australia

Test both the scheduling state and the callback before relying on a production event. WP-CLI can list registered events, execute a due event and check the site’s cron configuration.

wp cron event list
wp cron event run acme_daily_report --due-now
wp cron test

A manual run confirms that the callback completes without a PHP error, but it does not prove that the recurrence will arrive on time. Monitor event timestamps, callback logs and expected side effects. Failed API calls should have a recorded status and a controlled retry policy, and sensitive values such as API credentials or customer records should not appear in plaintext logs.

Cron callbacks run without a logged-in administrator, so they cannot assume that normal capability checks will pass. Treat each callback as a trusted server-side action, validate all stored data and protect external endpoints with signatures or secrets. Avoid copying WordPress salts, database passwords or payment credentials into hook arguments, where they may be stored in the cron option.

Many Australian organisations handling customer information are subject to the Privacy Act 1988 and the Australian Privacy Principles. A WooCommerce task that processes orders, addresses or contact details should collect only what it needs, restrict access and remove obsolete data on a defined schedule. If it sends commercial email, the Spam Act 2003 also makes consent, sender identification and an accessible unsubscribe mechanism important.

Local operations add another layer. Stock, delivery and reconciliation schedules may differ around Easter, Christmas and state-specific public holidays. Keep business calendars configurable, record logs in UTC with a readable Australian local timestamp, and use the intended Sydney, Melbourne, Brisbane or Perth trading timezone when evaluating opening hours.