Cron jobs
Scheduled tasks for a site: WordPress scheduled tasks, a PHP script, a web address or a command, every 5 minutes at most, stopped after 15 minutes, as the site's own user. Free on every plan; 5 per site, 25 per account. Times are UTC.
List a site's cron jobs
GET/api/v1/sites/{site_id}/cron-jobsScope cron:read
Every cron job of the site with its schedule (as written and in words, UTC), what it runs, whether it is on, why it is paused if it is (the site is frozen or suspended, it timed out 3 times in a row, or our team paused it), its next 3 run times and its last result. kinds lists what this site type may run. limits shows the per-site and per-account counts.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s "https://app.pbn.ltd/api/v1/sites/123/cron-jobs" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/cron-jobs", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cron-jobs", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"jobs": [
{
"id": 42,
"site_id": 1234,
"name": "WordPress scheduled tasks",
"kind": "wpcron",
"kind_label": "Run WordPress scheduled tasks",
"target": "",
"command": "wp cron event run --due-now",
"summary": "WordPress scheduled tasks (wp cron event run --due-now)",
"schedule": "7,22,37,52 * * * *",
"schedule_text": "Every 15 minutes (at :07, :22 ...)",
"schedule_spec": {
"mode": "minutes",
"every": 15
},
"enabled": true,
"state": "active",
"state_label": "On",
"state_reason": "",
"paused_reason": null,
"wp_pseudo_cron_off": true,
"next_runs": [
"2026-09-26T10:22:00Z",
"2026-09-26T10:37:00Z",
"2026-09-26T10:52:00Z"
],
"last_run_at": "2026-09-26T10:07:00Z",
"last_status": "ok",
"last_exit_code": 0,
"last_duration_seconds": 1.42,
"run_now_pending": false,
"created_at": "2026-09-26T09:00:00Z",
"updated_at": "2026-09-26T09:00:00Z"
}
],
"limits": {
"per_site": 5,
"per_account": 25,
"site_count": 1,
"account_count": 3,
"min_interval_minutes": 5,
"max_run_minutes": 15,
"timezone": "UTC"
},
"kinds": [
"wpcron",
"php",
"url",
"command"
],
"available": true,
"paused_reason": ""
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
List every cron job on the account
GET/api/v1/cron-jobsScope cron:read
All cron jobs on every site of the account (at most 25), each with its site id and domain.
Example
curl -s "https://app.pbn.ltd/api/v1/cron-jobs" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/cron-jobs", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/cron-jobs", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"jobs": [
{
"id": 42,
"site_id": 1234,
"name": "WordPress scheduled tasks",
"kind": "wpcron",
"kind_label": "Run WordPress scheduled tasks",
"target": "",
"command": "wp cron event run --due-now",
"summary": "WordPress scheduled tasks (wp cron event run --due-now)",
"schedule": "7,22,37,52 * * * *",
"schedule_text": "Every 15 minutes (at :07, :22 ...)",
"schedule_spec": {
"mode": "minutes",
"every": 15
},
"enabled": true,
"state": "active",
"state_label": "On",
"state_reason": "",
"paused_reason": null,
"wp_pseudo_cron_off": true,
"next_runs": [
"2026-09-26T10:22:00Z",
"2026-09-26T10:37:00Z",
"2026-09-26T10:52:00Z"
],
"last_run_at": "2026-09-26T10:07:00Z",
"last_status": "ok",
"last_exit_code": 0,
"last_duration_seconds": 1.42,
"run_now_pending": false,
"created_at": "2026-09-26T09:00:00Z",
"updated_at": "2026-09-26T09:00:00Z",
"domain": "example.com"
}
],
"count": 1,
"per_account": 25
}
}
Errors: rate_limited, scope_missing, unauthorized
One cron job with its last runs
GET/api/v1/sites/{site_id}/cron-jobs/{cron_id}Scope cron:read
The job and its last 10 runs: when, how long, the exit code, and the output (the first 1,000 and last 7,000 bytes of long output are kept).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
cron_id | path | integer | yes | The cron job id (see the list). |
Example
curl -s "https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"id": 42,
"site_id": 1234,
"name": "WordPress scheduled tasks",
"kind": "wpcron",
"kind_label": "Run WordPress scheduled tasks",
"target": "",
"command": "wp cron event run --due-now",
"summary": "WordPress scheduled tasks (wp cron event run --due-now)",
"schedule": "7,22,37,52 * * * *",
"schedule_text": "Every 15 minutes (at :07, :22 ...)",
"schedule_spec": {
"mode": "minutes",
"every": 15
},
"enabled": true,
"state": "active",
"state_label": "On",
"state_reason": "",
"paused_reason": null,
"wp_pseudo_cron_off": true,
"next_runs": [
"2026-09-26T10:22:00Z",
"2026-09-26T10:37:00Z",
"2026-09-26T10:52:00Z"
],
"last_run_at": "2026-09-26T10:07:00Z",
"last_status": "ok",
"last_exit_code": 0,
"last_duration_seconds": 1.42,
"run_now_pending": false,
"created_at": "2026-09-26T09:00:00Z",
"updated_at": "2026-09-26T09:00:00Z",
"runs": [
{
"id": 881,
"status": "ok",
"status_label": "Finished",
"trigger": "schedule",
"started_at": "2026-09-26T10:07:00Z",
"finished_at": "2026-09-26T10:07:02Z",
"duration_seconds": 1.42,
"exit_code": 0,
"output": "Success: Executed a total of 3 cron events.\n",
"output_bytes": 46,
"output_truncated": false,
"note": ""
}
]
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
The last runs of a cron job
GET/api/v1/sites/{site_id}/cron-jobs/{cron_id}/runsScope cron:read
Newest first. status: ok, failed (non-zero exit code), timeout (stopped at 15 minutes), skipped (the previous run was still going, or the server was paused) or error (could not start). trigger: schedule or manual (Run now).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
cron_id | path | integer | yes | The cron job id (see the list). |
limit | query | integer | no | How many (newest first, at most 20 are kept). Default: 20. |
Example
curl -s "https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42/runs" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42/runs", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42/runs", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"runs": [
{
"id": 881,
"status": "ok",
"status_label": "Finished",
"trigger": "schedule",
"started_at": "2026-09-26T10:07:00Z",
"finished_at": "2026-09-26T10:07:02Z",
"duration_seconds": 1.42,
"exit_code": 0,
"output": "Success: Executed a total of 3 cron events.\n",
"output_bytes": 46,
"output_truncated": false,
"note": ""
}
]
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
Add a cron job
POST/api/v1/sites/{site_id}/cron-jobsScope cron:write
Adds a job and puts it on the site's server within about a minute. It runs as the site's own user, in the site folder, with the site's PHP version, and is stopped after 15 minutes. Refused with limit_reached at 5 jobs on the site or 25 on the account, and with validation_failed for a schedule more often than every 5 minutes, a multi-line command, or a script path outside the site folder.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
name | body | string | no | A name you recognise (up to 80 characters). |
kind | body | string (one of: wpcron, php, url, command) | no | What runs. wpcron: WordPress scheduled tasks (wp cron event run --due-now, WordPress sites only); php: a PHP script, target = path inside the site folder ending in .php (not for Static HTML); url: open a web address, target = http(s) address; command: a shell command, target = the command (one line, up to 1,000 characters). Required when creating. |
target | body | string | no | The script path, web address or command (see kind). Not used for wpcron. |
schedule_mode | body | string (one of: minutes, hourly, daily, weekly, advanced) | no | How the schedule is given. minutes: every N minutes (every); hourly: once an hour at minute; daily: at hour:minute UTC; weekly: on weekday at hour:minute UTC; advanced: a 5-field cron expression. Required when creating. |
every | body | integer (one of: 5, 10, 15, 20, 30) | no | minutes mode: run every 5, 10, 15, 20 or 30 minutes. We pick a fixed start offset inside each window (e.g. :07, :22, :37, :52), so sites do not all start at the same second. |
minute | body | integer | no | hourly / daily / weekly: the minute (0-59). Default: a fixed minute picked for the job. |
hour | body | integer | no | daily / weekly: the hour, UTC. |
weekday | body | integer | no | weekly: 0 = Sunday, 1 = Monday ... 6 = Saturday. |
expression | body | string | no | advanced: five fields "minute hour day-of-month month day-of-week", UTC, e.g. "*/15 * * * *" or "30 2 * * 1-5". It may never run more often than every 5 minutes (checked across the whole hour, including from :55 to :00). A "*/N" minute field is moved to a fixed offset. |
enabled | body | boolean | no | Switch the job on (default) or off. |
wp_pseudo_cron_off | body | boolean | no | wpcron only: also stop WordPress running its scheduled tasks on page visits (sets DISABLE_WP_CRON in wp-config.php, only if it is not set already; taken out again when the job is deleted or switched off). Default true for wpcron. |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/cron-jobs" \
-H "Authorization: Bearer $PBN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "WordPress scheduled tasks", "kind": "wpcron", "schedule_mode": "minutes", "every": 15, "wp_pseudo_cron_off": true}'
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/cron-jobs", headers=headers, json={"name": "WordPress scheduled tasks", "kind": "wpcron", "schedule_mode": "minutes", "every": 15, "wp_pseudo_cron_off": True}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cron-jobs", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
body: JSON.stringify({"name": "WordPress scheduled tasks", "kind": "wpcron", "schedule_mode": "minutes", "every": 15, "wp_pseudo_cron_off": true})
});
console.log(res.status, await res.json());
Response
201
{
"data": {
"id": 42,
"site_id": 1234,
"name": "WordPress scheduled tasks",
"kind": "wpcron",
"kind_label": "Run WordPress scheduled tasks",
"target": "",
"command": "wp cron event run --due-now",
"summary": "WordPress scheduled tasks (wp cron event run --due-now)",
"schedule": "7,22,37,52 * * * *",
"schedule_text": "Every 15 minutes (at :07, :22 ...)",
"schedule_spec": {
"mode": "minutes",
"every": 15
},
"enabled": true,
"state": "active",
"state_label": "On",
"state_reason": "",
"paused_reason": null,
"wp_pseudo_cron_off": true,
"next_runs": [
"2026-09-26T10:22:00Z",
"2026-09-26T10:37:00Z",
"2026-09-26T10:52:00Z"
],
"last_run_at": "2026-09-26T10:07:00Z",
"last_status": "ok",
"last_exit_code": 0,
"last_duration_seconds": 1.42,
"run_now_pending": false,
"created_at": "2026-09-26T09:00:00Z",
"updated_at": "2026-09-26T09:00:00Z"
}
}
Errors: conflict, limit_reached, not_found, rate_limited, scope_missing, unauthorized, validation_failed
Change a cron job
PATCH/api/v1/sites/{site_id}/cron-jobs/{cron_id}Scope cron:write
Change any of the fields; the others stay as they are. To change the schedule give schedule_mode with its fields. Switching a job on again (enabled: true) also clears a pause after repeated timeouts. On the server within about a minute.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
cron_id | path | integer | yes | The cron job id (see the list). |
name | body | string | no | A name you recognise (up to 80 characters). |
kind | body | string (one of: wpcron, php, url, command) | no | What runs. wpcron: WordPress scheduled tasks (wp cron event run --due-now, WordPress sites only); php: a PHP script, target = path inside the site folder ending in .php (not for Static HTML); url: open a web address, target = http(s) address; command: a shell command, target = the command (one line, up to 1,000 characters). Required when creating. |
target | body | string | no | The script path, web address or command (see kind). Not used for wpcron. |
schedule_mode | body | string (one of: minutes, hourly, daily, weekly, advanced) | no | How the schedule is given. minutes: every N minutes (every); hourly: once an hour at minute; daily: at hour:minute UTC; weekly: on weekday at hour:minute UTC; advanced: a 5-field cron expression. Required when creating. |
every | body | integer (one of: 5, 10, 15, 20, 30) | no | minutes mode: run every 5, 10, 15, 20 or 30 minutes. We pick a fixed start offset inside each window (e.g. :07, :22, :37, :52), so sites do not all start at the same second. |
minute | body | integer | no | hourly / daily / weekly: the minute (0-59). Default: a fixed minute picked for the job. |
hour | body | integer | no | daily / weekly: the hour, UTC. |
weekday | body | integer | no | weekly: 0 = Sunday, 1 = Monday ... 6 = Saturday. |
expression | body | string | no | advanced: five fields "minute hour day-of-month month day-of-week", UTC, e.g. "*/15 * * * *" or "30 2 * * 1-5". It may never run more often than every 5 minutes (checked across the whole hour, including from :55 to :00). A "*/N" minute field is moved to a fixed offset. |
enabled | body | boolean | no | Switch the job on (default) or off. |
wp_pseudo_cron_off | body | boolean | no | wpcron only: also stop WordPress running its scheduled tasks on page visits (sets DISABLE_WP_CRON in wp-config.php, only if it is not set already; taken out again when the job is deleted or switched off). Default true for wpcron. |
Example
curl -s -X PATCH "https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42" \
-H "Authorization: Bearer $PBN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"kind": "php", "target": "scripts/nightly.php", "schedule_mode": "daily", "hour": 3, "minute": 20}'
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.patch("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42", headers=headers, json={"kind": "php", "target": "scripts/nightly.php", "schedule_mode": "daily", "hour": 3, "minute": 20}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42", {
method: "PATCH",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
body: JSON.stringify({"kind": "php", "target": "scripts/nightly.php", "schedule_mode": "daily", "hour": 3, "minute": 20})
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"id": 42,
"site_id": 1234,
"name": "WordPress scheduled tasks",
"kind": "wpcron",
"kind_label": "Run WordPress scheduled tasks",
"target": "",
"command": "wp cron event run --due-now",
"summary": "WordPress scheduled tasks (wp cron event run --due-now)",
"schedule": "7,22,37,52 * * * *",
"schedule_text": "Every 15 minutes (at :07, :22 ...)",
"schedule_spec": {
"mode": "minutes",
"every": 15
},
"enabled": true,
"state": "active",
"state_label": "On",
"state_reason": "",
"paused_reason": null,
"wp_pseudo_cron_off": true,
"next_runs": [
"2026-09-26T10:22:00Z",
"2026-09-26T10:37:00Z",
"2026-09-26T10:52:00Z"
],
"last_run_at": "2026-09-26T10:07:00Z",
"last_status": "ok",
"last_exit_code": 0,
"last_duration_seconds": 1.42,
"run_now_pending": false,
"created_at": "2026-09-26T09:00:00Z",
"updated_at": "2026-09-26T09:00:00Z"
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized, validation_failed
Delete a cron job
DELETE/api/v1/sites/{site_id}/cron-jobs/{cron_id}Scope cron:write · destructive
Deletes the job and its run history, and takes it off the server within about a minute. A run already going finishes.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
cron_id | path | integer | yes | The cron job id (see the list). |
confirm | body | boolean | yes | Must be true: the job and its run history are deleted. |
Example
curl -s -X DELETE "https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.delete("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42", {
method: "DELETE",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"deleted": true,
"id": 42
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized, validation_failed
Run a cron job now
POST/api/v1/sites/{site_id}/cron-jobs/{cron_id}/runScope cron:write
Starts the job on its server at once (once a minute per job, 10 times in 10 minutes per account). The run reports back like a scheduled one: read it with cron.runs a few seconds later (trigger "manual"). Refused while the site is frozen or suspended.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
cron_id | path | integer | yes | The cron job id (see the list). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42/run" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42/run", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cron-jobs/42/run", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"started": true,
"message": "Started.",
"job": {
"id": 42,
"site_id": 1234,
"name": "WordPress scheduled tasks",
"kind": "wpcron",
"kind_label": "Run WordPress scheduled tasks",
"target": "",
"command": "wp cron event run --due-now",
"summary": "WordPress scheduled tasks (wp cron event run --due-now)",
"schedule": "7,22,37,52 * * * *",
"schedule_text": "Every 15 minutes (at :07, :22 ...)",
"schedule_spec": {
"mode": "minutes",
"every": 15
},
"enabled": true,
"state": "active",
"state_label": "On",
"state_reason": "",
"paused_reason": null,
"wp_pseudo_cron_off": true,
"next_runs": [
"2026-09-26T10:22:00Z",
"2026-09-26T10:37:00Z",
"2026-09-26T10:52:00Z"
],
"last_run_at": "2026-09-26T10:07:00Z",
"last_status": "ok",
"last_exit_code": 0,
"last_duration_seconds": 1.42,
"run_now_pending": false,
"created_at": "2026-09-26T09:00:00Z",
"updated_at": "2026-09-26T09:00:00Z"
}
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized