WP Webhooks / Blog / WordPress internals
Article · WordPress internals

The Complete as_schedule_recurring_action Reference

as_schedule_recurring_action documentation: PHP signature, first-run timestamp, interval seconds, group and $unique parameters, with recurring code examples.

7 min 2026-07-04
#action-scheduler#wordpress#php

TL;DR: as_schedule_recurring_action() runs a hook repeatedly at a fixed interval in seconds, re-enqueuing the next occurrence only after the current one finishes.

  • Signature: as_schedule_recurring_action( int $timestamp, int $interval_in_seconds, string $hook, array $args = [], string $group = '', bool $unique = false, int $priority = 10 )
  • $timestamp is the first run; each later run is scheduled at previous_run + $interval, so missed runs do not stack.
  • Guard registration with as_has_scheduled_action() or $unique = true so activation does not create a second recurring series.

/ Signature

What is the as_schedule_recurring_action signature?

as_schedule_recurring_action() schedules a background action to fire again and again at a fixed number of seconds apart. It is part of Action Scheduler, the queue library bundled with WooCommerce. Like every scheduling function, it maps to a WordPress hook: each run calls do_action( $hook, ...$args ), so the executing code is an ordinary add_action() callback.

PHP — as_schedule_recurring_action full signature

as_schedule_recurring_action(
    int    $timestamp,             // Unix time of the first run
    int    $interval_in_seconds,   // gap between runs, e.g. 3600 = hourly
    string $hook,                  // action hook fired via do_action()
    array  $args     = [],          // args spread into the callback
    string $group    = '',          // label for filtering / bulk cancel
    bool   $unique   = false,       // skip if an identical series is pending
    int    $priority = 10           // 0-255, lower runs first (AS 3.5+)
): int;  // returns the first action's ID, or 0 on failure

It mirrors as_schedule_single_action() exactly, with one extra second argument: $interval_in_seconds. Everything after it — $hook, $args, $group, $unique, $priority — behaves identically.

/ Interval

How does the $interval_in_seconds parameter work?

The interval is a plain integer count of seconds — there are no named schedules like WP-Cron's hourly or daily. Compute it directly: HOUR_IN_SECONDS (3600), DAY_IN_SECONDS (86400), or your own value. Crucially, the interval is measured from when each run completes, not from a fixed wall-clock grid. After an occurrence finishes, Action Scheduler enqueues the next one at that run's scheduled time plus the interval, so a job set to run "every hour" drifts slightly if runs are delayed. When you need a job pinned to the clock (always at the top of the hour, always at 06:00), use as_schedule_cron_action() with a cron expression instead.

GoalInterval valueWordPress constant
Every 5 minutes3005 * MINUTE_IN_SECONDS
Hourly3600HOUR_IN_SECONDS
Every 6 hours216006 * HOUR_IN_SECONDS
Daily86400DAY_IN_SECONDS
Weekly604800WEEK_IN_SECONDS
How as_schedule_recurring_action re-enqueues each occurrenceas_schedule_recurring_action runs the hook at the first timestamp, then after each execution completes it schedules the next occurrence at the previous run plus the interval in seconds. Because the next slot is only booked after the current run finishes, missed runs do not stack up the way overdue WP-Cron events can.

not yet

yes

as_schedule_recurring_action()
first timestamp + interval

run at first timestamp

do_action( hook, ...args )

run completes

schedule next at
previous run + interval

next timestamp reached?

FIG 01 — Fixed-interval re-enqueue loop

/ Missed runs

What happens to missed recurring runs?

They do not pile up. Because the next occurrence is only scheduled after the previous one finishes, a site that goes hours without triggering the queue runs the action once when the queue next fires, then schedules the following occurrence from there. This is the opposite of WP-Cron's wp_schedule_event(), where a backlog of overdue events can all fire in a burst on the next page load. The trade-off is drift: if runs are consistently late, "hourly" becomes "every hour plus the lateness". For most maintenance work that is exactly what you want.

/ Registration

How do you register a recurring action without duplicates?

