← back to Nationalrealestate
src/ingest/brokers/ny_dos.ts
49 lines
/**
* NY — DOS "Active Real Estate Salespersons and Brokers" on data.ny.gov
* (yg7h-zjbf, refreshed daily; 147,941 rows probed 2026-07-21). Active licenses
* only, so license_status = 'Active'. Individuals carry their firm as
* business_name (no firm license number in-row → firms dedupe by normalized name).
* Entity types (CORPORATE/LLC/PARTNERSHIP/TRADENAME BROKER, PRINCIPAL OFFICE)
* become firm rows; BRANCH OFFICE rows are skipped (locations, not entities).
*/
import { download } from '../run.ts';
import { parseCsvObjects } from './csv.ts';
import type { BrokerRow, FetchedFile, StateAdapter } from './types.ts';
const URL = 'https://data.ny.gov/api/views/yg7h-zjbf/rows.csv?accessType=DOWNLOAD';
const INDIVIDUAL = new Set(['SALESPERSON', 'ASSOCIATE BROKER', 'INDIVIDUAL BROKER', 'REAL ESTATE SALESPERSON']);
const FIRM = new Set(['CORPORATE BROKER', 'LIMITED LIABILITY BROKER', 'PARTNERSHIP BROKER', 'TRADENAME BROKER', 'REAL ESTATE PRINCIPAL OFFICE']);
export const nyDos: StateAdapter = {
state: 'NY',
source: 'ny_dos',
url: URL,
async fetch() {
return [await download(URL, 'ny_dos_active_re_licensees.csv')];
},
*parse(files: FetchedFile[]): Generator<BrokerRow> {
for (const o of parseCsvObjects(files[0].path)) {
const type = (o.license_type || '').toUpperCase();
const city = o.business_city || null;
const st = (o.business_state || '').toUpperCase() || null;
if (!o.license_number) continue;
if (FIRM.has(type)) {
const name = o.business_name || o.license_holder_name;
if (!name) continue;
yield {
kind: 'firm', name, license_no: o.license_number,
license_type: type, license_status: 'Active', city, state_code: st,
};
} else if (INDIVIDUAL.has(type)) {
if (!o.license_holder_name) continue;
yield {
kind: 'broker', name: o.license_holder_name, license_no: o.license_number,
license_type: type, license_status: 'Active',
firm_name: o.business_name || null, firm_license_no: null,
city, state_code: st,
};
}
}
},
};