Collects point-in-time stats for an Unsplash account and stores them in SQLite. It also exports CSV files so you can open the data in a spreadsheet immediately.
- Account-level totals per run:
- total photos
- total downloads
- total views
- Per-photo totals per run:
- downloads
- views
- photo metadata (id, slug, description, created_at)
Each run is timestamped (UTC), so you can compute trends and deltas over time.
- Create an Unsplash API app and copy your
Access Key: - Create or update
.envin the project root:
UNSPLASH_ACCESS_KEY=your_access_key_here
UNSPLASH_USERNAME=tfinklea
UNSPLASH_RATE_LIMIT_FRACTION=0.8
UNSPLASH_MIN_REQUEST_INTERVAL_SECONDS=0- Sync the project environment with
uv:
uv sync- Run one collection for your account (
@tfinklea):
uv run --env-file .env python -m unsplash_stats.cli collect --username tfinkleaThe CLI prints progress after every API call, including estimated done percent when available.
This writes:
- SQLite DB:
data/unsplash_stats.sqlite - CSV exports:
exports/user_stats_history.csv,exports/photo_stats_history.csv,exports/photo_latest.csv
Collect snapshot:
uv run --env-file .env python -m unsplash_stats.cli collect --username tfinkleaCollect with limits (useful for testing):
uv run --env-file .env python -m unsplash_stats.cli collect --username tfinklea --max-photos 20 --max-pages 1Collect without CSV export:
uv run --env-file .env python -m unsplash_stats.cli collect --username tfinklea --skip-exportExport CSVs from an existing database:
uv run --env-file .env python -m unsplash_stats.cli export-csv --database data/unsplash_stats.sqlite --export-dir exportsRun the dashboard:
uv run --env-file .env python -m unsplash_stats.dashboardIn the UI, use Collect Now to run a fresh snapshot collection directly from the dashboard
using your current .env credentials and rate-limit settings.
Use the Collection Progress tab to monitor live API call counts and done percentage while the run is active.
Photo previews are cached locally (default: data/photo_cache; override with UNSPLASH_PHOTO_CACHE_DIR).
Startup pre-caches a small set of images (UNSPLASH_DASHBOARD_IMAGE_CACHE_WARM_LIMIT, default 6).
Run regression tests:
uv run --env-file .env python -m unittest discover -s tests -vBuild the image:
docker build -t unsplash-stats:latest .Run on a Docker host (persists DB, exports, and photo cache in named volumes):
docker run -d \
--name unsplash-stats \
--restart unless-stopped \
--env-file .env \
-e UNSPLASH_DATABASE=/app/data/unsplash_stats.sqlite \
-e UNSPLASH_EXPORT_DIR=/app/exports \
-e UNSPLASH_PHOTO_CACHE_DIR=/app/data/photo_cache \
-p 8050:8050 \
-v unsplash_stats_data:/app/data \
-v unsplash_stats_exports:/app/exports \
unsplash-stats:latestThen open: http://localhost:8050
Run with Docker Compose:
docker compose up -d --buildStop Compose deployment:
docker compose downThis repo now includes a Home Assistant add-on package at:
homeassistant-addon/unsplash_stats_dashboard
To add it to Home Assistant Supervisor:
- Add this repository in Settings -> Add-ons -> Add-on Store -> Repositories:
https://github.com/tfinklea/unsplash-stats
- Install Unsplash Stats Dashboard.
- Set add-on config options (minimum required):
unsplash_access_keyunsplash_username
- Start the add-on and open Web UI.
The add-on stores runtime data in /data inside the add-on volume:
/data/unsplash_stats.sqlite/data/exports/data/photo_cache
Use stricter throttling (50% of API limit):
uv run --env-file .env python -m unsplash_stats.cli collect --username tfinklea --rate-limit-fraction 0.5Add extra delay between paginated photo requests:
uv run --env-file .env python -m unsplash_stats.cli collect --username tfinklea --delay-seconds 2- Account totals:
GET /users/:username/statistics - Per-photo stats:
GET /users/:username/photos?stats=true&resolution=days&quantity=30 - Request volume is approximately:
2 + number_of_photo_pages- instead of
2 + number_of_photo_pages + number_of_photos
- Collector auto-throttles using
X-Ratelimit-Limitfrom Unsplash API responses. - Default
UNSPLASH_RATE_LIMIT_FRACTION=0.8means 80% speed:- Demo limit
50/hour-> target40/hour-> one request every90seconds. - Production limit
5000/hour-> target4000/hour-> one request every0.9seconds.
- Demo limit
- On 403/429 rate-limit responses, the collector automatically waits and retries until it can continue.
- Add a hard floor with
UNSPLASH_MIN_REQUEST_INTERVAL_SECONDSif you want to force even slower requests.
Run every 6 hours via cron (safer when throttling heavily):
0 */6 * * * cd /Users/tfinklea/git/unsplash-stats && /usr/bin/env uv run --env-file .env python -m unsplash_stats.cli collect --username tfinklea >> cron.log 2>&1- The collector uses official Unsplash API endpoints (more stable than scraping HTML).
- Per-photo stats are taken from paginated user-photo responses (
stats=true) to keep request count low. - Use
--max-photos/--max-pagesto shorten runs for smoke tests. - Dash UI includes account totals, per-run growth, tracked/new photo trends, movers, momentum/efficiency scatter plots, and per-photo drilldowns.