# City of Boston — ArcGIS (no key) # ============================================ # FY26 — Single parcel curl -s -G "https://gis.bostonplans.org/hosting/rest/services/FY26_Parcels_Detailed/FeatureServer/0/query" \ --data-urlencode "where=PID=''" \ --data-urlencode "outFields=*" \ --data-urlencode "returnGeometry=false" \ --data-urlencode "f=json" # FY26 — All parcels in ZIP curl -s -G "https://gis.bostonplans.org/hosting/rest/services/FY26_Parcels_Detailed/FeatureServer/0/query" \ --data-urlencode "where=ZIP_CODE='02134'" \ --data-urlencode "outFields=PID,FULL_ADDRESS,LU,LU_DESC,LIVING_AREA,TOTAL_VALUE,YR_BUILT,OWNER,OWN_OCC,ZIP_CODE" \ --data-urlencode "orderByFields=TOTAL_VALUE DESC" \ --data-urlencode "returnGeometry=false" \ --data-urlencode "f=json" # FY26 — All parcels on a street curl -s -G "https://gis.bostonplans.org/hosting/rest/services/FY26_Parcels_Detailed/FeatureServer/0/query" \ --data-urlencode "where=ST_NAME=''" \ --data-urlencode "outFields=*" \ --data-urlencode "orderByFields=TOTAL_VALUE DESC" \ --data-urlencode "returnGeometry=false" \ --data-urlencode "f=json" # FY26 — Filter by land use # where=LU IN ('R2','R3') AND ZIP_CODE='02134' # FY25 — Different endpoint, uses GIS_ID not PID curl -s -G "https://gisportal.boston.gov/arcgis/rest/services/Assessing/Property_Assessment_FY25/FeatureServer/0/query" \ --data-urlencode "where=GIS_ID=''" \ --data-urlencode "outFields=*" \ --data-urlencode "returnGeometry=false" \ --data-urlencode "f=json" # Historical year discovery: # https://www.arcgis.com/sharing/rest/search # Search: "Assessing" or "Property_Assessment" # City of Boston — CKAN (no key) # ============================================ # Building permits by street name curl -s -G "https://data.boston.gov/api/3/action/datastore_search" \ --data-urlencode "resource_id=6ddcd912-32a0-43df-9908-63574f8c7e77" \ --data-urlencode "q=" \ --data-urlencode "limit=100" # Filter results to specific address after fetching. # Annual assessment CSVs (FY2013–FY2026): # https://data.boston.gov/dataset/e02c44d2-3c64-459c-8fe2-e1ce5f38a035 # Filter by PID. Older CSVs may store tax as cents-integer (÷100). # Zillow — Apify (needs $APIFY_API_KEY) # ============================================ # General 3-step pattern for all Apify actors: # 1. Start curl -s -X POST "https://api.apify.com/v2/acts//runs?token=$APIFY_API_KEY" \ -H "Content-Type: application/json" \ -d '' # → .data.id = RUN_ID, .data.defaultDatasetId = DATASET_ID # 2. Poll curl -s "https://api.apify.com/v2/acts//runs/$RUN_ID?token=$APIFY_API_KEY" # → .data.status, poll every 10s until "SUCCEEDED" # 3. Fetch curl -s "https://api.apify.com/v2/acts//runs/$RUN_ID/dataset/items?token=$APIFY_API_KEY&format=json" # --- Detail Scraper: maxcopell/zillow-detail-scraper --- # Start config: # { # "propertyStatus": "RECENTLY_SOLD", # "startUrls": [{"url": "https://www.zillow.com/homedetails/..."}] # } # Result is array. Single URL → single-element array. # --- ZIP Search: maxcopell/zillow-zip-search --- # Start config: {"zipCodes":["02134"]} # Returns basic property list (no price history). Use to cross-ref addresses → ZPIDs. # --- Google Search Scraper: apify/google-search-scraper --- # Query: " zillow" # Extracts Zillow URL + ZPID from organic results. # Proven: "30 Seattle St Allston MA 02134 zillow" → ZPID 59086989. # --- Batch: pass multiple URLs to Detail Scraper in one run --- # "startUrls": [{"url":"..."}, {"url":"..."}, ...] # Split results by address afterward. # MA Secretary of State — Puppeteer (Incapsula, headful Chrome) # ============================================================== # url: https://corp.sec.state.ma.us/CorpWeb/CorpSearch/CorpSearch.aspx # blocked to curl → launch headful Chrome (headless: false) # flow: # 1. goto CorpSearch.aspx, wait networkidle2 # 2. fill input[name*="EntityName"] with name, fallback: first visible input[text] # 3. click input[value="Search Corporations"], wait navigation # 4. find whose textContent includes entity name (upper-cased), click, wait nav # fallback: first in first table row # 5. extract from CorpSummary: # querySelectorAll('table tr, .formTable tr, .corpTable tr') # for each row with ≥2 cells: label = cells[0].text (strip ':'), value = cells[1].text # also grab body.innerText(5000) as rawText # 6. save {fields[], rawText, url} # Suffolk Registry of Deeds — Puppeteer (ASP.NET __doPostBack) # ============================================================= # url: https://www.masslandrecords.com/suffolk/ # CRITICAL: ASP.NET WebForms. Changing