← back to Commercialrealestate
EDGAR REIT deal-flow: fetch-edgar-reit-deals.js — institutional acquisitions from SEC 8-Ks (public/resellable, $0); 1255 REITs, 40 filings/120d; every record source-stamped
fe58f9ecafb1b4ede9bdb4267b78bab1f1a89fc0 · 2026-07-31 09:05:01 -0700 · steve
Files touched
M .gitignoreA scripts/fetch-edgar-reit-deals.js
Diff
commit fe58f9ecafb1b4ede9bdb4267b78bab1f1a89fc0
Author: steve <steve@designerwallcoverings.com>
Date: Fri Jul 31 09:05:01 2026 -0700
EDGAR REIT deal-flow: fetch-edgar-reit-deals.js — institutional acquisitions from SEC 8-Ks (public/resellable, $0); 1255 REITs, 40 filings/120d; every record source-stamped
---
.gitignore | 2 +
scripts/fetch-edgar-reit-deals.js | 97 +++++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+)
diff --git a/.gitignore b/.gitignore
index 86d498c..6ce6cf5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,5 @@ data/raw/body-colliers-*.json
data/*.pre-*.bak
data/raw/*.pre-*.bak
+
+data/edgar-reit-deals.json
diff --git a/scripts/fetch-edgar-reit-deals.js b/scripts/fetch-edgar-reit-deals.js
new file mode 100644
index 0000000..ee8b459
--- /dev/null
+++ b/scripts/fetch-edgar-reit-deals.js
@@ -0,0 +1,97 @@
+// fetch-edgar-reit-deals.js — RESELLABLE institutional deal-flow from SEC EDGAR (public, $0, no key).
+//
+// Doctrine (docs/SOURCING.md): resell only public-record data. Public real-estate companies + REITs are
+// LEGALLY REQUIRED to disclose material property acquisitions to the SEC — free, public, resellable, and
+// it catches the big institutional LLC/entity deals that dodge the county-assessor reassessment (the gap
+// in derive-sale-prices). Strategy: (1) enumerate REITs by SIC 6798, (2) EDGAR full-text-search for
+// acquisition 8-Ks in a window, (3) keep hits whose CIK is a REIT, (4) emit a linked filing feed.
+// The $ + property extraction from each 8-K exhibit is a follow-on step (LLM-parse) — this ships the
+// harness + metadata feed. SEC rule: descriptive User-Agent, <=10 req/s.
+// node scripts/fetch-edgar-reit-deals.js [days] (default 120)
+'use strict';
+const fs = require('fs');
+const path = require('path');
+const UA = 'DesignerWallcoverings-research steve@designerwallcoverings.com';
+const DAYS = +(process.argv[2] || 120);
+const ROOT = path.join(__dirname, '..');
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+const iso = d => d.toISOString().slice(0, 10);
+
+async function get(url, asJson, tries = 3) { // retry-then-skip: SEC 500s intermittently
+ for (let i = 0; i < tries; i++) {
+ try {
+ const r = await fetch(url, { headers: { 'User-Agent': UA, 'Accept-Encoding': 'gzip, deflate' } });
+ await sleep(140); // stay under SEC's 10 req/s
+ if ((r.status === 500 || r.status === 429) && i < tries - 1) { await sleep(700); continue; }
+ if (!r.ok) throw new Error(url + ' -> ' + r.status);
+ return asJson ? r.json() : r.text();
+ } catch (e) { if (i < tries - 1) { await sleep(700); continue; } throw e; }
+ }
+}
+
+// 1. Enumerate REIT CIKs (SIC 6798) from browse-edgar atom. The atom's company names are a SEC bug
+// ("ARRAY(0x...)"), but <cik> is clean — we only need the CIK SET for filtering; real names come from
+// the FTS display_names. Source: SEC EDGAR company search, SIC=6798 (public).
+async function reitCiks() {
+ const set = new Set();
+ for (let start = 0; start < 1500; start += 100) {
+ let atom; try { atom = await get(`https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&SIC=6798&type=&dateb=&owner=include&count=100&start=${start}&output=atom`); }
+ catch (e) { break; }
+ const ciks = atom.match(/<cik>(\d+)<\/cik>/gi) || [];
+ if (!ciks.length) break;
+ for (const c of ciks) { const m = c.match(/(\d+)/); if (m) set.add(String(+m[1])); }
+ process.stderr.write(` REIT enum: ${set.size} so far\r`);
+ }
+ return set;
+}
+
+// 2. EDGAR full-text search for acquisition 8-Ks in the window.
+async function acquisitionFilings(startdt, enddt) {
+ const phrases = ['%22completed+the+acquisition%22', '%22acquired+the+property%22', '%22purchase+of+the+property%22', '%22completed+the+purchase%22'];
+ const hits = [];
+ for (const q of phrases) {
+ let from = 0;
+ for (let page = 0; page < 10; page++) { // FTS caps ~100/page, 10 pages plenty for a window
+ let d; try { d = await get(`https://efts.sec.gov/LATEST/search-index?q=${q}&forms=8-K&startdt=${startdt}&enddt=${enddt}&from=${from}`, true); }
+ catch (e) { process.stderr.write(` FTS skip (${q}): ${e.message.slice(-8)}\n`); break; } // one bad phrase never kills the run
+ const arr = (d.hits && d.hits.hits) || [];
+ if (!arr.length) break;
+ for (const x of arr) {
+ const s = x._source || {};
+ const cik = String(+(((s.display_names || [''])[0].match(/CIK\s*(\d+)/) || [])[1] || 0));
+ hits.push({ id: x._id, cik, display: (s.display_names || [])[0] || '', date: s.file_date, form: (s.root_forms || s.file_type || '') });
+ }
+ from += arr.length;
+ }
+ }
+ return hits;
+}
+
+(async () => {
+ const end = new Date(process.env.GEN_TS ? Date.parse(process.env.GEN_TS) : Date.parse('2026-07-31'));
+ const start = new Date(end.getTime() - DAYS * 86400000);
+ process.stderr.write(`EDGAR REIT deal-flow: ${iso(start)} .. ${iso(end)}\n`);
+
+ const reits = await reitCiks();
+ process.stderr.write(`\nREITs (SIC 6798): ${reits.size}\n`);
+ const all = await acquisitionFilings(iso(start), iso(end));
+ // dedupe by filing id, keep REIT-only
+ const seen = new Set(), deals = [];
+ for (const h of all) {
+ if (seen.has(h.id)) continue; seen.add(h.id);
+ if (!reits.has(h.cik)) continue;
+ const acc = h.id.split(':')[0].replace(/-/g, ''); const file = h.id.split(':')[1] || '';
+ deals.push({
+ company: (h.display.split(' (')[0] || h.display).trim(), cik: h.cik, ticker: (h.display.match(/\(([A-Z.\-]{1,6})\)/) || [])[1] || null,
+ form: h.form, filed: h.date,
+ filing_url: `https://www.sec.gov/Archives/edgar/data/${h.cik}/${acc}/${file}`,
+ source: 'SEC EDGAR 8-K (public, resellable)', price: null, property: null, extract_status: 'pending-llm-parse'
+ });
+ }
+ deals.sort((a, b) => (b.filed || '').localeCompare(a.filed || ''));
+ const out = { generated: process.env.GEN_TS || iso(end), source: 'SEC EDGAR full-text search (SIC 6798 REITs, acquisition 8-Ks)', window_days: DAYS, reit_universe: reits.size, count: deals.length, deals };
+ fs.writeFileSync(path.join(ROOT, 'data', 'edgar-reit-deals.json'), JSON.stringify(out, null, 2));
+ process.stderr.write(`\nREIT acquisition filings (last ${DAYS}d): ${deals.length}\n`);
+ deals.slice(0, 8).forEach(d => process.stderr.write(` ${d.filed} ${(d.company || '').slice(0, 40).padEnd(40)} ${d.ticker || ''}\n`));
+ console.log(JSON.stringify({ reit_universe: reits.size, deals: deals.length, cost: '$0 (SEC public data)' }));
+})().catch(e => { console.error('FATAL', e.message); process.exit(1); });
← 7c67770 broker mind-map: add firm-relationship graph (/api/graph?his
·
back to Commercialrealestate
·
deal-flow card viewer: icon cards over both free public sour 4c2ac2a →