Run a website task every day with zero AI cost
I build Dejo, and this post uses a feature that shipped in Dejo 0.1.2. Everything below is real output from my own machine.
A lot of browser work is the same small job every day: check a number on a dashboard, see if a status changed, grab this month's invoice. AI agents are good at figuring out how to do these the first time. They're a poor fit for doing them every morning, because every run is a new chat that reads the same pages again, and somebody has to start that chat.
Here's the setup I use instead: let the agent learn the task once, then let the operating system's scheduler run it, with no model at all.
Step 1: let your agent learn the task once
With Dejo added to your agent (setup takes a minute), ask for the job in your own words:
Go to github.com/microsoft/playwright and tell me how many issues are open.
The first time, the agent explores the page and saves the route with dejo_save. That's the only run that costs tokens. If the site needs a sign-in, do it now too: the agent opens a window, you sign in, and the session is kept. See signing in.
dejo sites shows what was saved:
github.com count open issues on microsoft/playwright 2 steps 1 runs health 100%
Step 2: run it from the terminal
dejo run replays the route with no agent and no model:
dejo run "count open issues on microsoft/playwright" --site github.com
dejo github.com (warm run)
task count open issues on microsoft/playwright
+ step 1 open the microsoft/playwright issues page [url] 1560ms
+ step 2 the exact open issue count for microsoft/playwright [css:...] 501ms
done in 2062ms · 2 steps from memory · 1 tool call · 0 model calls
+ the exact open issue count for microsoft/playwright: Open 175 (175)
Two seconds, zero model calls, and the answer on the last line. You don't have to type the task exactly as it was saved; close wording is matched.
Step 3: get output a script can read
For a scheduler you want something a script can parse. --json prints one line:
dejo run "count open issues on microsoft/playwright" --site github.com --json
{"outcome": "done", "ok": true, "site": "github.com", "task": "count open issues on microsoft/playwright", "answers": {"the exact open issue count for microsoft/playwright": "Open\n175\n (175)"}, "saved_files": [], "duration_ms": 1050, "model_calls": 0, "reason": "", "repair": null}
The exit code tells you what happened without reading the JSON at all:
| exit code | outcome | what to do |
|---|---|---|
| 0 | done | nothing; the answer is in answers |
| 1 | a step needs repairing | ask your agent to run the task once; it fixes that one step |
| 2 | no saved route, or the site was rebuilt | let your agent learn it again |
| 3 | the site wants a sign-in | sign in again (dejo login --site ...) |
| 4 | a captcha | open the site yourself and clear it |
| 5 | not on the route's starting page | usually the job is already done |
| 6 | the page answered with an error (404, 403...) | check the address; the page may be gone |
Step 4: schedule it
On Linux or macOS, cron. Cron runs with a very short PATH, so use the full path to dejo (find it with which dejo):
# every day at 9:00, append the result to a log
0 9 * * * /home/you/.local/bin/dejo run "count open issues on microsoft/playwright" --site github.com --json >> /home/you/dejo-daily.log 2>&1
On Windows, put the command in a small script and let Task Scheduler run it:
# C:\scripts\daily-check.ps1
dejo run "count open issues on microsoft/playwright" --site github.com --json |
Add-Content C:\scripts\dejo-daily.log
schtasks /Create /SC DAILY /ST 09:00 /TN "Dejo daily check" /TR "powershell -File C:\scripts\daily-check.ps1"
The run is headless, so no window opens and it doesn't need a screen. Signing in is the only part that needs one, and you did that in step 1.
Step 5: only hear about it when something's wrong
A log is fine for numbers you check now and then. For a job you care about, act on the exit code. A small bash example that writes the answer on success and a warning otherwise:
#!/usr/bin/env bash
out=$(dejo run "count open issues on microsoft/playwright" --site github.com --json)
code=$?
if [ "$code" -eq 0 ]; then
echo "$(date +%F) $(echo "$out" | jq -r '.answers[]' | tr '\n' ' ')" >> ~/issues.log
else
echo "$(date +%F) dejo stopped with code $code: $(echo "$out" | jq -r '.reason')" >> ~/dejo-problems.log
fi
Swap the last echo for whatever reaches you: an email, a Slack webhook, a desktop notification.
Things to know before you rely on it
- The browser profile is shared with your agent. Chrome allows one running browser per profile, so if a scheduled run starts while your agent has a sign-in window open, one of them can't open the profile. Schedule it for a quiet time, or give the scheduled job its own profile with
dejo --profile scheduler ...and sign that profile in once. - Passwords never go into the saved route. If a step fills a password form, put the password in an environment variable (
DEJO_SECRET_<SITE>_<FIELD>) that the scheduled job can see, or in~/.dejo/secrets.json. - Sessions still expire on the site's schedule. When one does, the run exits with 3 and you sign in again. Nothing else changes.
- If the site changes, the run exits with 1 and names the step. Ask your agent to run the task once; it repairs that step, and the scheduled job goes back to costing nothing.
The point of all this is that the expensive part, an AI figuring out a website, happens once. Everything after that is a two-second script. If you want the numbers on what that saves, they're in why your agent re-reads every website.
FAQ
Can an AI agent run a browser task on a schedule?
The agent itself usually can't: it runs when you open a chat. What you can schedule is the task the agent already learned. With Dejo, the agent learns it once, and a scheduler such as cron or Windows Task Scheduler runs `dejo run` every day with no model involved.
Does a scheduled run cost AI tokens?
No. `dejo run` replays the saved route in plain code and reports 0 model calls. Tokens are only spent on the first run, when your agent learns the task, and if a step ever needs repairing.
What happens if the site changes or the login expires?
The run stops and says why, with a distinct exit code: 1 when a step needs repairing, 3 when the site wants a sign-in, 4 for a captcha, 6 when the page itself is gone. Your script can check the code and tell you.