Skip to content

Repository files navigation

caero


Caero (zero), a self-hosted price tracker to help you catch the best deals.

caero dashboard

Features

  • Track Anything – Works on any website using URL + CSS selectors, with built-in fallbacks (ld+json, itemprop, data-price) and per-site default selectors.
  • Smart Insights – Historical price charts, all-time lows/highs, time-weighted averages, and per-product currency detection.
  • Flexible Recording – By default a price is stored only when it changes; flip "Record every check" per product to store every data point. Old history can be auto-thinned to daily min/max.
  • Instant Alerts – Get notified via Telegram, Email, ntfy, Gotify, or Discord when a price drops below a threshold, drops by a percentage, or changes at all. Plus notifications for broken selectors (and their recovery), URL redirects, and currency changes.
  • Multi-User – Admin-managed accounts, per-user products and notification defaults, optional open registration.
  • Self-Maintaining – Daily JSON backups with rotation, health endpoint for container monitoring, Alembic migrations run automatically on startup.

Quick Start (Docker)

The fastest way to get started is with Docker.

  1. Set up your settings:
    cp .env.example .env
    At minimum set a random SECRET_KEY and your TZ. If SECRET_KEY stays at the default, Caero generates a random one per start — secure, but logins won't survive restarts.
  2. Launch the app:
    docker compose up -d
  3. Start tracking: Open http://localhost:8000 in your browser. The first account you register becomes the admin.

Pull the latest image directly:docker pull ghcr.io/13/caero:latest


How to Track a Product

Don't let "CSS Selectors" scare you—it's just a way to point caero to the price.

  1. Find the Price: Go to your product page (e.g., Amazon, BestBuy).
  2. Copy the Path: Right-click the price on the page → Inspect. In the window that opens, right-click the highlighted code → CopyCopy selector.
  3. Paste & Save: Paste that into caero along with the URL. For well-known shops a default selector is filled in automatically.

Configuration

Everything is configured through .env — see .env.example for the full annotated list. Highlights:

VariablePurpose
SECRET_KEYJWT signing key — set a long random string
TZTimezone for the daily check times (10:00 means your 10:00)
TELEGRAM_BOT_TOKEN / SMTP_*Per-alert notification channels (Telegram token can also be set in the admin UI)
NTFY_URL / GOTIFY_* / DISCORD_WEBHOOK_URLBroadcast webhooks — every notification from all users goes to each configured channel (household-style setups)
BACKUP_KEEPDaily JSON backups to keep in /data/backups (0 = off)
PRICE_HISTORY_THIN_AFTER_DAYSAuto-thin old price rows to daily min/max (0 = keep everything)
SINGLE_USER_MODESkip login entirely for single-person setups

Backups & Health

  • A JSON backup of all data is written daily to /data/backups and can be restored via Settings → Admin → Import all data.
  • GET /api/health answers without authentication; the Docker image ships a matching HEALTHCHECK.

Development

Backend (FastAPI + SQLAlchemy, Python 3.13+, uv):

cd backend
uv sync
uv run fastapi dev app/main.py # http://localhost:8000
uv run pytest # tests
uv run ruff check app tests # lint

Frontend (React + Vite + Tailwind):

cd frontend
npm install
npm run dev # dev server, proxies /api to :8000
npm test# unit tests (vitest)
npm run lint # eslint

CI runs lint, unit tests, a production build, and a Playwright end-to-end suite on every push.

About

zero price tracker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - 13/caero: zero price tracker · GitHub
Skip to content

Repository files navigation

caero


Caero (zero), a self-hosted price tracker to help you catch the best deals.

caero dashboard

