CyberMax commited on
Commit
ff451cf
·
verified ·
1 Parent(s): 39dc93d

Initial free demo (CyberMax)

Browse files
Files changed (2) hide show
  1. README.md +24 -5
  2. index.html +99 -17
README.md CHANGED
@@ -1,10 +1,29 @@
1
  ---
2
- title: Zip Code Lookup
3
- emoji: 🏢
4
- colorFrom: yellow
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
1
  ---
2
+ title: "Ziplore: US ZIP Code Lookup & Radius Search"
3
+ emoji: 📍
4
+ colorFrom: green
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ license: cc-by-4.0
9
+ short_description: Free ZIP lookup with county, time zone & income
10
+ tags:
11
+ - zip-codes
12
+ - geocoding
13
+ - census
14
+ - demographics
15
+ - radius-search
16
+ - real-estate
17
+ - agents
18
+ - apify
19
+ datasets:
20
+ - shaw276/us-zip-codes-demographics
21
  ---
22
 
23
+ # Ziplore: free US ZIP code lookup & radius search
24
+
25
+ Type a ZIP code (or "lat,lon") and a radius: get city, county, time zone, local time, population, median household income, home value, rent and age for that ZIP and every ZIP around it. All 41,682 US ZIPs load in your browser; nothing is sent anywhere.
26
+
27
+ **Whole lists, workflows and AI agents:** [Ziplore on the Apify Store](https://apify.com/cybermax/zip-data-api) takes thousands of ZIPs, addresses, "City, ST" names or coordinates and returns JSON with county FIPS, UTC offsets and nearby ZIPs; $0.0005 per ZIP, and it's an MCP tool for Claude and Cursor. Bulk CSV: [shaw276/us-zip-codes-demographics](https://huggingface.co/datasets/shaw276/us-zip-codes-demographics).
28
+
29
+ Data: GeoNames (CC BY 4.0), US Census Bureau (2024 Gazetteer, ACS 2020–2024 5-year), timezone-boundary-builder (ODbL, © OpenStreetMap contributors). Made by CyberMax.
index.html CHANGED
@@ -1,19 +1,101 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Ziplore: free US ZIP code lookup & radius search</title>
7
+ <meta name="description" content="Free US ZIP code lookup: city, county, time zone, population, income and home value for any ZIP, plus every ZIP within N miles.">
8
+ <style>
9
+ :root { --bg: #fbfaf7; --fg: #1d2330; --muted: #5d6675; --line: #e3e0d8; --card: #fff; --accent: #1f6f5c; --accent-fg: #fff; }
10
+ @media (prefers-color-scheme: dark) { :root { --bg: #14171c; --fg: #e8e6e1; --muted: #9aa3ad; --line: #2b3038; --card: #1b1f26; --accent: #4fb89b; --accent-fg: #0d1a16; } }
11
+ * { box-sizing: border-box; }
12
+ body { margin: 0; background: var(--bg); color: var(--fg); font: 16px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif; }
13
+ main { max-width: 980px; margin: 0 auto; padding: 24px 16px 48px; }
14
+ h1 { font-size: 1.7rem; margin: 0 0 4px; }
15
+ .sub { color: var(--muted); margin: 0 0 20px; }
16
+ form { display: flex; flex-wrap: wrap; gap: 8px; align-items: end; background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 14px; }
17
+ label { display: flex; flex-direction: column; font-size: .85rem; color: var(--muted); gap: 4px; }
18
+ input { font: inherit; padding: 8px 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--bg); color: var(--fg); width: 150px; }
19
+ button { font: inherit; padding: 9px 16px; border: 0; border-radius: 6px; background: var(--accent); color: var(--accent-fg); cursor: pointer; }
20
+ #status { color: var(--muted); margin: 12px 0; min-height: 1.5em; }
21
+ .wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: 10px; background: var(--card); }
22
+ table { border-collapse: collapse; width: 100%; font-size: .9rem; }
23
+ th, td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--line); white-space: nowrap; }
24
+ th { font-weight: 600; color: var(--muted); }
25
+ td.n { text-align: right; font-variant-numeric: tabular-nums; }
26
+ .cta { margin-top: 24px; padding: 16px; border-radius: 10px; border: 1px solid var(--accent); background: var(--card); }
27
+ .cta a { color: var(--accent); font-weight: 600; }
28
+ footer { margin-top: 28px; color: var(--muted); font-size: .8rem; }
29
+ footer a { color: inherit; }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <main>
34
+ <h1>Ziplore: free US ZIP code lookup & radius search</h1>
35
+ <p class="sub">City, county, time zone and Census demographics for any US ZIP code, and every ZIP within a radius. 41,682 ZIPs, all in your browser.</p>
36
+ <form id="f">
37
+ <label>ZIP code or "lat,lon" <input id="q" value="10001" autocomplete="postal-code"></label>
38
+ <label>Radius (miles, 0 = just this ZIP) <input id="mi" type="number" min="0" max="100" step="1" value="2"></label>
39
+ <label>Min. population <input id="minpop" type="number" min="0" step="100" value="0"></label>
40
+ <button type="submit">Look up</button>
41
+ </form>
42
+ <div id="status">Loading ZIP table…</div>
43
+ <div class="wrap"><table id="t"><thead><tr><th>ZIP</th><th>City</th><th>State</th><th>County</th><th>Miles</th><th>Time zone</th><th>Local time</th><th>Population</th><th>Median household income</th><th>Median home value</th><th>Median rent</th><th>Median age</th></tr></thead><tbody></tbody></table></div>
44
+ <div class="cta">
45
+ <strong>Need this for a whole list, inside a workflow, or for an AI agent?</strong>
46
+ <a href="https://apify.com/cybermax/zip-data-api" target="_blank" rel="noopener">Run Ziplore on Apify</a>: send thousands of ZIPs, addresses, "City, ST" names or coordinates and get clean JSON with county FIPS, every county a ZIP touches, UTC offsets, per-capita income, owner-occupied share and nearby ZIPs. $0.0005 per ZIP, unknown ZIPs free, and it works as an MCP tool for Claude and Cursor.
47
+ Bulk data: <a href="https://huggingface.co/datasets/shaw276/us-zip-codes-demographics" target="_blank" rel="noopener">free dataset (CSV)</a>.
48
+ </div>
49
+ <footer>
50
+ Data: <a href="https://www.geonames.org/">GeoNames</a> (CC BY 4.0), US Census Bureau (2024 Gazetteer, ACS 2020–2024 5-year estimates), time zones from timezone-boundary-builder (ODbL, © OpenStreetMap contributors). Distances are between ZIP center points. Home values of $2,000,000 and incomes of $250,000 are Census caps ("or more"). Made by CyberMax. Not affiliated with USPS or the Census Bureau.
51
+ </footer>
52
+ </main>
53
+ <script>
54
+ let rows = [], byZip = new Map();
55
+ const $ = (id) => document.getElementById(id);
56
+ const fmt = (v, pre = '') => v == null ? '–' : pre + Number(v).toLocaleString('en-US');
57
+ const esc = (s) => String(s ?? '').replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
58
+ function miles(a, b, c, d) {
59
+ const r = Math.PI / 180, x = Math.sin((c - a) * r / 2) ** 2 + Math.cos(a * r) * Math.cos(c * r) * Math.sin((d - b) * r / 2) ** 2;
60
+ return 2 * 3958.7613 * Math.asin(Math.min(1, Math.sqrt(x)));
61
+ }
62
+ function localTime(tz) {
63
+ if (!tz) return '–';
64
+ try { return new Intl.DateTimeFormat('en-US', { timeZone: tz, hour: 'numeric', minute: '2-digit' }).format(new Date()); } catch { return '–'; }
65
+ }
66
+ function render(list) {
67
+ $('t').tBodies[0].innerHTML = list.map(([d, r]) => `<tr><td>${esc(r.zip)}</td><td>${esc(r.city)}</td><td>${esc(r.state)}</td><td>${esc(r.county)}</td>
68
+ <td class="n">${d == null ? '–' : d.toFixed(1)}</td><td>${esc(r.timezone || '–')}</td><td>${localTime(r.timezone)}</td><td class="n">${fmt(r.population)}</td>
69
+ <td class="n">${fmt(r.medianHouseholdIncome, '$')}</td><td class="n">${fmt(r.medianHomeValue, '$')}</td><td class="n">${fmt(r.medianGrossRent, '$')}</td><td class="n">${r.medianAge ?? '–'}</td></tr>`).join('');
70
+ }
71
+ function run() {
72
+ const q = $('q').value.trim(), mi = Math.min(Math.max(Number($('mi').value) || 0, 0), 100), minPop = Number($('minpop').value) || 0;
73
+ let lat, lon, center;
74
+ const pt = q.match(/^(-?\d+(?:\.\d+)?)\s*[,\s]\s*(-?\d+(?:\.\d+)?)$/);
75
+ if (pt) { lat = +pt[1]; lon = +pt[2]; }
76
+ else {
77
+ const z = (q.match(/\d{5}/) || [q.replace(/\D/g, '').padStart(5, '0')])[0];
78
+ center = byZip.get(z);
79
+ if (!center) { $('status').textContent = `${z} isn't a US ZIP code in this table.`; render([]); return; }
80
+ lat = center.latitude; lon = center.longitude;
81
+ }
82
+ let list;
83
+ if (mi > 0 || !center) {
84
+ list = rows.filter((r) => !['AA', 'AE', 'AP'].includes(r.state)).map((r) => [miles(lat, lon, r.latitude, r.longitude), r]);
85
+ list = mi > 0 ? list.filter(([d]) => d <= mi) : [list.filter(([, r]) => r.hasZcta).sort((a, b) => a[0] - b[0])[0]].filter(Boolean);
86
+ list = list.filter(([d, r]) => (r.population ?? 0) >= minPop || (center && r === center)).sort((a, b) => a[0] - b[0]);
87
+ } else list = [[null, center]];
88
+ const shown = list.slice(0, 300);
89
+ $('status').textContent = mi > 0 ? `${list.length} ZIP code${list.length === 1 ? '' : 's'} within ${mi} miles${list.length > 300 ? ' (first 300 shown)' : ''}.` : (center ? '' : 'Nearest ZIP code to that point:');
90
+ render(shown);
91
+ }
92
+ $('f').addEventListener('submit', (e) => { e.preventDefault(); run(); });
93
+ fetch('zips.json').then((r) => r.json()).then(({ cols, rows: raw }) => {
94
+ rows = raw.map((a) => Object.fromEntries(cols.map((c, i) => [c, a[i]])));
95
+ byZip = new Map(rows.map((r) => [r.zip, r]));
96
+ $('status').textContent = '';
97
+ run();
98
+ }).catch(() => { $('status').textContent = 'Could not load the ZIP table. Reload the page.'; });
99
+ </script>
100
+ </body>
101
  </html>