# Apify Missed Schedule Monitor (`automa-flow/apify-missed-schedule-monitor`) Actor

Check Apify schedules for runs that never started, exclusive-overlap skips, late starts and failed runs. One row per schedule, no heartbeat code in your Actors.

- **URL**: https://apify.com/automa-flow/apify-missed-schedule-monitor.md
- **Developed by:** [Vadim Bezrukov](https://apify.com/automa-flow) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 schedule checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Apify Missed Schedule Monitor

Find the Apify schedules that should have started a run and did not, without adding heartbeat code to every Actor or Task.

When a scheduled scraper goes quiet, the usual question is: did it fail, did it run late, did Apify skip it because the previous run was still going, or did the schedule never fire at all? This Actor reads your schedules and run history through the Apify API and answers that for every expected run in the window, one row per schedule.

It is an independent Actor by automa-flow, not an official Apify feature. It only reads your account.

### Quick start

1. In Apify Console, open **Settings > API & Integrations** and create a token you can revoke. The Actor only reads schedules, schedule logs and run history. A scoped token works when it can read your schedules (with their logs) and your Actor runs; a token that cannot read them fails the run as `AUTH_SCOPE_INSUFFICIENT` and the message names the read that was refused.
2. Paste the token into **Apify account token**. Leave **Schedule IDs** empty to check every schedule the token can see, or list the IDs you care about.
3. Run it. You get one row per schedule. Act on `MISSING_INVOCATION` and `RUN_FAILED`. `SKIPPED_OVERLAP` means the previous run was still busy, which is usually expected.

Want to see the output first? Leave the token empty. The run returns a labeled sample (`record_type: DEMO_SCHEDULE_CHECK`, `is_demo: true`) instead of checking an account.

### What each status means

| Status | Meaning |
| --- | --- |
| `OK` | A scheduler run matched the expected time and started inside the grace period. |
| `RUNNING` | The matching run has not finished yet. The next check updates it. |
| `WAITING_GRACE` | The slot is due, and the grace period is still open. |
| `LATE_START` | The matching run started after the grace period. |
| `SKIPPED_OVERLAP` | The schedule is exclusive and its previous run was still active, so Apify skipped this start. |
| `RUN_FAILED` | The matching run ended as `FAILED`, `TIMED-OUT` or `ABORTED`. |
| `MISSING_INVOCATION` | The grace period passed and no run from this schedule exists for the slot. |
| `DISABLED` | The schedule is disabled, so nothing was expected. |
| `UNVERIFIABLE_*` | The data cannot prove a miss (daylight-saving edge, config edit, short history, duplicate actions, a token that cannot read the schedule log), so the slot is not called missing. |
| `SOURCE_CONTRACT_AMBIGUOUS` | Run metadata or the schedule log disagree, so the slot is not called missing. |
| `NOT_FOUND` | A schedule ID you listed does not exist. |
| `SOURCE_FAILED` | That schedule could not be read. It is not treated as healthy or missing. |
| `OMITTED` | Not checked in this run because of `maxSchedules` or your charge limit. It is checked next time. |

A slot is matched by the run's own scheduler metadata (`meta.scheduleId`, `meta.scheduledAt`) and the Actor or Task it targets, never by start time alone. A miss or an overlap skip is confirmed by the schedule's own log, where Apify records that it skipped the start or could not start it. If a miss is followed by a late run within six hours, the next check turns it into `LATE_START`.

### Example row

Illustrative values, shortened. Every row also carries the schedule's cron expression, timezone, check window, matched run IDs and a fingerprint.

```json
{
  "record_type": "SCHEDULE_CHECK",
  "schedule_id": "schedule_123",
  "schedule_name": "daily-products",
  "status": "SKIPPED_OVERLAP",
  "severity": "WARNING",
  "expected_invocations": 3,
  "matched_runs": 2,
  "new_incidents": 1,
  "incidents": [
    {
      "status": "SKIPPED_OVERLAP",
      "expected_at": "2026-10-01T19:30:00Z",
      "is_new": true,
      "explanation": "The previous scheduled run was still active when this exclusive occurrence was due."
    }
  ]
}
```

`new_incidents` counts problems this check found for the first time, so an alert can fire once instead of on every run. `RUN_SUMMARY` in the run's key-value store has the totals.

### Run it on a schedule

Save a Task with your token and the schedules to watch, then schedule that Task every 15, 30 or 60 minutes. Each schedule keeps its own checkpoint, so a check continues where the last one stopped (up to 24 hours back). Use a different `stateNamespace` for each Task that should keep its own checkpoint.

For alerts, add a webhook or an integration on the Task (for example on run succeeded) and filter the dataset for rows where `new_incidents` is above 0. The Actor does not send email, Slack or Telegram messages itself.

A saved Task with an empty token fails as `AUTH_MISSING` instead of returning sample rows, so a monitoring Task never goes quiet on demo data.

### Related automa-flow tools

- **Workflows outside Apify:** [Workflow Heartbeat Monitor](https://apify.com/automa-flow/workflow-heartbeat-monitor) watches n8n, Make and cron jobs through a heartbeat at their last successful step, including runs that finish with zero useful output.

Each Actor runs and charges separately. This Actor does not call them.

### Pricing

Pay per event:

- **Actor start**: $0.0005 per run.
- **Schedule checked** (`schedule_checked`): $0.001 for each schedule whose check finalized an expected run that no earlier check reported, whether that run was on time, late, failed, skipped or missing.

Free: disabled schedules, schedules with nothing due, slots a later check looks at again, a run that is still in progress (it is charged once, when it finishes), `NOT_FOUND`, unreadable schedules, the demo and retries.

For example, one schedule that runs every 30 minutes, checked every 15 minutes, costs about $0.10 a day: 96 starts and 48 checked slots. A run never charges more than the maximum cost per run you set (`maxTotalChargeUsd` in the API). When that limit is reached, the remaining schedules are `OMITTED` and checked on the next run.

### Token and security

The token is a secret input. It is used only as Bearer authentication to `https://api.apify.com`, and it is never printed or written to the dataset or key-value store. The Actor never creates, edits or deletes schedules, and never restarts runs. It runs with limited permissions of its own.

A rejected token fails the run as `AUTH_INVALID`. A token that cannot read schedules or runs fails as `AUTH_SCOPE_INSUFFICIENT`. If only the schedule log is unreadable, the check continues, but a slot with no run becomes `UNVERIFIABLE_LOG_ACCESS` instead of `MISSING_INVOCATION`, because only the log can prove a miss.

### API, n8n, Make and AI agents

Start the Actor with the same input JSON from the API, n8n, Make or Zapier. One unreadable schedule does not stop the others.

AI agents can call it through the Apify MCP server:

`https://mcp.apify.com?tools=automa-flow/apify-missed-schedule-monitor`

The agent should pass the user's own read-only token and treat `MISSING_INVOCATION` as the actionable miss. `SKIPPED_OVERLAP` is not a miss.

### Limitations

- The first check of a schedule starts from the later of `lookbackMinutes` and the schedule's last edit, because the configuration before an edit is not known yet.
- A check looks back at most 24 hours. An older gap is reported as `UNVERIFIABLE_GAP`.
- At most 400 run details are read per check. A run that could not be read leaves its slot `SOURCE_CONTRACT_AMBIGUOUS` instead of failing the other schedules.
- If the run history in the window exceeds 50,000 runs, the run fails as `INPUT_TOO_BROAD` instead of silently truncating.
- Daylight-saving gaps and repeated hours are `UNVERIFIABLE_DST_EDGE` when no run matches.
- Two actions in one schedule that target the same Actor or Task are `UNVERIFIABLE_ACTION_MAPPING`; the Actor does not guess which action a run belongs to.

### How it works

The Actor reads your schedules, expands each cron expression in its IANA timezone, and lists your runs for the window. The run list does not say which schedule started a run, so the Actor reads the details of the scheduler runs that can decide a slot: runs of the same Actor or Task near the slot, and the previous run of an exclusive schedule. Runs of unrelated Actors and Tasks are never read. Checkpoints live in a named key-value store in your account, `apify-missed-schedule-monitor-state`, keyed by the namespace and the selected schedule IDs; the token is not part of the key. A failed read never overwrites the last good state.

Update history is in the [changelog](https://apify.com/automa-flow/apify-missed-schedule-monitor/changelog).

# Changelog

This Actor's version history is a separate document: https://apify.com/automa-flow/apify-missed-schedule-monitor/changelog.md

# Actor input Schema

## `accountToken` (type: `string`):

Read-only token you can revoke, from Apify Console > Settings > API & Integrations. Leave empty for a labeled sample run (DEMO_SCHEDULE_CHECK rows). A saved Task without a token fails as AUTH_MISSING. The token is never printed or written to the Dataset or key-value store.

## `scheduleIds` (type: `array`):

Leave empty to evaluate every schedule this token can read. Otherwise evaluate only these schedule IDs. A missing ID is reported as NOT_FOUND and is not charged.

## `lookbackMinutes` (type: `integer`):

How far back the first check looks when this state namespace has no saved checkpoint, up to 24 hours. Later checks continue from each schedule's last check.

## `graceSeconds` (type: `integer`):

An expected invocation stays WAITING_GRACE until this many seconds after its scheduled time. A start later than that is LATE_START.

## `overlapHistoryHours` (type: `integer`):

How far back a still-open exclusive predecessor may have started. The Actor does not download this whole period for every run. It loads the check window plus a short margin, then looks up the latest preceding run only for exclusive slots that need it.

## `maxSchedules` (type: `integer`):

Hard cap after discovery or filtering. When more schedules match, the Actor keeps the lowest schedule IDs and reports how many were omitted.

## `stateNamespace` (type: `string`):

Separate saved checkpoints when several monitoring Tasks share this Actor. Use letters, digits, dots, underscores, or hyphens, 1-64 characters.

## Actor input object example

```json
{
  "scheduleIds": [],
  "lookbackMinutes": 180,
  "graceSeconds": 120,
  "overlapHistoryHours": 168,
  "maxSchedules": 100,
  "stateNamespace": "default"
}
```

# Actor output Schema

## `scheduleChecks` (type: `string`):

One row per schedule that was evaluated, not found, or could not be read.

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "scheduleIds": [],
    "lookbackMinutes": 180,
    "graceSeconds": 120,
    "overlapHistoryHours": 168,
    "maxSchedules": 100,
    "stateNamespace": "default"
};

// Run the Actor and wait for it to finish
const run = await client.actor("automa-flow/apify-missed-schedule-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "scheduleIds": [],
    "lookbackMinutes": 180,
    "graceSeconds": 120,
    "overlapHistoryHours": 168,
    "maxSchedules": 100,
    "stateNamespace": "default",
}

# Run the Actor and wait for it to finish
run = client.actor("automa-flow/apify-missed-schedule-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "scheduleIds": [],
  "lookbackMinutes": 180,
  "graceSeconds": 120,
  "overlapHistoryHours": 168,
  "maxSchedules": 100,
  "stateNamespace": "default"
}' |
apify call automa-flow/apify-missed-schedule-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automa-flow/apify-missed-schedule-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/aM48Bu15ASR4RLe7f/builds/gGRvMuaTZGT5D7FaV/openapi.json
