crawlr
An AI-powered, self-healing web scraper for e-commerce price & stock monitoring.
Website ·
PyPI ·
pip install crawlr
Crawlr uses an LLM (or a free offline heuristic) only to generate and repair CSS selectors, then extracts pages deterministically with those cached selectors. When a site changes its layout and the selectors break, Crawlr detects it and regenerates them automatically — so your monitoring never silently dies.
- Runs free & offline — no API key required; add an OpenAI/Anthropic key for higher accuracy.
- Self-healing — survives redesigns that break traditional scrapers.
- Zero-config —
crawlr watch <url>auto-detects the right schema (product, jobs, real estate, news). - Price & stock alerts — webhook (signed), Slack, Discord, Teams, Telegram, ntfy, or email, with throttling.
- General-purpose — e-commerce ships in; add new verticals as simple YAML.
Install
pipx install crawlr # or: pip install crawlr
Quickstart
crawlr watch "https://store.com/product/123" --target 25 # schema auto-detected
crawlr watchlist # current price, movement, stock
crawlr monitor --daemon # keep checking in the background
crawlr serve # optional local dashboard
Omit --schema and Crawlr detects the page type for you (product, product list, jobs,
real estate, news). Pass --schema <name> to override.
Deploy with Docker
docker run -p 8000:8000 -v crawlr-data:/data ghcr.io/ardfaiyaz/crawlr # dashboard on :8000
Or run the dashboard and a background monitor together:
docker compose up -d # then open http://localhost:8000
Build locally with docker build -t crawlr .. Data (SQLite DB, selector cache,
snapshots) persists in the /data volume.
Alert triggers
Pick one per watch with --trigger (or in the dashboard):
| Trigger | Alerts when |
|---|---|
any_change |
any watched field changes |
price_drop |
the price goes down |
price_below |
price is at/below your target |
price_above |
price is at/above your target |
back_in_stock |
the item becomes available |
out_of_stock |
the item sells out |
For richer logic, run crawlr init to create an editable crawlr.rules.yaml that maps
circumstances to actions (and overrides per-watch triggers):
default_action: ignore
rules:
- when: price_drops_below
amount: 25
action: alert
- when: back_in_stock
action: alert
Every option, explained
crawlr watch <url> [options] — start tracking a product page or a store's product list.
| Option | What it does |
|---|---|
--target 500 |
Ping me when the price drops to 500 or below (also sets the trigger to price_below) |
--restock |
Ping me when the item goes from out of stock → in stock |
--trigger <name> |
Choose exactly when to be alerted (see the table below) |
--interval 60 |
Check this page every 60 minutes (default: 60) |
--schema <name> |
Force the page type instead of auto-detecting: product, product_list, jobs, real_estate, news |
Triggers (--trigger ...), in plain English:
| Trigger | Alerts you when… |
|---|---|
any_change |
anything you watch changes (price, stock, title…) |
price_drop |
the price goes down by any amount |
price_below |
the price is at or below your --target |
price_above |
the price is at or above your --target |
back_in_stock |
the item becomes available again |
out_of_stock |
the item sells out |
The commands you'll actually use:
| Command | What it does |
|---|---|
crawlr watch <url> |
Start tracking a product or store page |
crawlr watchlist |
Show everything you track: price, change, stock, status |
crawlr monitor |
Check all due pages once, right now |
crawlr monitor --daemon |
Keep checking forever in the background |
crawlr compare <url> <url> … |
One-off price comparison across several links |
crawlr insights <id> |
Price analytics: all-time low/high, average, "deal score" |
crawlr pause <id> / resume <id> |
Temporarily stop / restart tracking one item |
crawlr unwatch <id> |
Stop tracking an item and delete its history |
crawlr serve |
Open the web dashboard at http://localhost:8000 |
crawlr doctor |
Check that your setup works |
crawlr test-alert |
Send a test notification so you know alerts reach you |
The
<id>numbers come fromcrawlr watchlistorcrawlr sites.
Recipes
# Track a competitor's price, alert on a 10%+ drop, check hourly
crawlr watch "https://store.com/p/123" --trigger price_drop --interval 60
# Get pinged when something is back in stock
crawlr watch "https://store.com/p/123" --restock
# Compare the same product across three shops
crawlr compare "https://a.com/p" "https://b.com/p" "https://c.com/p"
# Monitor a job board (schema auto-detected)
crawlr watch "https://boards.example/jobs" --interval 720
# Free phone alerts via ntfy.sh
export CRAWLR_ALERT_NTFY="https://ntfy.sh/my-crawlr-alerts"
crawlr monitor --daemon
# Pause / resume / remove a watch
crawlr pause 3 && crawlr resume 3 && crawlr unwatch 3
# Pipe machine-readable output into other tools
crawlr watchlist --json | jq '.[] | {title, price}'
Custom schemas (no code)
Add new verticals by dropping a YAML file into your schema directory:
name: jobs
item_selector: ".job-card"
fields:
- name: title
description: the job title
type: text
required: true
- name: salary
description: annual salary
type: number
Then crawlr scrape <url> --schema jobs. List schemas with crawlr schemas.
Configuration
Set via environment variables or a .env file:
| Variable | Default | Description |
|---|---|---|
CRAWLR_LLM_PROVIDER |
none |
openai, anthropic, or none (offline heuristic) |
CRAWLR_LLM_API_KEY |
— | API key for the chosen provider |
CRAWLR_DATABASE_URL |
— | postgresql://... to use Postgres instead of SQLite |
CRAWLR_ALERT_WEBHOOK |
— | Webhook URL for change alerts |
CRAWLR_ALERT_SLACK |
— | Slack incoming-webhook URL |
CRAWLR_ALERT_DISCORD |
— | Discord incoming-webhook URL |
CRAWLR_ALERT_TEAMS |
— | Microsoft Teams incoming-webhook URL |
CRAWLR_ALERT_NTFY |
— | ntfy.sh (or self-hosted) topic URL |
CRAWLR_ALERT_TELEGRAM_TOKEN |
— | Telegram bot token (from @BotFather) |
CRAWLR_ALERT_TELEGRAM_CHAT_ID |
— | Telegram chat id to send alerts to |
CRAWLR_WEBHOOK_SECRET |
— | HMAC-signs generic webhook payloads |
CRAWLR_ALERT_THROTTLE_MINUTES |
0 |
Suppress repeat alerts within N minutes |
CRAWLR_PROXIES |
— | Comma-separated proxy URLs to rotate |
CRAWLR_RESPECT_ROBOTS |
true |
Honor robots.txt |
CRAWLR_FX_BASE |
USD |
Currency that compare converts prices into |
CRAWLR_FX_LIVE |
false |
Fetch live FX rates (cached) instead of the pinned table |
CRAWLR_FX_API_URL |
open.er-api.com |
Live FX endpoint ({"rates": {CODE: perUSD}}) |
CRAWLR_FX_RATES |
— | Pinned rate overrides, e.g. EUR=0.92,GBP=0.79 |
Examples
Ready-to-run recipes live in examples/ — watch competitors, use a
custom schema, and call the hosted API from Python.
Development
pip install -e '.[dev]'
pytest # offline test suite
ruff check . # lint
crawlr eval # extraction accuracy gate
Contributing
Contributions are welcome — see CONTRIBUTING.md. Great first contributions: new vertical presets, golden eval cases for sites that extract wrong, and docs/examples.
Demo
A short GIF is the best intro. Generate one with VHS:
vhs docs/demo.tape → docs/demo.gif (see docs/DEMO.md).
Does it work on big marketplaces (Lazada, Shopee, Amazon)?
Short answer: it can, but they're the hardest sites to scrape — expect extra setup, and don't count on 100% reliability.
Crawlr shines on sites with clean HTML or embedded product data (schema.org JSON-LD). Big marketplaces are deliberately hostile to bots, in three ways — here's how to give Crawlr its best shot:
-
They render prices with JavaScript. The price often isn't in the raw HTML; it loads after the page runs its scripts. Install the browser engine so Crawlr can render it:
pip install "crawlr[js]" playwright install chromium
Then pass
--js(e.g.crawlr watch "<url>" --js) or let Crawlr auto-escalate when it detects a JS-only shell. -
They have anti-bot protection (Cloudflare, CAPTCHAs, rate limits). Crawlr detects when it's blocked and skips that run instead of saving a bogus price — but getting through reliably usually needs rotating proxies:
export CRAWLR_PROXIES="http://user:pass@host:port,http://host2:port"
-
robots.txt — many marketplaces disallow scraping these pages, and Crawlr honors
robots.txtby default, so it may refuse to fetch. Only override this for sites you're permitted to scrape (see Legal & responsible use below):export CRAWLR_RESPECT_ROBOTS=false
Rule of thumb:
| Site type | How well it works |
|---|---|
| Independent shops, Shopify / WooCommerce, anything with schema.org data | ✅ Works well out of the box |
| A marketplace's official API (if it offers one) | ✅ Most reliable — prefer this |
| Lazada / Shopee / Amazon product pages | ⚠️ Possible with crawlr[js] + proxies; may still get blocked |
| Pages behind a login, or a CAPTCHA on every request | ❌ Not a good fit |
Tip: prove the whole flow works on an easy site first, then point it at the hard one:
crawlr watch "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html" --target 60
crawlr watchlist
Legal & responsible use
Crawlr is a general-purpose scraping tool, and you are responsible for how you use it. Before monitoring any site, make sure you comply with:
- the target site's Terms of Service and
robots.txt— Crawlr honorsrobots.txtby default, so keepCRAWLR_RESPECT_ROBOTS=trueunless you own the site; - applicable laws such as the CFAA (US) and GDPR/CCPA whenever personal data is involved — prefer public, non-personal data and avoid collecting PII;
- reasonable rate limits — use sensible intervals and delays so you don't overload a site.
Crawlr is meant for legitimate uses such as tracking your own listings, price research, and public-data monitoring. Please don't use it to bypass authentication, paywalls, or anti-abuse protections. The maintainers provide the software "as is" and are not responsible for how it is used (see LICENSE).
License
Crawlr is released under the MIT License — free to use, modify, and distribute, including commercially. See LICENSE.
Building a commercial or hosted product on top of Crawlr? An open-core model works well: keep the CLI and library MIT-licensed (this repo) and offer any hosted service, team features, or premium connectors as a separate paid layer.
Metadata
Release files for crawlr 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| crawlr-0.3.1.tar.gz | 71.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| crawlr-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 159.2 kB
Release files / crawlr-0.3.1.tar.gz
| Download URL | crawlr-0.3.1.tar.gz |
|---|---|
| Size | 71.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a9f4bbaa419edfaabe0e266685fc2250d22e94238c37ae4ff42a60ac44699f77
|
|
BLAKE2b-256 checksum How to use checksums |
d072aacadd0d66f56ab14dbd38c325a519505c869dcec7f9f055b8b8e284e70a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 20, 2026.
Transparency logRelease files / crawlr-0.3.1-py3-none-any.whl
| Download URL | crawlr-0.3.1-py3-none-any.whl |
|---|---|
| Size | 87.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d746aad2e886035948c7f64a5325319629734eee55592d6a129a318942073203
|
|
BLAKE2b-256 checksum How to use checksums |
b16506f3ba869224961faa8844b8bc126cd0283c142120d521e5f9d2dd87d6b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 20, 2026.
Transparency log