Skip to content

heybeaux/prizm-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PRIZM Code Lookup API

Small Flask API for looking up Canadian PRIZM segments by postal code. It is shaped for a Salesforce Flow that submits up to 10 Canadian postal codes per run.

What Changed

The current PRIZM site no longer needs browser scraping. The React frontend calls public JSON services:

  • Environics geocoder: resolves a postal code to PRIZM segmentation code
  • Supabase REST: returns PRIZM segment profile data

This app now uses those APIs directly instead of Selenium/Chromium, which makes Railway deployment much lighter and avoids the old long-running browser crash mode.

Endpoints

Health

GET /health

Password-protected Dashboard

GET /dashboard

The dashboard shows cache totals, recent daily additions, recent failures, searchable cached postal-code data, CSV export, and a manual weekly-report send button. It uses HTTP Basic Auth.

Recommended dashboard/reporting variables:

DASHBOARD_USERNAME=dena
DASHBOARD_PASSWORD=<set a dashboard password>
WEEKLY_REPORT_RECIPIENTS=dena@example.org
SMTP_HOST=<smtp host>
SMTP_PORT=587
SMTP_USERNAME=<smtp username>
SMTP_PASSWORD=<smtp password>
SMTP_FROM=<sender email>
SMTP_STARTTLS=1

If DASHBOARD_PASSWORD is not set, the dashboard can temporarily use PRIZM_API_KEY as the password unless ALLOW_API_KEY_AS_DASHBOARD_PASSWORD=0 is set.

Single Lookup

GET /api/prizm?postal_code=V8A0A8

Batch Lookup

POST /api/prizm/batch
Content-Type: application/json

{
  "postal_codes": ["V8A0A8", "M5V3L9"]
}

The default batch limit is 10 postal codes. Override with MAX_BATCH_POSTAL_CODES.

All Segments

GET /api/segments

Useful if you want the full segment reference data without postal-code lookup.

Cache Entries and CSV Export

GET /api/cache/entries?status=success&search=V8A&limit=500
GET /api/cache/export.csv

Weekly Report

GET /api/reports/weekly
POST /api/reports/weekly/send

For Railway weekly email delivery, create a cron service/worker that runs:

python send_weekly_report.py

Railway evaluates native cron schedules in UTC; Monday 8am Vancouver is 0 15 * * 1 during PDT.

For Wilderness Committee Salesforce delivery, WCPrizmWeeklyReportEmail can be scheduled instead. It calls /api/reports/weekly with the configured API key/custom label and sends the plain-text report via Salesforce email. The deployed production schedule is 0 0 8 ? * MON (WC PRIZM Weekly Report).

Example Response

{
  "postal_code": "V8A 0A8",
  "prizm_code": "21",
  "segment_number": "21",
  "segment_name": "Scenic Retirement",
  "segment_description": "Older, middle-income suburbanites",
  "average_household_income": "$140,223",
  "education": "High School/College",
  "urbanity": "Suburban",
  "occupation": "Mix",
  "diversity": "Low",
  "family_life": "Couples/Families",
  "tenure": "Own",
  "home_type": "Single Detached/Row",
  "status": "success"
}

Local Development

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py

Then open http://localhost:8080/health.

Railway Deployment

Railway can deploy this repo with either the Dockerfile or the Procfile.

Recommended Railway variables:

PRIZM_API_KEY=<set-this-and-send-it-from-salesforce>
MAX_BATCH_POSTAL_CODES=10
PRIZM_CACHE_DB_PATH=/data/prizm_cache_v2.db
PRIZM_SUCCESS_CACHE_DAYS=3650
PRIZM_INVALID_CACHE_DAYS=90
PRIZM_ERROR_CACHE_DAYS=30
WEB_CONCURRENCY=2
GUNICORN_THREADS=4
GUNICORN_TIMEOUT=60

If you set PRIZM_CACHE_DB_PATH=/data/..., add a Railway volume mounted at /data so cached lookups survive redeploys. The cache is optional; the API works without a persistent volume.

To preload Railway's persistent cache from the existing CSV export, run this in a Railway shell after the volume is mounted:

python cache_cli.py import-csv prizm_import_09112025.csv --replace
python cache_cli.py stats

Successful lookups default to a 10-year cache, invalid postal-code formats default to 90 days, and cacheable not-found/error results default to 30 days. Upstream quota/network failures are not cached.

When PRIZM_API_KEY is set, all /api/* routes require either:

X-API-Key: <key>

or:

Authorization: Bearer <key>

Optional override variables:

PRIZM_GEOCODER_API_URL=https://api.environicsanalytics.com/geocoder-openauth
PRIZM_GEOCODER_COUNTRY=ca
PRIZM_GEOCODER_VINTAGE=2026
PRIZM_SUPABASE_URL=https://rkfddhcgcubrelqdzajw.supabase.co
PRIZM_SUPABASE_KEY=<public publishable key>
PRIZM_UPSTREAM_TIMEOUT=12
CORS_ALLOW_ORIGIN=*

Tests

python3 -m unittest test_prizm_api.py

Notes

  • Selenium files are left in the repo for historical reference, but the Flask API no longer imports or uses Selenium.
  • Rural postal codes are resolved directly from the frontend's Supabase rural_postal_codes table.
  • Urban postal codes still depend on the Environics geocoder API. During triage on June 21, 2026, that endpoint returned 403 Quota Exceeded, so urban postal-code lookups may fail until the public quota replenishes or a licensed/geocoding credential is available.
  • prizm_cache_v2.db, CSV exports, debug screenshots, and node_modules are runtime/local artifacts and are ignored by Docker.
  • The old crash was most likely caused by a globally reused Selenium Chrome session combined with Flask debug mode and repeated screenshot/page-source capture. Removing the browser from request handling eliminates that failure path.

About

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors