Cron Event Generator
Scheduling is the easy half. This writes the guard that stops duplicates, the lock that stops overlap, and the cleanup that runs on deactivation.
<?php // Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Plugin Name: acme sync products * Description: Scheduled task: acme_sync_products. * Version: 1.0.0 * Requires PHP: 7.4 * Text Domain: acme */ defined( 'ABSPATH' ) || exit; /** * Schedule the event, once. */ function acme_schedule() { if ( wp_next_scheduled( 'acme_sync_products' ) ) { return; } wp_schedule_event( strtotime( 'tomorrow 03:00' ), 'daily', 'acme_sync_products' ); } /** * The work itself. */ function acme_run() { if ( get_transient( 'acme_running' ) ) { return; } set_transient( 'acme_running', time(), 5 * MINUTE_IN_SECONDS ); $response = wp_remote_get( 'https://api.example.com/products', array( 'timeout' => 20, ) ); if ( is_wp_error( $response ) ) { error_log( '[cron] fetch failed: ' . $response->get_error_message() ); return; } $items = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $items ) ) { return; } foreach ( $items as $item ) { // Upsert each item here. } update_option( 'acme_last_sync', time(), false ); delete_transient( 'acme_running' ); } add_action( 'acme_sync_products', 'acme_run' ); /** * Clear the event. Runs on deactivation — an orphaned event survives the plugin. */ function acme_unschedule() { wp_clear_scheduled_hook( 'acme_sync_products' ); } register_activation_hook( __FILE__, 'acme_schedule' ); register_deactivation_hook( __FILE__, 'acme_unschedule' );
About this generator
Cron Event Generator Online
A complete wp_schedule_event example built around your own hook name, not a snippet you have to finish. Scheduling is the easy half: this also writes the wp_next_scheduled() guard that stops duplicate events, the transient lock that stops two overlapping requests running the same job twice, and the deactivation routine that clears the schedule when the plugin is switched off.
The Reference tab carries the part most tutorials skip — the built-in schedule lengths, the DISABLE_WP_CRON plus crontab recipe for sites where timing matters, the WP-CLI commands for listing and firing an event by hand, and why a timestamp built with strtotime() follows the server clock rather than the site timezone. Free, and generated entirely in your browser.
Reviewed Output tested on WordPress 6.8 · PHP 8.3
Why the WP-Cron generator beats a bare wp_schedule_event() call
The failure modes are all invisible until they are expensive. An unguarded schedule call adds another copy of the event on every page load. A deactivated plugin leaves its event behind forever. And the job that "runs daily" quietly runs whenever the next visitor arrives, because WP-Cron is not cron.
The duplicate guard, on by default
Every schedule call is wrapped in wp_next_scheduled() first. Turn that off and the generator raises an error, because the classic symptom — a daily job that fires forty times a day — comes from exactly this.
Custom intervals via cron_schedules
Choose an interval in minutes and the generator adds a cron_schedules filter with the seconds calculated and a translated display name. Anything under five minutes is flagged: WP-Cron cannot honour it, since it only runs when a page is loaded and never twice in one request.
An overlap lock, not just a schedule
A transient lock is written around the callback body with a five-minute expiry, so two simultaneous requests cannot both run a due event. On a busy site that is a real race, and anything non-idempotent runs twice without it.
Cleanup that matches the schedule call
Deactivation clears the hook with wp_clear_scheduled_hook(). Add arguments to the event and the cleanup switches to wp_next_scheduled() plus wp_unschedule_event() with the identical argument array — the only way to find an event that was scheduled with arguments.
Output that knows where it will live
The plugin output wires scheduling to register_activation_hook() and cleanup to register_deactivation_hook(). The snippet and functions.php outputs schedule on init instead, because a theme has no activation hook, and say so in a comment.
Four job bodies to start from
A wp_remote_get() fetch with a timeout and both failure branches handled, a bounded get_posts() cleanup batch, a wp_mail() digest, or your own code. The guards and the lock are generated around whichever you choose.
How does the Cron Event Generator work?
Four steps to a scheduled task that can be deactivated cleanly and debugged from WP-CLI.
- 01
Name the event
Give the hook a prefixed name — this is the action WP-Cron fires, and anything else on the site can hook it too. Pick a recurrence (hourly, twicedaily, daily, weekly, a custom interval, or a one-off) and when the first run should happen.
- 02
Describe the work
Choose a starting job body or write your own, and add any arguments the event should be scheduled with. Argument values are passed through to the callback and become part of the event's identity.
- 03
Set the safety net
Keep or drop the
wp_next_scheduled()guard, the transient overlap lock and the deactivation cleanup. Each one is explained in the Checks tab when it is missing. - 04
Export where it belongs
Switch to the plugin output for anything that matters — that is the only shape with real activation and deactivation hooks — then copy or download the file.
Worked example — a daily sync scheduled on activation
A daily event whose first run is queued for tomorrow at 03:00, guarded against double scheduling and cleared when the plugin is deactivated.
/**
* Schedule the event, once.
*/
function acme_schedule() {
if ( wp_next_scheduled( 'acme_sync_products' ) ) {
return;
}
wp_schedule_event(
strtotime( 'tomorrow 03:00' ),
'daily',
'acme_sync_products'
);
}
/**
* Clear the event. Runs on deactivation — an orphaned event survives the plugin.
*/
function acme_unschedule() {
wp_clear_scheduled_hook( 'acme_sync_products' );
}
register_activation_hook( __FILE__, 'acme_schedule' );
register_deactivation_hook( __FILE__, 'acme_unschedule' );The callback itself sits between these two functions in the full output, with the transient lock around your job body and add_action( 'acme_sync_products', 'acme_run' ) underneath it.
WP-Cron & scheduled events — frequently asked questions
The questions that come up whenever a scheduled task does not run when it was supposed to.
Why does my WP-Cron event run late, or not at all?
WP-Cron is not a real cron daemon. It only checks for due events when someone loads a page, so on a low-traffic site a job scheduled for 09:00 runs whenever the next visitor arrives, which might be hours later. On a site behind a password, a staging copy or a new site with no traffic, it may not run at all until you visit it yourself.
How do I make WordPress scheduled tasks run on time?
Set define( 'DISABLE_WP_CRON', true ); in wp-config.php to stop the page-load trigger, then add a real system cron entry that hits the site every few minutes — either requesting wp-cron.php?doing_wp_cron or running wp cron event run --due-now through WP-CLI. Disabling the trigger without adding the crontab is worse than doing nothing: then nothing scheduled runs at all.
How do I add a custom cron interval, such as every 15 minutes?
Filter cron_schedules and add a key with interval in seconds and a translated display name, then pass that key as the recurrence to wp_schedule_event(). The filter has to be registered before the event is scheduled, or WordPress will not recognise the recurrence. Intervals shorter than about five minutes are unreliable, because WP-Cron only fires on page loads and never twice within one request.
Why is my cron event scheduled multiple times?
Because the schedule call ran more than once. Calling wp_schedule_event() on init without checking wp_next_scheduled() first adds another event on every single page load. Guard it, or schedule from an activation hook, which only fires once. To clean up an existing mess, wp_clear_scheduled_hook() removes every queued event for that hook.
How do I unschedule an event that was scheduled with arguments?
Arguments are part of the event's identity, so you have to pass the identical array back: wp_next_scheduled( 'my_hook', $args ) to get the timestamp, then wp_unschedule_event( $timestamp, 'my_hook', $args ). wp_clear_scheduled_hook( 'my_hook', $args ) does the same for every occurrence. Calling either with no arguments will not match an event that was scheduled with them.
Does wp_schedule_event() use the site timezone?
No. It takes a Unix timestamp, which is timezone-free, and WP-Cron compares it against UTC. A timestamp built with strtotime( 'tomorrow 03:00' ) resolves against the server clock, so a site set to Europe/Lisbon on a US-hosted server can run its "overnight" job in the afternoon. When the hour genuinely matters, build the timestamp from wp_date() or add the site's GMT offset.
Where do I paste the code from the Cron Event generator?
You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.
Can I use the generated code in client work or a commercial plugin?
Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.
The other Core generators in the library, plus the WordPress tools most often used alongside this one.