---
title: "as_schedule_recurring_action: Signature & Interval"
description: "as_schedule_recurring_action documentation: PHP signature, first-run timestamp, interval seconds, group and $unique parameters, with recurring code examples."
url: "https://wpwebhooks.org/blog/as-schedule-recurring-action-reference/"
date: "2026-07-04"
---

# as_schedule_recurring_action: Signature & Interval

**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()`](https://actionscheduler.org/api/#function-reference--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](https://actionscheduler.org/), 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()`](https://developer.wordpress.org/reference/functions/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()`](https://wpwebhooks.org/blog/action-scheduler-api-functions/) 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.

| Goal | Interval value | WordPress constant |
| --- | --- | --- |
| Every 5 minutes | 300 | `5 * MINUTE_IN_SECONDS` |
| Hourly | 3600 | `HOUR_IN_SECONDS` |
| Every 6 hours | 21600 | `6 * HOUR_IN_SECONDS` |
| Daily | 86400 | `DAY_IN_SECONDS` |
| Weekly | 604800 | `WEEK_IN_SECONDS` |

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()`](https://developer.wordpress.org/reference/functions/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.

| Feature | wp\_schedule\_event (recurring) | as\_schedule\_recurring\_action |
| --- | --- | --- |
| Schedule unit | Named schedule (hourly, daily…) | Interval in seconds |
| Missed runs | Can fire in a burst on next load | One run, then reschedule forward |
| Retry | None | Automatic on failure |
| Per-run log | None | Full attempt history in wp-admin |
| Cancel one series | wp\_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](https://wpwebhooks.org/blog/action-scheduler-api-functions/).

/ 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](https://wpwebhooks.org/blog/action-scheduler-api-functions/).

## Structured data

```json
{"@context":"https://schema.org","@type":"Article","headline":"as_schedule_recurring_action: Signature & Interval","description":"as_schedule_recurring_action documentation: PHP signature, first-run timestamp, interval seconds, group and $unique parameters, with recurring code examples.","datePublished":"2026-07-04","dateModified":"2026-07-04","author":{"@type":"Person","name":"Mateusz Skorupa","url":"https://wpwebhooks.org/about/"},"publisher":{"@type":"Organization","name":"WP Webhooks","url":"https://wpwebhooks.org"},"url":"https://wpwebhooks.org/blog/as-schedule-recurring-action-reference/","image":"https://wpwebhooks.org/og_image.jpg","keywords":["as schedule recurring action","as schedule recurring action signature","as schedule recurring action interval","action scheduler recurring action","as schedule recurring action parameters","action scheduler recurring interval seconds"]}

{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"WP Webhooks","item":"https://wpwebhooks.org/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://wpwebhooks.org/blog/"},{"@type":"ListItem","position":3,"name":"as_schedule_recurring_action: Signature & Interval","item":"https://wpwebhooks.org/blog/as-schedule-recurring-action-reference/"}]}

{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"What is the signature of as_schedule_recurring_action?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"How does the interval work in as_schedule_recurring_action?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"What happens to missed recurring runs?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"How do I register a recurring action without creating duplicates?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"How do I stop a recurring action?","acceptedAnswer":{"@type":"Answer","text":"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."}}]}
```