Features

  • Track Anything – Works on any website using URL + CSS selectors, with built-in fallbacks (ld+json, itemprop, data-price) and per-site default selectors.
  • Smart Insights – Historical price charts, all-time lows/highs, time-weighted averages, and per-product currency detection.
  • Flexible Recording – By default a price is stored only when it changes; flip "Record every check" per product to store every data point. Old history can be auto-thinned to daily min/max.
  • Instant Alerts – Get notified via Telegram, Email, ntfy, Gotify, or Discord when a price drops below a threshold, drops by a percentage, or changes at all. Plus notifications for broken selectors (and their recovery), URL redirects, and currency changes.
  • Multi-User – Admin-managed accounts, per-user products and notification defaults, optional open registration.
  • Self-Maintaining – Daily JSON backups with rotation, health endpoint for container monitoring, Alembic migrations run automatically on startup.

Quick Start (Docker)

The fastest way to get started is with Docker.

  1. Set up your settings:
    cp .env.example .env
    At minimum set a random SECRET_KEY and your TZ. If SECRET_KEY stays at the default, Caero generates a random one per start — secure, but logins won't survive restarts.
  2. Launch the app:
    docker compose up -d
  3. Start tracking: Open http://localhost:8000 in your browser. The first account you register becomes the admin.

Pull the latest image directly:docker pull ghcr.io/13/caero:latest


How to Track a Product

Don't let "CSS Selectors" scare you—it's just a way to point caero to the price.

  1. Find the Price: Go to your product page (e.g., Amazon, BestBuy).
  2. Copy the Path: Right-click the price on the page → Inspect. In the window that opens, right-click the highlighted code → CopyCopy selector.
  3. Paste & Save: Paste that into caero along with the URL. For well-known shops a default selector is filled in automatically.

Configuration

Everything is configured through .env — see .env.example for the full annotated list. Highlights:

VariablePurpose
SECRET_KEYJWT signing key — set a long random string
TZTimezone for the daily check times (10:00 means your 10:00)
TELEGRAM_BOT_TOKEN / SMTP_*Per-alert notification channels (Telegram token can also be set in the admin UI)
NTFY_URL / GOTIFY_* / DISCORD_WEBHOOK_URLBroadcast webhooks — every notification from all users goes to each configured channel (household-style setups)
BACKUP_KEEPDaily JSON backups to keep in /data/backups (0 = off)
PRICE_HISTORY_THIN_AFTER_DAYSAuto-thin old price rows to daily min/max (0 = keep everything)
SINGLE_USER_MODESkip login entirely for single-person setups

Backups & Health

  • A JSON backup of all data is written daily to /data/backups and can be restored via Settings → Admin → Import all data.
  • GET /api/health answers without authentication; the Docker image ships a matching HEALTHCHECK.

Development

Backend (FastAPI + SQLAlchemy, Python 3.13+, uv):

cd backend
uv sync
uv run fastapi dev app/main.py # http://localhost:8000
uv run pytest # tests
uv run ruff check app tests # lint

Frontend (React + Vite + Tailwind):

cd frontend
npm install
npm run dev # dev server, proxies /api to :8000
npm test# unit tests (vitest)
npm run lint # eslint

CI runs lint, unit tests, a production build, and a Playwright end-to-end suite on every push.

About

zero price tracker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - 13/caero: zero price tracker · GitHub
Skip to content

Repository files navigation

caero


Caero (zero), a self-hosted price tracker to help you catch the best deals.

caero dashboard

Features

  • Track Anything – Works on any website using URL + CSS selectors, with built-in fallbacks (ld+json, itemprop, data-price) and per-site default selectors.
  • Smart Insights – Historical price charts, all-time lows/highs, time-weighted averages, and per-product currency detection.
  • Flexible Recording – By default a price is stored only when it changes; flip "Record every check" per product to store every data point. Old history can be auto-thinned to daily min/max.
  • Instant Alerts – Get notified via Telegram, Email, ntfy, Gotify, or Discord when a price drops below a threshold, drops by a percentage, or changes at all. Plus notifications for broken selectors (and their recovery), URL redirects, and currency changes.
  • Multi-User – Admin-managed accounts, per-user products and notification defaults, optional open registration.
  • Self-Maintaining – Daily JSON backups with rotation, health endpoint for container monitoring, Alembic migrations run automatically on startup.

Quick Start (Docker)

The fastest way to get started is with Docker.

  1. Set up your settings:
    cp .env.example .env
    At minimum set a random SECRET_KEY and your TZ. If SECRET_KEY stays at the default, Caero generates a random one per start — secure, but logins won't survive restarts.
  2. Launch the app:
    docker compose up -d
  3. Start tracking: Open http://localhost:8000 in your browser. The first account you register becomes the admin.

Pull the latest image directly:docker pull ghcr.io/13/caero:latest


How to Track a Product

Don't let "CSS Selectors" scare you—it's just a way to point caero to the price.

  1. Find the Price: Go to your product page (e.g., Amazon, BestBuy).
  2. Copy the Path: Right-click the price on the page → Inspect. In the window that opens, right-click the highlighted code → CopyCopy selector.
  3. Paste & Save: Paste that into caero along with the URL. For well-known shops a default selector is filled in automatically.

Configuration

Everything is configured through .env — see .env.example for the full annotated list. Highlights:

VariablePurpose
SECRET_KEYJWT signing key — set a long random string
TZTimezone for the daily check times (10:00 means your 10:00)
TELEGRAM_BOT_TOKEN / SMTP_*Per-alert notification channels (Telegram token can also be set in the admin UI)
NTFY_URL / GOTIFY_* / DISCORD_WEBHOOK_URLBroadcast webhooks — every notification from all users goes to each configured channel (household-style setups)
BACKUP_KEEPDaily JSON backups to keep in /data/backups (0 = off)
PRICE_HISTORY_THIN_AFTER_DAYSAuto-thin old price rows to daily min/max (0 = keep everything)
SINGLE_USER_MODESkip login entirely for single-person setups

Backups & Health

  • A JSON backup of all data is written daily to /data/backups and can be restored via Settings → Admin → Import all data.
  • GET /api/health answers without authentication; the Docker image ships a matching HEALTHCHECK.

Development

Backend (FastAPI + SQLAlchemy, Python 3.13+, uv):

cd backend
uv sync
uv run fastapi dev app/main.py # http://localhost:8000
uv run pytest # tests
uv run ruff check app tests # lint

Frontend (React + Vite + Tailwind):

cd frontend
npm install
npm run dev # dev server, proxies /api to :8000
npm test# unit tests (vitest)
npm run lint # eslint

CI runs lint, unit tests, a production build, and a Playwright end-to-end suite on every push.

About

zero price tracker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - 13/caero: zero price tracker · GitHub
Skip to content

Repository files navigation

caero


Caero (zero), a self-hosted price tracker to help you catch the best deals.

caero dashboard

Features

  • Track Anything – Works on any website using URL + CSS selectors, with built-in fallbacks (ld+json, itemprop, data-price) and per-site default selectors.
  • Smart Insights – Historical price charts, all-time lows/highs, time-weighted averages, and per-product currency detection.
  • Flexible Recording – By default a price is stored only when it changes; flip "Record every check" per product to store every data point. Old history can be auto-thinned to daily min/max.
  • Instant Alerts – Get notified via Telegram, Email, ntfy, Gotify, or Discord when a price drops below a threshold, drops by a percentage, or changes at all. Plus notifications for broken selectors (and their recovery), URL redirects, and currency changes.
  • Multi-User – Admin-managed accounts, per-user products and notification defaults, optional open registration.
  • Self-Maintaining – Daily JSON backups with rotation, health endpoint for container monitoring, Alembic migrations run automatically on startup.

Quick Start (Docker)

The fastest way to get started is with Docker.

  1. Set up your settings:
    cp .env.example .env
    At minimum set a random SECRET_KEY and your TZ. If SECRET_KEY stays at the default, Caero generates a random one per start — secure, but logins won't survive restarts.
  2. Launch the app:
    docker compose up -d
  3. Start tracking: Open http://localhost:8000 in your browser. The first account you register becomes the admin.

Pull the latest image directly:docker pull ghcr.io/13/caero:latest


