← back to Nationalrealestate
src/ingest/brokers/il_idfpr.ts
72 lines
/**
* IL — IDFPR "Professional Licensing" on illinois-edp.data.socrata.com
* (pzzh-kp68; 4.26M rows all professions, probed 2026-07-21). We pull only
* license_type='REAL ESTATE' via SODA CSV (663,185 rows / 593,494 distinct
* license numbers — renewal-era duplicate rows exist, so pages are ordered by
* license_number + lastmodifieddate ascending: engine's last-write-wins keeps
* the newest row). `description` is the specific credential (SALESPERSON /
* BROKER / MANAGING BROKER / BROKER CORPORATION / …) and becomes our
* license_type. Education credentials (CE/pre-license courses, schools,
* instructors) are skipped. No employer linkage columns → individuals carry
* no firm, like CO.
*/
import { download } from '../run.ts';
import { parseCsvObjects } from './csv.ts';
import type { BrokerRow, FetchedFile, StateAdapter } from './types.ts';
const PAGE = 300_000;
const MAX_PAGES = 10;
const BASE = 'https://illinois-edp.data.socrata.com/resource/pzzh-kp68.csv';
const QUERY = '?$where=' + encodeURIComponent("license_type='REAL ESTATE'")
+ '&$order=' + encodeURIComponent('license_number,lastmodifieddate')
+ `&$limit=${PAGE}`;
const EDUCATION = /CE (COURSE|INSTRUCTOR|SCHOOL|SPONSOR)|PRE-LICENSE|EDUCATION PROVIDER/;
const FIRM = /CORPORATION|LIMITED LIABILITY|PARTNERSHIP|ASSOCIATION|BRANCH OFFICE|VIRTUAL OFFICE/;
export const ilIdfpr: StateAdapter = {
state: 'IL',
source: 'il_idfpr',
url: BASE + QUERY,
async fetch() {
const out: FetchedFile[] = [];
for (let p = 0; p < MAX_PAGES; p++) {
const f = await download(`${BASE}${QUERY}&$offset=${p * PAGE}`, `il_idfpr_p${p}.csv`);
const { statSync } = await import('node:fs');
out.push(f);
// header-only page (~300 bytes) = past the last row
if (statSync(f.path).size < 1000) break;
await new Promise(r => setTimeout(r, 500)); // ≤2 req/s
}
return out;
},
*parse(files: FetchedFile[]): Generator<BrokerRow> {
for (const file of files) {
for (const o of parseCsvObjects(file.path)) {
const desc = (o.description || '').toUpperCase();
if (!o.license_number || !desc || EDUCATION.test(desc)) continue;
const status = o.license_status || null;
const city = o.city || null;
const st = (o.state || '').toUpperCase() || null;
const personName = [o.first_name, o.middle, o.last_name].filter(Boolean).join(' ');
const bizName = o.business_name || o.businessdba || '';
if (FIRM.test(desc) || o.business === 'Y') {
const name = bizName || personName;
if (!name) continue;
yield {
kind: 'firm', name, license_no: o.license_number,
license_type: desc, license_status: status, city, state_code: st,
};
} else {
const name = personName || bizName;
if (!name) continue;
yield {
kind: 'broker', name, license_no: o.license_number,
license_type: desc, license_status: status, city, state_code: st,
};
}
}
}
},
};