How to schedule a scraper with relative dates
The most common failure in a scheduled scraping job is not a block. It is a date literal that was correct when you saved it and quietly wrong a week later — still running, still succeeding, still billing, and measuring something nobody asked for.
The short version
- Decide what should stay constant. A price watch keeps the lead time constant (always 30 days out). A review feed keeps the lookback constant (always the last 2 days). Those are different constants and they need different inputs.
- Write the date as a relative value. Stay dates take "30 days", "6 weeks", "+40d", "today" or "tomorrow". Review cut-offs take "30 days", "2 weeks", "6 months" and mean "on or after that far back".
- Let the source ordering end the run. With newest-first ordering, a lookback cut stops paging at the first page entirely older than the cut, so a daily run reads one or two pages per entity.
- Watch the run, not just the schedule. A daily canary that asserts rows and shape catches a source change; a schedule that never fails is not the same as one that is still right.
Two different constants
Every scheduled scraping job is holding one thing steady. Get the wrong one and the series you build is not comparable with itself.
Forward-looking: hold the lead time constant. A hotel or flight price watch should ask the same question every morning — "what does a stay 30 days from today cost?" — because that is what makes today's number comparable with yesterday's. Hard-code 2026-10-03 and the lead time shrinks by one day every day: after a month you are watching a last-minute rate and comparing it with an advance purchase.
Backward-looking: hold the lookback constant. A review or complaints feed should ask "what has appeared since two days ago?" every day. Hard-code the cut and you re-read the same growing window forever, paying more each run for rows you already have.
{ "hotelIds": ["536251"], "checkIn": "30 days", "nights": 2, "currency": "EUR" }
{ "locationIds": ["4509998"], "sinceDate": "2 days", "maxRating": 3, "requireText": true }
Both of those inputs mean exactly the same thing on the day you save them and on the day a year from now.
The grammar
Stay and departure dates accept:
2026-10-03 an absolute date
30 days relative to the run date
6 weeks also 3 months, 1 year
+40d compact form
today
tomorrow
Lookback cut-offs accept an absolute date or 7 days, 2 weeks, 6 months, meaning "published on or after that point". Trustpilot additionally passes its own server-side range (last 30 days, 3, 6 or 12 months), which is filtered on Trustpilot's side and therefore free.
Why the lookback also makes the run cheap
Most review sources page newest-first. With a lookback cut, the walk stops at the first page that is entirely older than the cut — so a daily run reads one or two pages per entity instead of the whole corpus, and pays only for the new reviews.
That is what makes a daily cadence cheaper per signal than a weekly one: you are paying for what changed, not for a re-read. A property with two new complaints a week costs about a cent a month to watch, and the same watch with no date cut costs the price of the entire review history, every single day.
The failure this prevents, in detail
A stale hard-coded date does not throw. It behaves like this:
- The run starts on schedule and succeeds.
- The source refuses a past check-in, or simply has nothing to price.
- Zero priced rows are delivered, so the run costs nothing.
- The status row says so, and nobody reads status rows.
- The dashboard flatlines and everyone assumes the market went quiet.
Nothing in that sequence looks like an error. It is a silent-zero failure, and it is why the status row exists: rows delivered, rows filtered, pages read, and the source's own total, per target, per run. Log it or alert on it, and a stale schedule announces itself the first morning instead of the first quarter.
Scheduling the run itself
On Apify, a schedule is a cron expression attached to an actor or a task; in n8n or Make it is a Schedule trigger in front of an HTTP call. Either way, the three habits that matter are the same:
- Fix the hour. Prices and reviews move on the source's clock. A run that drifts across the day makes a series noisier than the thing it measures.
- Put the entity ids in the input, not a name lookup. Resolve names once, store the ids, and every later run is faster, cheaper and immune to a lookup resolving to a different property.
- Set a spending cap on the run. A source change that suddenly returns ten times the rows should cost you a cap, not a surprise.
Watch the watcher
A schedule that never fails is not the same as a schedule that is still right. Sources change their internal payloads without notice, and the failure mode of a scraper meeting a changed payload is usually zero rows rather than an exception.
The honest answer to that is a daily canary: a small run per actor, on a known target, asserting that rows arrive and carry the fields they should. Anything else is trusting that nothing on the public internet changed overnight.
Run it
Relative dates are supported on the stay side by Google Hotels, Booking prices, Agoda prices, Airbnb prices, Hotel price tracker, Rate parity, Google Flights and the Flight price tracker; and on the lookback side by TripAdvisor, TripAdvisor restaurants, Trustpilot, Indeed, App Store, Google Play, Hostelworld, Kurzurlaub and Zoover. Related: tracking competitor prices with n8n and how not to overpay for scraped rows.
FAQ
What exactly can I write in a date field?
Stay and departure dates accept an absolute YYYY-MM-DD, or a relative value: "30 days", "6 weeks", "3 months", the compact "+40d", plus "today" and "tomorrow". Lookback fields accept an absolute date or a relative one like "7 days", "2 weeks", "6 months", meaning "published on or after that point".
Which fields are relative on which actors?
The stay side — check-in and check-out on Google Hotels, Booking, Agoda and Airbnb; check-in on the hotel trackers and the rate parity checker; departure date on both flight actors. The lookback side — the date cut on TripAdvisor hotel and restaurant reviews, Trustpilot, Indeed, the App Store, Google Play, Hostelworld, Kurzurlaub and Zoover. Trustpilot also takes its own server-side date range.
Do relative dates work in n8n, Make and the API?
Yes — they are ordinary string values in the run input, so anything that can POST JSON can use them. That is the point: the date logic lives in the input, not in a template expression you have to maintain in the orchestrator.
Why did my scheduled hotel run start returning nothing?
The single most likely reason is a hard-coded check-in that is now in the past. Most sources refuse a past check-in outright, so the run succeeds with zero priced rows and costs nothing — which is why it can go unnoticed for weeks. The second most likely reason is a date more than a year out.
Run Google Hotels Prices Scraper on Apify →
Other actors used on this page: apify.com/kestrel/booking-prices-scraper, apify.com/kestrel/flight-price-tracker, apify.com/kestrel/tripadvisor-reviews-scraper, apify.com/kestrel/trustpilot-reviews-scraper, apify.com/kestrel/hotel-rate-parity.