Skip to content
API

Schedules

Run Actors and tasks on a cron schedule, as a view of your automations, and read each schedule log.

Schedules start Actors or tasks on a recurring basis using cron expressions. All endpoints are prefixed with https://api.scrape.ly/v2. Authenticate with Authorization: Bearer $SCRAPELY_TOKEN.

Creating, updating, and deleting schedules needs an API key: the Default key, a full-access key, or a scoped key with schedules:write, actors:run, and the run permission actor-default (see API keys & permissions). Actor run tokens receive 403 insufficient-permissions. Reading schedules and their logs needs schedules:read on a scoped key.

There is no endpoint to trigger a schedule manually. To run its Actor or task now, use the Actors API or Tasks API, or Run now on the automation with the same ID.

Schedules are automations

Every schedule is an automation with a schedule trigger, and the same ID works in both APIs. /v2/schedules is a view of your automations in the shape existing schedule clients expect:

  • It lists only automations with a schedule trigger. Automations that run manually or after another run finishes are not listed here, and GET, PUT, DELETE, and /log on them return 404. Use the Automations API for those.
  • An automation's filters, AI step, and deliveries appear under one extra key, automation, which is only present when it has any of them. It never contains secrets:
.json
"automation": {
  "hasAiStep": true,
  "filters": { "onlyWithResults": false, "onlyNewItems": { "keyFields": ["url"] }, "conditions": [] },
  "deliveries": [ { "channel": "email" }, { "channel": "webhook" } ]
}
  • A PUT changes only the fields you send. Replacing actions replaces the run steps and keeps the automation's AI step, filters, and deliveries. To change those, use Update automation.
  • DELETE also deletes the automation's run history.
  • notificationsEnabled is the automation's failure alert (options.notifyOnFailure in the Automations API). Both read and change the same setting.

Endpoints

MethodPathDescription
GET/v2/schedulesList schedules you own
POST/v2/schedulesCreate a schedule
GET/v2/schedules/{scheduleId}Get a schedule
PUT/v2/schedules/{scheduleId}Update a schedule
DELETE/v2/schedules/{scheduleId}Delete a schedule
GET/v2/schedules/{scheduleId}/logGet the schedule log

Errors and limits

Statuserror.typeWhen
400invalid-inputMissing name, cronExpression, or actions; more than 10 Actor or 10 task actions; a RUN_ACTOR action without actorId; a negative offset or limit.
400invalid-cron-expressionThe cron expression is not valid, or it never fires (for example 0 0 30 2 *). See Cron expressions.
400invalid-timezonetimezone is not an IANA time zone name.
400duplicate-nameYou already have a schedule with this name.
400actor-not-builtOne of your scheduled Actors has no successful build yet.
400actor-not-availableA scheduled Actor of someone else's can't be run right now.
400task-not-foundA RUN_ACTOR_TASK action names a task you don't own.
403insufficient-permissionsCreating, updating, or deleting with an Actor run token, or with an API key whose scopes don't cover it.
403limit-reachedCreating a schedule when you already have your plan's maximum number of schedules. Schedules and automations share one limit.
403forbiddenThe schedule belongs to another account.
404record-not-foundThe schedule does not exist, or it is an automation without a schedule trigger.
404record-or-token-not-foundA RUN_ACTOR action names an Actor that does not exist.

Get list of schedules

GET/v2/schedules
Query paramTypeDescription
searchstringSubstring match on the schedule name.
sortBystringcreatedAt (default), name, isEnabled, lastRunAt, or nextRunAt.
descboolean1 or true reverses the sort order. Default is ascending.
offsetnumberNumber of schedules to skip (default 0).
limitnumberPage size (default 1000, max 1000).
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  "https://api.scrape.ly/v2/schedules?search=daily&sortBy=nextRunAt&desc=1&limit=20"

nextRunAt is null while the schedule is disabled.

GET/v2/schedules?limit=5&sortBy=createdAt

Create schedule

POST/v2/schedules
Body fieldTypeRequiredDescription
namestring or nullYesSchedule name, unique among your schedules. The field must be present; null is allowed.
cronExpressionstringYesFive-field cron expression or macro such as @daily, evaluated in timezone. For example, 0 6 * * * means daily at 06:00. See Cron expressions.
actionsarrayYesNon-empty list of actions.
timezonestringNoIANA time zone name such as Europe/Berlin (default UTC). The cron expression is evaluated in this time zone, including daylight saving. An unknown name returns 400 invalid-timezone.
isEnabledbooleanNoWhether the schedule fires (default true).
isExclusivebooleanNoSkip a firing while the previous firing's runs are still going (default false).
titlestringNoDisplay title.
descriptionstringNoDescription.
notificationsEnabledbooleanNoThe schedule's failure alert. Returned as true unless you turned it off. See the note below.
consoleNotificationsEnabledbooleanNoStored and returned (default true).
.bash
curl -X POST https://api.scrape.ly/v2/schedules \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "daily-scrape",
    "cronExpression": "0 6 * * *",
    "actions": [
      { "type": "RUN_ACTOR_TASK", "actorTaskId": "kQ7mN2pXv9LsT4wRb" }
    ]
  }'

Returns 201 with { "data": { ...schedule } }. Actions in the response are normalized.

Actions

Each action is RUN_ACTOR or RUN_ACTOR_TASK; a missing type means RUN_ACTOR. A schedule can have at most 10 actions of each type, and every scheduled Actor must have at least one successful build. All actions start together each time the schedule fires.

Action fieldTypeDescription
typestringRUN_ACTOR or RUN_ACTOR_TASK. Not returned in responses.
actorIdstringActor to run (RUN_ACTOR, required).
actorTaskIdstringTask to run (RUN_ACTOR_TASK).
runInputobject{ "body": "<input as a string>", "contentType": "application/json" }. Bodies with a JSON content type are parsed; others are passed as a string. input is accepted as an alias. On a RUN_ACTOR_TASK action, a JSON object is merged over the task's input (see below).
runOptionsobjectbuild, timeoutSecs, memoryMbytes, maxItems, maxTotalChargeUsd, and restartOnError. build is ignored on someone else's Actor, which runs the User Build, and isn't saved or returned for it.

When a scheduled run starts, each setting falls back in order:

  • Input: for a RUN_ACTOR action, the action's runInput, then your default saved input for the Actor, then the prefill and default values from the Actor's input schema. For a RUN_ACTOR_TASK action, the task's input as it is when the schedule fires, with the action's runInput merged over it one level deep: top-level fields in runInput replace the task's, the other fields stay as saved. To change a nested value, send the whole top-level field.
  • Options: the action's runOptions, then the task's options, then the Actor's defaults, then build latest, a 3,600-second timeout, and 2,048 MB of memory. maxTotalChargeUsd falls back to the Actor's minimum.

Scheduled runs have origin set to SCHEDULER.

Responses normalize actions and give each one a 9-character id:

  • RUN_ACTOR_TASK returns id, actorTaskId, taskName (when known), and the stored input as input. runOptions is not returned but is still used.
  • RUN_ACTOR returns id, actorId, actorName (when known), runInput, and runOptions.

A RUN_ACTOR action on create:

.json
{
  "type": "RUN_ACTOR",
  "actorId": "eLN5rTB8sYTBWdfzx",
  "runInput": { "body": "{\"startUrls\":[{\"url\":\"https://example.com\"}]}", "contentType": "application/json" },
  "runOptions": { "memoryMbytes": 2048, "timeoutSecs": 3600 }
}

Cron expressions

cronExpression has five space-separated fields, evaluated in the schedule's timezone:

FieldValues
Minute0-59
Hour0-23
Day of month1-31
Month1-12 or JAN-DEC
Day of week0-7 or SUN-SAT (0 and 7 are Sunday)

Each field can be *, a number (5), a range (1-5), a list (1,15,30 or 1-5,10), or a step: */15, 1-30/5, or 5/15 (from 5 to the end of the range, every 15). Names are case-insensitive. ? means the same as * in the day fields.

Instead of the five fields, you can use a macro:

MacroSame as
@yearly, @annually0 0 1 1 *
@monthly0 0 1 * *
@weekly0 0 * * 0
@daily, @midnight0 0 * * *
@hourly0 * * * *

The rules follow standard (Vixie) cron:

  • When both day of month and day of week are restricted, a day matches if either matches. 0 9 1 * MON fires on the 1st of the month and on every Monday.
  • Daylight saving: a schedule with a fixed minute and hour fires once when the clock falls back, on the first occurrence of that time. If the clock springs forward over that time, it fires right after the jump. Schedules with * or a step in the minute or hour field follow elapsed time, so they have no runs in the skipped hour and run again in the repeated hour.
ExpressionFires
*/15 * * * *Every 15 minutes
0 6 * * *Daily at 06:00 in timezone
30 8 * * MON-FRIWeekdays at 08:30
0 0 1 * *Midnight on the 1st of each month
@daily with timezone Europe/BerlinMidnight Berlin time (22:00 or 23:00 UTC)

The scheduler checks for due schedules once a minute, so runs start up to about a minute after the scheduled time. Times missed while the scheduler was down are skipped, not caught up.

Get schedule

GET/v2/schedules/{scheduleId}
.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/schedules/9d4b2f6e-7a1c-4e3b-8f5d-2c0a1b9e7d3f

Returns { "data": { ...schedule } } with normalized actions.

GET/v2/schedules/9d4b2f6e-7a1c-4e3b-8f5d-2c0a1b9e7d3f

Update schedule

PUT/v2/schedules/{scheduleId}

Send only the fields you want to change; the fields are the same as Create schedule. actions, if sent, replaces the whole list, and the action limits and build check apply to the new list. nextRunAt is recalculated.

.bash
curl -X PUT https://api.scrape.ly/v2/schedules/9d4b2f6e-7a1c-4e3b-8f5d-2c0a1b9e7d3f \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "isEnabled": false,
    "cronExpression": "0 7 * * *"
  }'

Returns the updated schedule.

Delete schedule

DELETE/v2/schedules/{scheduleId}

Returns 204 No Content.

.bash
curl -X DELETE -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/schedules/9d4b2f6e-7a1c-4e3b-8f5d-2c0a1b9e7d3f

Get schedule log

GET/v2/schedules/{scheduleId}/log

Returns JSON { "data": [...] }, not plain text. Each entry is one firing of the schedule, oldest first. The newest 30 firings are kept.

FieldTypeDescription
messagestringWhat happened when the schedule fired.
levelstringINFO or ERROR.
createdAtstringISO 8601 timestamp.

Messages take these forms:

MessageLevel
Successfully started N run(s): <runId>, <runId>INFO
Started N run(s), M error(s): <details>ERROR
Skipped: the previous run is still going (exclusive).INFO
Skipped a firing missed during an outage (due <time>).INFO
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/schedules/9d4b2f6e-7a1c-4e3b-8f5d-2c0a1b9e7d3f/log
GET/v2/schedules/9d4b2f6e-7a1c-4e3b-8f5d-2c0a1b9e7d3f/log