The classic bug is calling as_schedule_recurring_action() on every plugin load, or on every activation, which creates a second, third, and fourth parallel series — all firing the same hook. Guard the call so it only schedules when nothing is already queued. The two safe patterns are an as_has_scheduled_action() check or the $unique = true flag.

PHP — register once, on init or activation

add_action( 'init', function() {
    // Only schedule if this series is not already queued
    if ( as_has_scheduled_action( 'myplugin_hourly_sync', [], 'sync' ) ) {
        return;
    }

    as_schedule_recurring_action(
        time(),
        HOUR_IN_SECONDS,
        'myplugin_hourly_sync',
        [],
        'sync'
    );
} );

add_action( 'myplugin_hourly_sync', function() {
    // recurring background work runs here
} );
Recurring actions schedule the next run after the current one finishes — so a stalled site never wakes up to a stampede of overdue jobs. — Action Scheduler admin guide

/ vs recurring WP-Cron

How does it compare to a recurring WP-Cron event?

Both repeat a hook on a schedule, but Action Scheduler adds persistence, per-run logging, and retry — and it will not fire a backlog all at once.

Featurewp_schedule_event (recurring)as_schedule_recurring_action
Schedule unitNamed schedule (hourly, daily…)Interval in seconds
Missed runsCan fire in a burst on next loadOne run, then reschedule forward
RetryNoneAutomatic on failure
Per-run logNoneFull attempt history in wp-admin
Cancel one serieswp_clear_scheduled_hook (all)as_unschedule_all_actions by hook/group

/ Cancel

How do you stop a recurring action?

Call as_unschedule_all_actions( $hook, $args, $group ) to cancel the whole series, typically on plugin deactivation. It is safe to call even when nothing is scheduled — it is a no-op rather than an error. To inspect before acting, as_next_scheduled_action( $hook ) returns the next run's Unix timestamp, or false if the series has stopped. The full query-and-cancel set is covered in the Action Scheduler PHP API reference.

/ Webhooks

Where does this fit with webhook delivery?

Recurring actions are for polling and maintenance — syncing a remote list every hour, pruning logs nightly — not for event-driven webhooks. When you want an outbound call the moment something happens in WordPress, you want an event trigger, not a timer: hook do_action and POST on the event itself, behind a queue that retries and logs each delivery. Use as_schedule_recurring_action() when the trigger is genuinely time-based, and an event-driven webhook when it is not. For the whole scheduling family, see the API functions reference.

FAQ

Common questions always ask.

Don't see yours? Open an issue on GitHub or check the full reference in the API docs.

What is the signature of as_schedule_recurring_action? +
as_schedule_recurring_action( int $timestamp, int $interval_in_seconds, string $hook, array $args = [], string $group = '', bool $unique = false, int $priority = 10 ) and it returns the first action's integer ID. It matches as_schedule_single_action exactly but adds $interval_in_seconds as the second argument.
How does the interval work in as_schedule_recurring_action? +
The interval is a plain integer count of seconds — for example 3600 for hourly — with no named schedules. The next occurrence is scheduled at the previous run plus the interval, measured from when each run completes rather than from a fixed clock grid, so runs can drift slightly if they are delayed.
What happens to missed recurring runs? +
They do not stack up. Because the next occurrence is only scheduled after the current one finishes, a site that goes idle runs the action once when the queue next fires, then schedules the following run from there. This is unlike recurring WP-Cron, where a backlog of overdue events can fire in a burst.
How do I register a recurring action without creating duplicates? +
Guard the call so it only schedules when nothing is already queued. Either check if ( ! as_has_scheduled_action( $hook, $args, $group ) ) before scheduling, or pass $unique = true. Both prevent activation or repeated init hooks from creating multiple parallel series firing the same hook.
How do I stop a recurring action? +
Call as_unschedule_all_actions( $hook, $args, $group ) to cancel the whole series, typically on plugin deactivation. It is safe to call even when nothing is scheduled — it is a no-op rather than an error. Use as_next_scheduled_action( $hook ) to check the next run timestamp before cancelling.
Ready

Your next automation is
one sentence away.

$ wp plugin install flowsystems-webhook-actions --activate