AlterLabAlterLab
Guide

Scheduler

Automate recurring scrapes with cron expressions. AlterLab runs your schedules, collects results, and delivers them via webhook.

Balance-Based Access

The number of schedules and minimum interval you can use depends on your credit balance. See the Balance-Based Limits section below.

How It Works

1

Create

Define a schedule with one or more URLs, a cron expression, and optional output formats and webhook URL.

2

Run

At each scheduled time, AlterLab submits a batch scrape for all URLs in the schedule. Credits are debited per run.

3

Deliver

Results are available via the execution history endpoint or delivered to your webhook URL when the run completes.

Create a Schedule

POST
/api/v1/schedules
curl -X POST https://api.alterlab.io/api/v1/schedules \
  -H "Authorization: Bearer your_jwt_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Daily product prices",
    "urls": [
      "https://store.example.com/product-1",
      "https://store.example.com/product-2"
    ],
    "cron": "0 9 * * *",
    "timezone": "America/New_York",
    "formats": ["json", "markdown"],
    "options": {
      "wait_for": ".price-tag",
      "timeout": 30
    },
    "webhook_url": "https://your-server.com/schedule-webhook"
  }'

Request Body

ParameterTypeRequiredDescription
namestringYesHuman-readable name (1–255 chars)
urlsstring[]YesURLs to scrape each run (1–100)
cronstringYesStandard 5-field cron expression (minute hour day month weekday)
timezonestringNoIANA timezone (default: UTC)
formatsstring[]Notext, markdown, json, html, rag
optionsobjectNowait_for, timeout, headers, cookies, proxy_country
webhook_urlstringNoURL to receive results after each run

Cron Syntax

Standard 5-field cron expressions. Only minute-level granularity (no seconds).

ExpressionSchedule
0 9 * * *Every day at 9:00 AM
*/15 * * * *Every 15 minutes
0 */6 * * *Every 6 hours
0 8 * * 1-5Weekdays at 8:00 AM
30 2 1 * *1st of each month at 2:30 AM

Balance-Based Limits

Schedule limits scale with your credit balance. Add more credits to unlock more schedules and shorter intervals.

BalanceMax Active SchedulesMin Interval
$0 – $50360 minutes
$50 – $2001015 minutes
$200 – $500255 minutes
$500+1001 minute

Manage Schedules

List Schedules

GET
/api/v1/schedules

Query parameters: limit (1–100, default 20), offset (default 0), active_only (default false).

curl https://api.alterlab.io/api/v1/schedules?active_only=true \
  -H "Authorization: Bearer your_jwt_token"

Update a Schedule

PATCH
/api/v1/schedules/{schedule_id}

Send only the fields you want to change. Changing cron or timezone recomputes next_run_at.

curl -X PATCH https://api.alterlab.io/api/v1/schedules/{schedule_id} \
  -H "Authorization: Bearer your_jwt_token" \
  -H "Content-Type: application/json" \
  -d '{ "cron": "0 */2 * * *", "name": "Every 2 hours" }'

Pause & Resume

# Pause
curl -X POST https://api.alterlab.io/api/v1/schedules/{schedule_id}/pause \
  -H "Authorization: Bearer your_jwt_token"

# Resume (recomputes next_run_at, re-checks balance limits)
curl -X POST https://api.alterlab.io/api/v1/schedules/{schedule_id}/resume \
  -H "Authorization: Bearer your_jwt_token"

Delete a Schedule

DELETE
/api/v1/schedules/{schedule_id}

Soft-deletes the schedule (deactivates it). Returns 204 No Content.

Execution History

GET
/api/v1/schedules/{schedule_id}/runs

Returns a paginated list of past executions for a schedule.

{
  "runs": [
    {
      "id": "run-uuid-...",
      "schedule_id": "schedule-uuid-...",
      "status": "completed",
      "job_ids": ["job-1-...", "job-2-..."],
      "batch_id": "batch-...",
      "urls_total": 2,
      "urls_completed": 2,
      "urls_failed": 0,
      "credits_used": 30000,
      "started_at": "2026-03-03T09:00:01Z",
      "completed_at": "2026-03-03T09:00:14Z",
      "created_at": "2026-03-03T09:00:00Z"
    }
  ],
  "total": 42
}

Webhook Delivery

If your schedule has a webhook_url, results are delivered after each run completes. The webhook payload follows the same format as batch webhooks.

Tip

Combine schedules with webhooks for fully automated data pipelines. Schedule the scrape, receive results at your endpoint, and process them without any polling.

Python Example

import requests

API_URL = "https://api.alterlab.io/api/v1"
HEADERS = {
    "Authorization": "Bearer your_jwt_token",
    "Content-Type": "application/json",
}

# Create a schedule
schedule = requests.post(
    f"{API_URL}/schedules",
    headers=HEADERS,
    json={
        "name": "Competitor prices - daily",
        "urls": [
            "https://competitor-a.com/product",
            "https://competitor-b.com/product",
        ],
        "cron": "0 9 * * *",
        "timezone": "America/New_York",
        "formats": ["json"],
        "webhook_url": "https://your-server.com/prices",
    },
).json()

print(f"Schedule created: {schedule['id']}")
print(f"Next run: {schedule['next_run_at']}")

# List active schedules
schedules = requests.get(
    f"{API_URL}/schedules?active_only=true",
    headers=HEADERS,
).json()

for s in schedules["schedules"]:
    print(f"  {s['name']} — next: {s['next_run_at']}, runs: {s['run_count']}")

# Check execution history
runs = requests.get(
    f"{API_URL}/schedules/{schedule['id']}/runs",
    headers=HEADERS,
).json()

for run in runs["runs"]:
    print(f"  Run {run['id']}: {run['status']} — {run['urls_completed']}/{run['urls_total']}")

Node.js Example

const API_URL = "https://api.alterlab.io/api/v1";
const headers = {
  Authorization: "Bearer your_jwt_token",
  "Content-Type": "application/json",
};

// Create a schedule
const schedule = await fetch(`${API_URL}/schedules`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    name: "Competitor prices - daily",
    urls: [
      "https://competitor-a.com/product",
      "https://competitor-b.com/product",
    ],
    cron: "0 9 * * *",
    timezone: "America/New_York",
    formats: ["json"],
    webhook_url: "https://your-server.com/prices",
  }),
}).then((r) => r.json());

console.log(`Schedule: ${schedule.id}, next run: ${schedule.next_run_at}`);

// List active schedules
const list = await fetch(`${API_URL}/schedules?active_only=true`, {
  headers,
}).then((r) => r.json());

for (const s of list.schedules) {
  console.log(`  ${s.name} — runs: ${s.run_count}`);
}

// Check runs
const runs = await fetch(`${API_URL}/schedules/${schedule.id}/runs`, {
  headers,
}).then((r) => r.json());

for (const run of runs.runs) {
  console.log(`  ${run.status}: ${run.urls_completed}/${run.urls_total}`);
}