Real estate investment research data pipeline for US metros and cities
Project description
Find your next real estate market in minutes, not months.
A pip-installable Python package that pulls public government data about every major US metro and city — population growth, job growth, wages, unemployment — and stores it locally in SQLite for analysis. Use it as a library, from the CLI, or build your own UI on top.
Install
pip install cityscope
Or with uv:
uv add cityscope
Python API
from cityscope import api
# Fetch data from public sources
api.fetch("census_population")
api.fetch("bls_employment", skip_laus=True)
# Query as a DataFrame
df = api.to_dataframe(metric="population_change_pct", geo_type="metro", year=2024)
print(df.sort_values("value", ascending=False).head(10))
# Or as a list of dicts
rows = api.query(metric="employment_change_pct", geo_type="metro", year=2024, limit=10)
for row in rows:
print(f"{row['name']}: {row['value']:+.1f}% job growth")
# Look up stats for a specific address
report = api.lookup("1600 Amphitheatre Pkwy, Mountain View, CA", auto_fetch=True)
print(f"Metro: {report.metro.name} — {report.metro.metrics}")
print(f"City: {report.city.name} — population {report.city.population:,}")
print(f"County: {report.county.name}")
# Configure (optional — works with defaults)
api.configure(db_path="my_data.db", min_population=100_000)
# Check what you have
api.status()
api.list_sources()
api.get_geographies(geo_type="metro", min_population=500_000)
CLI
# Fetch data
cityscope fetch census_population
cityscope fetch bls_employment --skip-laus
cityscope fetch --all
# Query
cityscope query -m population_change_pct -g metro -y 2024
cityscope query -m employment_change_pct -g metro -y 2024 -n 10
cityscope query -m avg_annual_pay -y 2024 -n 15
# Look up stats for an address
cityscope lookup "1600 Amphitheatre Pkwy, Mountain View, CA"
cityscope lookup "123 Main St, Austin, TX" --auto-fetch
# Info
cityscope sources
cityscope status
Commands
| Command | Description |
|---|---|
fetch <source> |
Pull data from a source (census_population, bls_employment) |
fetch --all |
Pull from all sources |
query |
Query stored data (-m, -g, -y, --min-pop, -n) |
lookup <address> |
Look up stats for a US address (metro + city + county) |
sources |
List available data sources |
status |
Show fetched data summary |
init-config |
Generate default config/settings.yaml |
Address Lookup
The lookup command geocodes a US address (via the free Census Geocoder) and returns stats for the enclosing metro, city, and county:
cityscope lookup "1600 Amphitheatre Pkwy, Mountain View, CA" --auto-fetch
With --auto-fetch, cityscope will fall back to fetching missing data from source APIs on-the-fly (useful for addresses in counties or smaller cities not covered by the default 200k+ metro/city fetch). Results are cached in the local DB.
Global flags: -v (verbose logging), -c PATH (custom config file).
Fetch Flags
| Flag | Description |
|---|---|
--vintage YEAR |
Override Census vintage year |
--min-pop N |
Override population filter (default: 200,000) |
--skip-laus |
Skip unemployment rate (avoids BLS API daily limit) |
What Data You Get
370+ metros and cities (200k+ population), each tracked across 9 metrics over 5 years (2020–2024):
| Metric | Source | Description |
|---|---|---|
population |
Census PEP/ACS | Total population |
population_change_pct |
Census PEP/ACS | Year-over-year population growth % |
employment |
BLS QCEW | Total nonfarm jobs |
employment_change_pct |
BLS QCEW | Year-over-year job growth % |
avg_annual_pay |
BLS QCEW | Average annual pay ($) |
avg_weekly_wage |
BLS QCEW | Average weekly wage ($) |
unemployment_rate |
BLS LAUS | Annual avg unemployment rate (%) |
All data is free, public domain, pulled directly from federal APIs.
Configuration
Optional — everything works with defaults. For higher API limits:
# config/settings.yaml
census:
api_key: null # Free: https://api.census.gov/data/key_signup.html
bls:
api_key: null # Free: https://data.bls.gov/registrationEngine/
storage:
db_path: data/cityscope.db
pipeline:
min_population: 200000
Or configure programmatically:
api.configure(census_api_key="your_key", bls_api_key="your_key")
Architecture
Census API ──┐ ┌── Python API (cityscope.api)
├── Pipeline ── SQLite DB ──┤
BLS QCEW ──┘ (fetch) └── CLI (cityscope)
Data flows: Source → Pipeline → SQLite → API/CLI/your code.
Adding Data Sources
Each source is a self-contained class with @SourceRegistry.register:
from cityscope.core.registry import SourceRegistry
from cityscope.core.source import DataSource
from cityscope.core.models import FetchResult
@SourceRegistry.register
class MySource(DataSource):
source_id = "my_source"
name = "My Data Source"
description = "What it provides"
def fetch(self, **kwargs) -> FetchResult:
...
Add the import to src/cityscope/sources/__init__.py — it auto-registers in CLI, API, and pipeline.
Dashboard
For a visual dashboard, see urban-research-ui.
Roadmap
- Rent data (HUD Fair Market Rents, Zillow ZORI)
- Home price index (FHFA HPI)
- Crime stats (FBI Crime Data Explorer)
- School quality (NCES)
- Walkability (EPA Smart Location Database)
- Migration flows (IRS SOI county-to-county)
- Neighborhood-level data (Census tract)
- Composite scoring engine
See data_sources.md for research on 50+ public data sources.
Contributing
Pull requests welcome. The easiest way to contribute is adding a new data source — the plugin architecture makes it straightforward.
License
MIT
Built with Claude Code (Claude Opus 4.6).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cityscope-0.3.1.tar.gz.
File metadata
- Download URL: cityscope-0.3.1.tar.gz
- Upload date:
- Size: 112.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be7d2c3e4fb26d2bc50c7c171404ef1bc3fb589910d839ad2c79027c3dd0aa76
|
|
| MD5 |
2d36e1947baab56244f5d623785e8e53
|
|
| BLAKE2b-256 |
5a6e4dcae86703be1a926c87ac14278b6491aa232a44e145c7f6bcc76f32676c
|
Provenance
The following attestation bundles were made for cityscope-0.3.1.tar.gz:
Publisher:
publish.yml on kkarbasi/cityscope
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cityscope-0.3.1.tar.gz -
Subject digest:
be7d2c3e4fb26d2bc50c7c171404ef1bc3fb589910d839ad2c79027c3dd0aa76 - Sigstore transparency entry: 1436808462
- Sigstore integration time:
-
Permalink:
kkarbasi/cityscope@09fc528ab2307afd669d419d6684117c2661708d -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/kkarbasi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@09fc528ab2307afd669d419d6684117c2661708d -
Trigger Event:
release
-
Statement type:
File details
Details for the file cityscope-0.3.1-py3-none-any.whl.
File metadata
- Download URL: cityscope-0.3.1-py3-none-any.whl
- Upload date:
- Size: 35.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d1ceabb11474802c73e28f4d48ecb6cb58b690e903d55d16ecfa080cca7ed51
|
|
| MD5 |
b0f18d1512a64e3ab7d8748481991530
|
|
| BLAKE2b-256 |
6cc6fa6c7d3f8b6a5b2b147b23f540f3a45396e5af4a6bd5ffc543f40908db67
|
Provenance
The following attestation bundles were made for cityscope-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on kkarbasi/cityscope
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cityscope-0.3.1-py3-none-any.whl -
Subject digest:
5d1ceabb11474802c73e28f4d48ecb6cb58b690e903d55d16ecfa080cca7ed51 - Sigstore transparency entry: 1436808492
- Sigstore integration time:
-
Permalink:
kkarbasi/cityscope@09fc528ab2307afd669d419d6684117c2661708d -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/kkarbasi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@09fc528ab2307afd669d419d6684117c2661708d -
Trigger Event:
release
-
Statement type: