Extracting Structured Data From a Zoning Compliance Letter
9 min read · updated August 11, 2026
A zoning compliance letter is a short prose letter carrying a legal conclusion about one parcel on one date. Extract it as “address, zone, compliant yes/no” and you have thrown away everything that made anybody pay for it.
What the letter certifies
A municipality issues one of these on request, usually because a lender or a buyer asked. It typically states the zoning district a parcel sits in, whether the existing use is allowed in that district, whether the structures conform to bulk and setback requirements, whether there are open violations or enforcement actions, and whether any variances or special permits are on record. It is a statement of the municipality’s position as of the date of signature, and its weight comes from being cited — it names the sections of the zoning ordinance it is applying.
Nothing in that is tabular, which is why this document defeats the usual approach. There is no grid to align and no key-value block to read; the answer is distributed across three or four paragraphs of careful lawyerly prose, and the qualifications inside those paragraphs carry as much information as the conclusions. The right mental model is closer to reading a contract clause than to reading an invoice, and the field you are extracting is often a distinction rather than a value.
Parcel identifiers have no check digit
Many document identifiers validate themselves. An ISBN-13 has a mod-10 check digit, an IBAN has a mod-97 remainder, a VIN has a published transliteration and weight table. You can therefore catch an OCR error arithmetically, without leaving the page — the whole approach to a checksum-validated identifier field rests on that. A parcel identifier — APN, PIN, PID, tax map key, depending on the jurisdiction — has none of that. It is a positional code assigned by an assessor, typically encoding book, map, block and lot, and any digit string of the right shape is syntactically valid.
Three consequences follow, and all three bite.
- Punctuation is part of the identifier.
013-24-005-0110and0132400050110may be the same parcel, or the second may be unparseable in the county’s own system. Preserve the printed form verbatim in one field and put any normalised form in a second, clearly derived field. - Leading zeros are load-bearing and fragile. The classic loss is not OCR at all — it is a spreadsheet or a JSON consumer coercing the value to a number. Type the field as a string everywhere and reject any pipeline stage that cannot promise that.
- Validation means lookup. Because there is no checksum, the only real check is that the identifier resolves in the assessor’s roll and that the address it resolves to matches the address printed on the letter. Two independent statements of the same parcel on one page is the redundancy the document gives you; use it.
Character confusion is worth targeting specifically. In these codes zero and letter O, one and letter I, five and S, and eight and B are the frequent substitutions, and unlike a natural-language field there is no surrounding context for a model to correct against. If the jurisdiction’s format is known — and it is, per county — encode it as a pattern and treat a violation as a re-read trigger rather than as an error to accept.
Permitted, conditional, or nonconforming
The field that a boolean destroys is use status. There are at least three states and they have completely different consequences for whoever asked for the letter:
- Permitted by right. The use is listed as allowed in the district. Nothing further is required.
- Permitted by special permit, conditional use or variance. The use is allowed because a specific approval was granted, and that approval has its own case number, date and conditions. Those conditions can run with the land, so the case number is a field, not a footnote.
- Legal nonconforming. The use or the structure predates the current ordinance and is allowed to continue as it exists. This is the state that most often gets flattened to “compliant: true”, and it is the state with the most conditions attached to it — letters commonly add language about what happens if the use is discontinued or the structure is substantially damaged. Extract the presence of that language as a flag and keep the sentence.
A fourth state exists and is easy to miss: the letter declines to opine. Municipalities sometimes answer only part of a request, or state that they do not certify setbacks without a survey. “Not addressed” must be representable and must not collapse into “no violations”. The general shape of that problem — distinguishing absent from negative from unread — recurs across this cluster and is why missing-field handling deserves deciding once rather than per document type.
The citation is the grounding
A zoning letter without section references is an opinion; with them it is a traceable application of a published ordinance. So capture the citations as structured references — the ordinance or code name, the section number as printed, and what each was cited for — rather than leaving them inside a paragraph of extracted text.
{
"district": "R-2",
"district_name": "Two-Family Residential",
"use_status": "legal_nonconforming",
"use_described": "three-unit residential",
"citations": [
{ "code": "Example Zoning Ordinance", "section": "155-12.3",
"cited_for": "permitted uses in R-2" },
{ "code": "Example Zoning Ordinance", "section": "155-40.1",
"cited_for": "continuation of nonconforming uses" }
],
"open_violations": "none of record",
"variances": [
{ "case_number": "ZBA-2019-044", "granted": "2019-06-11",
"subject": "side yard setback" }
],
"speaks_as_of": "2026-04-02",
"signed_by": "Zoning Administrator"
}A district code such as R-2 is meaningless without the jurisdiction. Every municipality invents its own, and R-2 in one town is not R-2 in the next one along. Store the issuing jurisdiction on the same record and never build a cross-property comparison keyed on the bare district code.
As-of dates and the letter’s own disclaimers
The letter speaks as of its date, and it usually says so. Ordinances are amended, enforcement actions open, and a letter from eighteen months ago describes a state of affairs that may no longer hold. Record speaks_as_of as a first-class field, separate from any date the document was received or scanned, and let downstream consumers age it.
Then extract the disclaimers rather than discarding them as boilerplate. Typical qualifications are that the statement relies on information supplied by the requester, that it does not constitute a survey, that it does not certify building-code compliance, or that it is void if the use changes. Each of those narrows the scope of the conclusion you just extracted, and a downstream system that has the conclusion without the qualification is more confident than the document is. Keep them as a list of verbatim sentences with a short label each.