How to Track a Product

Don't let "CSS Selectors" scare you—it's just a way to point caero to the price.

  1. Find the Price: Go to your product page (e.g., Amazon, BestBuy).
  2. Copy the Path: Right-click the price on the page → Inspect. In the window that opens, right-click the highlighted code → CopyCopy selector.
  3. Paste & Save: Paste that into caero along with the URL. For well-known shops a default selector is filled in automatically.

Configuration

Everything is configured through .env — see .env.example for the full annotated list. Highlights:

VariablePurpose
SECRET_KEYJWT signing key — set a long random string
TZTimezone for the daily check times (10:00 means your 10:00)
TELEGRAM_BOT_TOKEN / SMTP_*Per-alert notification channels (Telegram token can also be set in the admin UI)
NTFY_URL / GOTIFY_* / DISCORD_WEBHOOK_URLBroadcast webhooks — every notification from all users goes to each configured channel (household-style setups)
BACKUP_KEEPDaily JSON backups to keep in /data/backups (0 = off)
PRICE_HISTORY_THIN_AFTER_DAYSAuto-thin old price rows to daily min/max (0 = keep everything)
SINGLE_USER_MODESkip login entirely for single-person setups

Backups & Health

  • A JSON backup of all data is written daily to /data/backups and can be restored via Settings → Admin → Import all data.
  • GET /api/health answers without authentication; the Docker image ships a matching HEALTHCHECK.

Development

Backend (FastAPI + SQLAlchemy, Python 3.13+, uv):

cd backend
uv sync
uv run fastapi dev app/main.py # http://localhost:8000
uv run pytest # tests
uv run ruff check app tests # lint

Frontend (React + Vite + Tailwind):

cd frontend
npm install
npm run dev # dev server, proxies /api to :8000
npm test# unit tests (vitest)
npm run lint # eslint

CI runs lint, unit tests, a production build, and a Playwright end-to-end suite on every push.

About

zero price tracker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - 13/caero: zero price tracker · GitHub
Skip to content

Repository files navigation

caero


Caero (zero), a self-hosted price tracker to help you catch the best deals.

caero dashboard

Features

  • Track Anything – Works on any website using URL + CSS selectors, with built-in fallbacks (ld+json, itemprop, data-price) and per-site default selectors.
  • Smart Insights – Historical price charts, all-time lows/highs, time-weighted averages, and per-product currency detection.
  • Flexible Recording – By default a price is stored only when it changes; flip "Record every check" per product to store every data point. Old history can be auto-thinned to daily min/max.
  • Instant Alerts – Get notified via Telegram, Email, ntfy, Gotify, or Discord when a price drops below a threshold, drops by a percentage, or changes at all. Plus notifications for broken selectors (and their recovery), URL redirects, and currency changes.
  • Multi-User – Admin-managed accounts, per-user products and notification defaults, optional open registration.
  • Self-Maintaining – Daily JSON backups with rotation, health endpoint for container monitoring, Alembic migrations run automatically on startup.

Quick Start (Docker)

The fastest way to get started is with Docker.

  1. Set up your settings:
    cp .env.example .env
    At minimum set a random SECRET_KEY and your TZ. If SECRET_KEY stays at the default, Caero generates a random one per start — secure, but logins won't survive restarts.
  2. Launch the app:
    docker compose up -d
  3. Start tracking: Open http://localhost:8000 in your browser. The first account you register becomes the admin.

Pull the latest image directly:docker pull ghcr.io/13/caero:latest


How to Track a Product

Don't let "CSS Selectors" scare you—it's just a way to point caero to the price.

  1. Find the Price: Go to your product page (e.g., Amazon, BestBuy).
  2. Copy the Path: Right-click the price on the page → Inspect. In the window that opens, right-click the highlighted code → CopyCopy selector.
  3. Paste & Save: Paste that into caero along with the URL. For well-known shops a default selector is filled in automatically.

Configuration

Everything is configured through .env — see .env.example for the full annotated list. Highlights:

VariablePurpose
SECRET_KEYJWT signing key — set a long random string
TZTimezone for the daily check times (10:00 means your 10:00)
TELEGRAM_BOT_TOKEN / SMTP_*Per-alert notification channels (Telegram token can also be set in the admin UI)
NTFY_URL / GOTIFY_* / DISCORD_WEBHOOK_URLBroadcast webhooks — every notification from all users goes to each configured channel (household-style setups)
BACKUP_KEEPDaily JSON backups to keep in /data/backups (0 = off)
PRICE_HISTORY_THIN_AFTER_DAYSAuto-thin old price rows to daily min/max (0 = keep everything)
SINGLE_USER_MODESkip login entirely for single-person setups

Backups & Health

  • A JSON backup of all data is written daily to /data/backups and can be restored via Settings → Admin → Import all data.
  • GET /api/health answers without authentication; the Docker image ships a matching HEALTHCHECK.

Development

Backend (FastAPI + SQLAlchemy, Python 3.13+, uv):

cd backend
uv sync
uv run fastapi dev app/main.py # http://localhost:8000
uv run pytest # tests
uv run ruff check app tests # lint

Frontend (React + Vite + Tailwind):

cd frontend
npm install
npm run dev # dev server, proxies /api to :8000
npm test# unit tests (vitest)
npm run lint # eslint

CI runs lint, unit tests, a production build, and a Playwright end-to-end suite on every push.

About

zero price tracker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - 13/caero: zero price tracker · GitHub
Skip to content

Repository files navigation

caero


Caero (zero), a self-hosted price tracker to help you catch the best deals.

caero dashboard

Features

  • Track Anything – Works on any website using URL + CSS selectors, with built-in fallbacks (ld+json, itemprop, data-price) and per-site default selectors.
  • Smart Insights – Historical price charts, all-time lows/highs, time-weighted averages, and per-product currency detection.
  • Flexible Recording – By default a price is stored only when it changes; flip "Record every check" per product to store every data point. Old history can be auto-thinned to daily min/max.
  • Instant Alerts – Get notified via Telegram, Email, ntfy, Gotify, or Discord when a price drops below a threshold, drops by a percentage, or changes at all. Plus notifications for broken selectors (and their recovery), URL redirects, and currency changes.
  • Multi-User – Admin-managed accounts, per-user products and notification defaults, optional open registration.
  • Self-Maintaining – Daily JSON backups with rotation, health endpoint for container monitoring, Alembic migrations run automatically on startup.

Quick Start (Docker)

The fastest way to get started is with Docker.

  1. Set up your settings:
    cp .env.example .env
    At minimum set a random SECRET_KEY and your TZ. If SECRET_KEY stays at the default, Caero generates a random one per start — secure, but logins won't survive restarts.
  2. Launch the app:
    docker compose up -d
  3. Start tracking: Open http://localhost:8000 in your browser. The first account you register becomes the admin.

Pull the latest image directly:docker pull ghcr.io/13/caero:latest


How to Track a Product

Don't let "CSS Selectors" scare you—it's just a way to point caero to the price.

  1. Find the Price: Go to your product page (e.g., Amazon, BestBuy).
  2. Copy the Path: Right-click the price on the page → Inspect. In the window that opens, right-click the highlighted code → CopyCopy selector.
  3. Paste & Save: Paste that into caero along with the URL. For well-known shops a default selector is filled in automatically.

Configuration

Everything is configured through .env — see .env.example for the full annotated list. Highlights:

VariablePurpose
SECRET_KEYJWT signing key — set a long random string
TZTimezone for the daily check times (10:00 means your 10:00)
TELEGRAM_BOT_TOKEN / SMTP_*Per-alert notification channels (Telegram token can also be set in the admin UI)
NTFY_URL / GOTIFY_* / DISCORD_WEBHOOK_URLBroadcast webhooks — every notification from all users goes to each configured channel (household-style setups)
BACKUP_KEEPDaily JSON backups to keep in /data/backups (0 = off)
PRICE_HISTORY_THIN_AFTER_DAYSAuto-thin old price rows to daily min/max (0 = keep everything)
SINGLE_USER_MODESkip login entirely for single-person setups

