Extracting Structured Fields From a HUD-1 Settlement Statement
10 min read · updated August 11, 2026
The HUD-1 numbers its lines, and the numbering is the specification. A form where every amount has an address and every block has a printed total is a form whose extraction can be validated arithmetically before any human looks at it — provided you know which amounts are deliberately excluded from the totals.
Why you still meet this form
The HUD-1 Settlement Statement is the settlement disclosure required under the Real Estate Settlement Procedures Act, published by the US Department of Housing and Urban Development. For most consumer mortgage closings it was superseded by the Closing Disclosure when the integrated disclosure rules took effect, but it did not disappear: it remains in use for transaction types outside that rule’s scope, and every archive of closings from before the change is full of them.
That makes it a document type with a particular profile — low current volume, high historical volume, arriving as scans of varying quality when a title search or a basis calculation reaches back into old files. It is a good example of the situation argued about on designing a schema for a document you see a handful of times a year, except that here the form is prescribed, so the usual objection to building something specific does not apply.
The numbered series as an addressing scheme
The form’s first page is organised into hundred-numbered blocks, borrower on the left and seller on the right. The 100 series is the gross amount due from the borrower and the 200 series is what has already been paid by or on their behalf; the 300 series resolves the two into cash at settlement. The 400 and 500 series do the same for the seller, and the 600 series is the seller’s cash line. The second page carries the settlement charges themselves in series from 700 through 1300 — broker fees, items payable in connection with the loan, items required by the lender to be paid in advance, reserves deposited with the lender, title charges, government recording and transfer charges, and additional charges — totalling in the 1400 line.
Address by line number, not by label. Labels are partly fillable: the form leaves blank numbered lines within each series for the settlement agent to write in whatever the transaction needed, so the label on line 108 is transaction-specific while the line number is not. A schema keyed on line number with the label as a value handles that; a schema keyed on label does not.
{
"line": "1303",
"series": 1300,
"label_as_printed": "Survey",
"borrower_paid": "425.00",
"seller_paid": null,
"poc": null
}Three identities, worked
The form’s value to an extraction pipeline is that it foots three ways. Take a synthetic residential sale — and note that the payoff figure on the seller’s side is the 186,448.52 derived on the payoff statement page, which is what reconciling two documents against each other looks like.
BORROWER 101 Contract sales price 425,000.00 103 Settlement charges to borrower (line 1400) 11,240.75 106 City/town taxes paid in advance 512.40 --------------------------------------------------------- 120 GROSS AMOUNT DUE FROM BORROWER 436,753.15 201 Deposit or earnest money 12,750.00 202 Principal amount of new loan(s) 340,000.00 --------------------------------------------------------- 220 TOTAL PAID BY/FOR BORROWER 352,750.00 301 Gross amount due from borrower (line 120) 436,753.15 302 Less amounts paid by/for borrower (220) 352,750.00 --------------------------------------------------------- 303 CASH FROM BORROWER 84,003.15
Check the two sums. 425,000.00 + 11,240.75 = 436,240.75, plus 512.40 = 436,753.15, which is line 120. Then 12,750.00 + 340,000.00 = 352,750.00, which is line 220. And 436,753.15 − 352,750.00 = 84,003.15, which is line 303. The seller’s side works the same way:
SELLER 401 Contract sales price 425,000.00 406 City/town taxes paid in advance 512.40 --------------------------------------------------------- 420 GROSS AMOUNT DUE TO SELLER 425,512.40 502 Settlement charges to seller (line 1400) 27,180.00 504 Payoff of first mortgage loan 186,448.52 --------------------------------------------------------- 520 TOTAL REDUCTIONS IN AMOUNT DUE SELLER 213,628.52 601 Gross amount due to seller (line 420) 425,512.40 602 Less total reductions (line 520) 213,628.52 --------------------------------------------------------- 603 CASH TO SELLER 211,883.88
27,180.00 + 186,448.52 = 213,628.52, and 425,512.40 − 213,628.52 = 211,883.88. The third identity ties the pages together: the total settlement charges on line 1400 appear on the first page as line 103 in the borrower’s column and line 502 in the seller’s. So an extraction of page two that sums to something other than 11,240.75 and 27,180.00 respectively has lost a charge line, and you know that from page one without reading page two carefully at all.
Encode all three as post-extraction assertions with a one-cent tolerance, and record which failed rather than a single pass or fail. Knowing that the 200 series is short while the 100 series foots points at one block of the page, and prioritising the review queue by financial impact is far easier when the failure names an amount.
Amounts printed beside the column, not in it
Here is the detail that breaks extractions of this form specifically. Some charges are paid outside of closing — an appraisal fee the borrower already paid the lender directly, a commission paid ahead of settlement — and the form’s convention is that these are shown marked as paid outside closing, outside the columns, so that they are disclosed without being included in the totals.
804 Appraisal fee to Cascade Appraisal 550.00 (POC) 805 Credit report to National Credit 42.50
A reader that harvests every number on the line and sums the column adds 550.00 to the settlement charges. Line 1400 then disagrees with lines 103 and 502 by exactly that amount, and if all three totals were extracted rather than derived, nothing detects it. The extraction has to treat the paid-outside marker as a column of its own:
{
"line": "804",
"label_as_printed": "Appraisal fee to Cascade Appraisal",
"borrower_paid": null,
"seller_paid": null,
"poc": { "amount": "550.00", "paid_by": "borrower" }
}The poc amount is disclosed data and belongs in the record; it simply never enters a column total. This is a good general illustration of why a money field on a document is not just a number: the same numeral in two positions on one line means two different things, and the position is the semantics.
Two columns on one physical row
The remaining difficulties are structural rather than arithmetic.
- Borrower and seller share a physical row. Line 101 and line 401 are frequently printed side by side, both labelled “Contract sales price”, both showing 425,000.00. A row-wise reader that flattens the page produces one line with two amounts and no way to tell which party each belongs to. Segment the page into a borrower region and a seller region by horizontal position first, then read each region independently — the same coordinate-clustering approach that a document with a sidebar and a main column needs, applied to two equal columns.
- Blank numbered lines are normal. Most statements use a minority of the available lines. An extractor that emits a row for every number in the series, with nulls, is easier to validate than one that emits only populated lines, because the shape is then fixed and comparable across documents.
- Handwritten amendments. Settlement agents cross out and initial figures at the table. The struck value and the written replacement both appear; the replacement governs, and both should be captured with the correction noted, not silently resolved. A record that shows only the final figure cannot answer a later question about what changed, which is the entire purpose of an audit trail on an extracted field.
- Parentheses and dashes. Negative amounts appear in parentheses, and empty cells are often a dash or a row of hyphens rather than blank. Normalise both explicitly before parsing, and keep null distinct from zero throughout — see writing a validation rule for a currency amount field.