Where every figure on this site comes from. Which ones a script re-checks on every change, which ones nothing watches, and how much weight to put on each.
This site says Elmira's property taxes fall unfairly. That claim is only worth something if the numbers behind it are right.
Until now you had no way to test that. The explanations lived in code comments that nobody outside the project reads. So this page sets out how a figure is built, from the state record it starts as to the sentence you read. It also says plainly which figures are checked automatically and which are not, because that difference matters more than anything else here. Where the method itself is a judgment call, that is the other page: known limits and judgment calls.
Loading the source map…
A green outline means a script re-checks that page's figures on every change. An amber one means nothing does. Click any page to jump to its recipe below. This map is drawn from the same file the recipes come from, so it cannot drift from them.
When you open the assessment page, your browser downloads jcurve.json and draws the chart from whatever it finds there.
Re-run the script with newer sales, and every chart reading that file changes on its own. No one has to notice, remember, or retype anything.
When someone writes "38.9% of the city's assessed value is off the tax rolls" into a paragraph, that figure is fixed. It was right the day it was typed.
If next year's roll says 41%, the chart beside it will move and the sentence will not. The page then contradicts its own graph, and nothing about it looks wrong.
Nearly every page here mixes both. So the question worth asking about any figure on this site is not "where did it come from" — that is in the footer of every page. It is "is anything still watching it?" For most of the site, the answer is no. The rest of this page is about that split.
For these, a change is checked by a script that actively tries to prove the page wrong. Here is what it does.
This is not a lower standard of sourcing. Every figure on these pages was worked out from the same records and is cited in the page footer. The difference is what happens afterwards.
So the honest description of a figure on one of these pages is: correct when written, sourced, and unwatched since. That is a good deal weaker than the guarded pages, and you should read it that way.
| Page | Charts drawn from data | Prose checked by a script |
|---|---|---|
| Checked in full | ||
| Homepage45 figures from seven studies, plus both hand-drawn charts | no | yes |
| Assessment regressivity | yes | yes |
| Reassessment | yes | yes |
| Zoning, Explained | yes | yes |
| IDA Overview | yes | yes |
| IDA Projects | yes | yes |
| IDA & Elmira | yes | yes |
| IDA-Owned Property | yes | yes |
| The IDA Board | no | yes |
| Charts live from data, prose unwatched | ||
| City Fiscal Health | yes | no |
| Frozen Assessments | yes | no |
| City Budget Explorer | yes | no |
| County Budget | yes | no |
| City–County Relationship | yes | no |
| The Water Board | yes | no |
| Multi-Year Trends | yes | no |
| Fair-Share Map | yes | no |
| Maintenance Rebate | yes | no |
| The Long Decline | yes | no |
| Tax Value per Acre | yes | no |
| All figures typed, nothing watching | ||
| PILOT Agreements | no | no |
| Fiscal Decoder | no | no |
| Why It Matters | no | no |
| 1940 Redlining Map | no | no |
| Council Minutes | no | no |
| Contact Your Officials | no | no |
| The Talk | no | no |
| Retired pageRedirects to Tax Value per Acre | no | no |
| Glossary | no | no |
| Data & SourcesScanned for retracted figures only | no | no |
Each one says where that page's data starts, which script works out its figures, what gets filtered out on the way, and which choices were judgment rather than arithmetic. They carry rules and never figures — “drop any sale under $10,000” stays true when the data is refreshed, where a count would quietly go stale. Where a rule is enforced by a line of code, the recipe names that line, and a check fails the build if the code stops saying what the recipe claims.
Every source is downloadable twice over: the publisher's own copy, and a gzipped copy of the exact file this site used. Both matter, because the publisher's version moves on and one of them is a search form rather than a fixed file.
Loading the recipes…
Throughout this, the ratio means one thing: a home's assessed value divided by the price it actually sold for. A ratio of 0.60 means the assessor valued the house at 60% of what a buyer paid. The higher the ratio, the more tax you pay per dollar your home is really worth.
New York publishes the price of every property sale through its SalesWeb service. We keep
a copy at data/raw/SaleswebExtract.csv, covering all of Chemung County.
Keep only the City of Elmira, only ordinary single-family houses, only arm's-length sales, only 2018 to 2025. Drop anything under $10,000 and any ratio above 5.0. Those are almost always data errors or transfers between relatives.
What is left: 1,689 sales
Assessed value divided by sale price, using the assessment that was on the roll at the time of that sale, not a later one.
The middle ratio of all 1,689: 0.631. The typical Elmira house was assessed at about 63% of what it sold for.
Group the sales into price bands. Homes that sold under $50,000 had a middle ratio of 1.175. Homes at $150,000 and up had 0.457.
Cheap homes were assessed above their sale price. Expensive ones at less than half. The raw gap: 2.573×
Sorting by sale price puts sale price in two places at once: along the bottom of the chart, and underneath the ratio. A house that happened to sell cheap lands in the low band and gets a high ratio, for the same reason. The curve bends before any unfairness is involved.
Sorting by assessed value instead does not fix this. It tilts the error the other way, because assessed value is the top of the ratio. So neither version can be measured against a flat line.
Find every house that sold twice within three years — 510 pairs. How far apart those two prices are tells you how much a single sale price strays from what a house is really worth.
Then simulate a city with exactly that much randomness in its prices and no unfairness at all. Run the same method on it. It still produces a gap of 1.271×. That is the part we caused, now measured.
2.573 ÷ 1.271 = 2.02×
That is what the page publishes, and what it rounds to "about twice as much". The bigger, better-sounding 2.573× is not claimed anywhere, because part of it was never real.
All of it goes into jcurve.json. The assessment page downloads that file and draws both curves, raw and corrected.
audit_jcurve_figures.py redoes every step above from the raw sales file each
time it runs. It also refuses to let a price band be drawn if too few sales fall into it,
which is the mistake that produced the first version of this analysis.
That first version pooled all eleven towns in the county together. Those towns assess on very different scales, so differences between towns were being read as unfairness within Elmira. It was wrong. We withdrew it, and the check now makes sure those old figures cannot quietly reappear anywhere on the site.
The 2.0× came from 1,689 rows in a file. This came from an agreement signed in 1994 between the city and a college. There is no table to recompute, no roll to filter, nothing for a script to rebuild.
The City Chamberlain's office confirmed the amount and said it has not changed since the 1994 agreement.
The payment appears in the 2026 adopted budget under account 412890, "Other General Department Income". Notably it is not booked with the PILOTs at all, which is itself part of the story.
The executed 1994 agreement itself. A records request for it is open, and the PILOT page says so in plain sight rather than implying we have read it.
No script can confirm this figure, and adding one would not help. Verifying it means a person reading a budget PDF, or obtaining the agreement.
If the city renegotiated the payment tomorrow, nothing in our pipeline would notice. The sentence would sit there being wrong until someone read the next budget.
Naming the source precisely enough that you can go and check it yourself, and saying out loud which part we have not seen. That is a weaker guarantee than the 2.0× has, and it should be read as weaker.
The state files city parcels under the surrounding town. Ask the data for "Elmira" and you also get about 3,800 parcels that pay no city tax at all. On the 2025 roll that is 3,161 properties in the Town of Elmira, plus 625 utility assessments filed with the town. Including them makes the city look wealthier and less taxed than it is.
Every script now selects city parcels through one shared function. If it cannot find them it stops with an error, rather than quietly handing back an empty result.
Elmira's official code is 070400. Read from a saved file it arrives as the number 70400. Read from the state's live service it arrives as the text "070400". Compare the wrong one and you match nothing at all — no error, just zero results.
One shared function now converts both forms before anything is compared.
Elmira's rate can be given per $1,000 of assessed value or per $1,000 of market value. They are different numbers measuring different things.
Averaging them once put a miscalculated rate on this site for a year. Any page showing a rate now has to say which of the two it means, in text you can see.
The host refuses any single file above 25 MB, so the maps and data files are built to stay under it. That is why the site sends you small summary files rather than the whole roll.
The full roll is public. It is linked on the sources page, straight from the state.
There are several places where a careful person could do this differently and reach a different answer. The state's published equalization rate for the whole roll says 56%, while the ratio measured from house sales lands anywhere from 46% to 63% depending on which sales and whose dollars. The 3D maps join parcel shapes recorded in 2021 to assessments from 2025. Sorting homes by sale price bends the assessment curve before any unfairness is involved.
Those have a page of their own: Known limits and judgment calls. Each entry says what we chose, why, what the choice costs, and what would change if you chose otherwise. It also lists the figures we have withdrawn, and what this site does not claim. If you are here because we asked you to check our work, start there.
Still to come: a figure-by-figure list covering the whole site, built from the checking scripts themselves.