Backups & Health

  • A JSON backup of all data is written daily to /data/backups and can be restored via Settings → Admin → Import all data.
  • GET /api/health answers without authentication; the Docker image ships a matching HEALTHCHECK.

Development

Backend (FastAPI + SQLAlchemy, Python 3.13+, uv):

cd backend
uv sync
uv run fastapi dev app/main.py # http://localhost:8000
uv run pytest # tests
uv run ruff check app tests # lint

Frontend (React + Vite + Tailwind):

cd frontend
npm install
npm run dev # dev server, proxies /api to :8000
npm test# unit tests (vitest)
npm run lint # eslint

CI runs lint, unit tests, a production build, and a Playwright end-to-end suite on every push.

About

zero price tracker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - 13/caero: zero price tracker · GitHub
Skip to content

Repository files navigation

caero


Caero (zero), a self-hosted price tracker to help you catch the best deals.

caero dashboard

Features

  • Track Anything – Works on any website using URL + CSS selectors, with built-in fallbacks (ld+json, itemprop, data-price) and per-site default selectors.
  • Smart Insights – Historical price charts, all-time lows/highs, time-weighted averages, and per-product currency detection.
  • Flexible Recording – By default a price is stored only when it changes; flip "Record every check" per product to store every data point. Old history can be auto-thinned to daily min/max.
  • Instant Alerts – Get notified via Telegram, Email, ntfy, Gotify, or Discord when a price drops below a threshold, drops by a percentage, or changes at all. Plus notifications for broken selectors (and their recovery), URL redirects, and currency changes.
  • Multi-User – Admin-managed accounts, per-user products and notification defaults, optional open registration.
  • Self-Maintaining – Daily JSON backups with rotation, health endpoint for container monitoring, Alembic migrations run automatically on startup.

Quick Start (Docker)

The fastest way to get started is with Docker.

  1. Set up your settings:
    cp .env.example .env
    At minimum set a random SECRET_KEY and your TZ. If SECRET_KEY stays at the default, Caero generates a random one per start — secure, but logins won't survive restarts.
  2. Launch the app:
    docker compose up -d
  3. Start tracking: Open http://localhost:8000 in your browser. The first account you register becomes the admin.

Pull the latest image directly:docker pull ghcr.io/13/caero:latest


How to Track a Product

Don't let "CSS Selectors" scare you—it's just a way to point caero to the price.

  1. Find the Price: Go to your product page (e.g., Amazon, BestBuy).
  2. Copy the Path: Right-click the price on the page → Inspect. In the window that opens, right-click the highlighted code → CopyCopy selector.
  3. Paste & Save: Paste that into caero along with the URL. For well-known shops a default selector is filled in automatically.

Configuration

Everything is configured through .env — see .env.example for the full annotated list. Highlights:

VariablePurpose
SECRET_KEYJWT signing key — set a long random string
TZTimezone for the daily check times (10:00 means your 10:00)
TELEGRAM_BOT_TOKEN / SMTP_*Per-alert notification channels (Telegram token can also be set in the admin UI)
NTFY_URL / GOTIFY_* / DISCORD_WEBHOOK_URLBroadcast webhooks — every notification from all users goes to each configured channel (household-style setups)
BACKUP_KEEPDaily JSON backups to keep in /data/backups (0 = off)
PRICE_HISTORY_THIN_AFTER_DAYSAuto-thin old price rows to daily min/max (0 = keep everything)
SINGLE_USER_MODESkip login entirely for single-person setups

Backups & Health

  • A JSON backup of all data is written daily to /data/backups and can be restored via Settings → Admin → Import all data.
  • GET /api/health answers without authentication; the Docker image ships a matching HEALTHCHECK.

Development

