schedule_tracker.pycreates a scheduled Batch job that scrapes every product at 09:00 each day.run_now.pyfires the job immediately so you can test it.save_snapshot.pywrites the latest run toproducts-YYYY-MM-DD.json.stop_tracker.pypauses and deletes the schedule.- Optionally,
webhook_receiver.pygets a signedrun.completedevent after every run.
- How a scheduled job stores its URLs as a template and creates a new run on every fire.
- How to set a calendar schedule with a timezone.
- How to verify a signed webhook from Zenrows.
Create the daily schedule
A scheduled Batch job keeps its tasks andzenrows_params as a template. Each time the schedule fires, Zenrows starts a fresh run of those tasks, the same kind of run you waited on in Lesson 4: one Fetch request per URL. You set the cadence with one of three builders from zenrows.batch: Rate (every 60 minutes or more, in minute, hour or day units), Calendar (times of day on a daily, weekly or monthly cadence, in a timezone) or At (one future time).
Create schedule_tracker.py. It reuses product_urls.txt from Lesson 3 and PRODUCT_SELECTORS from Lesson 2:
Python (SDK)
python schedule_tracker.py:
tracker_job_id.txt so the next scripts can find the job. Set timezone to any IANA timezone name, and times_of_day to full hours in 24-hour format, such as "09:00" or "18:00".
Test it with a run now
You don’t have to wait until tomorrow.rerun() on a scheduled job that hasn’t fired yet starts a run from its template right away. Create run_now.py:
Python (SDK)
client.job(job_id) points at the job by ID without fetching it first. Run python run_now.py:
Save a dated snapshot
Each run’s results stay available after it finishes.save_snapshot.py reads the job’s latest run and writes it to a file named after today’s date, so every day adds one snapshot you can compare with the day before:
Python (SDK)
python save_snapshot.py:
save_snapshot.py after each daily run, by hand, from a cron job, or from the webhook in the next section.
Get notified when a run finishes
A webhook tells your app the moment a run ends, so it can save the snapshot without polling. Zenrows POSTs arun.completed event when a run completes and a run.failed event when it fails. With signature: True, each delivery is signed with your organization’s HMAC key, so your receiver can prove it came from Zenrows.
1
Create a signing key and attach the webhook
Your receiver needs a public HTTPS URL. This script creates your organization’s HMAC key, prints its secret (Zenrows returns it once, so store it now) and attaches the webhook to the tracker job:If your organization already has a key, reuse its secret and skip
Python (SDK)
rotate_hmac_key(). See Manage HMAC keys.2
Verify each delivery
Each signed delivery carries an The receiver verifies the raw request body as received, before any JSON parsing, so the signature always matches. The event body carries
X-Signature header in the form t=<unix>,v1=<hex>,kid=<key id>. Recompute HMAC-SHA256(secret, "<t>.<raw body>") with the base64-decoded secret and compare it to v1. Deliveries arrive at least once, so skip any X-ZenRows-Event-Id you’ve already handled. This Flask receiver (pip install flask) does both:Python (Flask)
event_type, job_id, run_id and the run’s stats, so your handler knows which run to download.Pause or delete the schedule
A scheduled job keeps firing, and spending credits, until you pause or delete it. When you’re done with the course, runstop_tracker.py:
Python (SDK)
job.schedule.pause() alone, and job.schedule.resume() later. To change the cadence, use job.schedule.update(...) with a new builder, see Scheduled jobs.
Ask your AI agent
An agent handles both halves of the tracker: a coding agent sets up the schedule, and any MCP client checks the results whenever you ask. The schedule itself runs in the Zenrows cloud, so nothing needs to stay open on your side. Set up the daily schedule. Paste this prompt into a coding agent such as Claude Code or Cursor, with the Zenrows docs connected. The agent looks up the details and writes the calls.What you built
Your tracker now runs itself. Over six lessons you used all five Zenrows primitives in one project:Where to go next
Turn a whole catalog into a structured dataset
The production version of this course: crawl, scrape and store a full catalog.
Detect changes on any page and get alerted
Compare each day’s snapshot with the last one and alert on what changed.
Choose the right Zenrows primitive
When to reach for Fetch, Extract, Crawl, Batch or Browser on your own project.
Zenrows Academy
Browse every recipe by goal, surface and industry.