Backend (FastAPI + SQLAlchemy, Python 3.13+, uv):

cd backend
uv sync
uv run fastapi dev app/main.py # http://localhost:8000
uv run pytest # tests
uv run ruff check app tests # lint

Frontend (React + Vite + Tailwind):

cd frontend
npm install
npm run dev # dev server, proxies /api to :8000
npm test# unit tests (vitest)
npm run lint # eslint

CI runs lint, unit tests, a production build, and a Playwright end-to-end suite on every push.

About

zero price tracker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - 13/caero: zero price tracker · GitHub
Skip to content

Repository files navigation

caero


Caero (zero), a self-hosted price tracker to help you catch the best deals.

caero dashboard

Features

  • Track Anything – Works on any website using URL + CSS selectors, with built-in fallbacks (ld+json, itemprop, data-price) and per-site default selectors.
  • Smart Insights – Historical price charts, all-time lows/highs, time-weighted averages, and per-product currency detection.
  • Flexible Recording – By default a price is stored only when it changes; flip "Record every check" per product to store every data point. Old history can be auto-thinned to daily min/max.
  • Instant Alerts – Get notified via Telegram, Email, ntfy, Gotify, or Discord when a price drops below a threshold, drops by a percentage, or changes at all. Plus notifications for broken selectors (and their recovery), URL redirects, and currency changes.
  • Multi-User – Admin-managed accounts, per-user products and notification defaults, optional open registration.
  • Self-Maintaining – Daily JSON backups with rotation, health endpoint for container monitoring, Alembic migrations run automatically on startup.

Quick Start (Docker)

The fastest way to get started is with Docker.

  1. Set up your settings:
    cp .env.example .env
    At minimum set a random SECRET_KEY and your TZ. If SECRET_KEY stays at the default, Caero generates a random one per start — secure, but logins won't survive restarts.
  2. Launch the app:
    docker compose up -d
  3. Start tracking: Open http://localhost:8000 in your browser. The first account you register becomes the admin.

Pull the latest image directly:docker pull ghcr.io/13/caero:latest


How to Track a Product

Don't let "CSS Selectors" scare you—it's just a way to point caero to the price.

  1. Find the Price: Go to your product page (e.g., Amazon, BestBuy).
  2. Copy the Path: Right-click the price on the page → Inspect. In the window that opens, right-click the highlighted code → CopyCopy selector.
  3. Paste & Save: Paste that into caero along with the URL. For well-known shops a default selector is filled in automatically.

Configuration

Everything is configured through .env — see .env.example for the full annotated list. Highlights:

VariablePurpose
SECRET_KEYJWT signing key — set a long random string
TZTimezone for the daily check times (10:00 means your 10:00)
TELEGRAM_BOT_TOKEN / SMTP_*Per-alert notification channels (Telegram token can also be set in the admin UI)
NTFY_URL / GOTIFY_* / DISCORD_WEBHOOK_URLBroadcast webhooks — every notification from all users goes to each configured channel (household-style setups)
BACKUP_KEEPDaily JSON backups to keep in /data/backups (0 = off)
PRICE_HISTORY_THIN_AFTER_DAYSAuto-thin old price rows to daily min/max (0 = keep everything)
SINGLE_USER_MODESkip login entirely for single-person setups

Backups & Health

  • A JSON backup of all data is written daily to /data/backups and can be restored via Settings → Admin → Import all data.
  • GET /api/health answers without authentication; the Docker image ships a matching HEALTHCHECK.

Development

Backend (FastAPI + SQLAlchemy, Python 3.13+, uv):

cd backend
uv sync
uv run fastapi dev app/main.py # http://localhost:8000
uv run pytest # tests
uv run ruff check app tests # lint

Frontend (React + Vite + Tailwind):

cd frontend
npm install
npm run dev # dev server, proxies /api to :8000
npm test# unit tests (vitest)
npm run lint # eslint

CI runs lint, unit tests, a production build, and a Playwright end-to-end suite on every push.

About

zero price tracker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages