How we build a record
A record is a set of fields copied from one official page, plus the date we last confirmed that page still says what we stored. Nothing is inferred, averaged or filled in from a similar programme.
The three layers we read
Sources sit in three layers, and the layer decides how much of a record can be trusted to arrive unattended. Layer A is a machine-readable federal feed: fields arrive already separated, so a sweep can update a record without a human reading it. Layer B is a state agency that publishes structured listings with its own vocabulary, which has to be mapped once and then checked. Layer C is a portal page written for people, where the fields exist only as prose and every one of them has to be located before it can be stored.
The layer travels with the source rather than with the record, and it is published on the data sources page beside each feed. It is the honest predictor of how quickly a change on the grantmaker's side reaches your screen: a layer A change can land in the next sweep, a layer C change can wait for a human.
One record, one source page
Each record points at exactly one canonical page — a state portal listing, a federal opportunity notice, or a foundation filing. When two pages disagree, we store the one the grantmaker publishes and link it. We do not merge two sources into a single record, because a merged record cannot be cited.
That rule costs us coverage and we keep it anyway. A record assembled from three pages looks more complete and cannot be checked against anything, which makes the verification date on it meaningless.
How a field is extracted
A layer C page is read by a language model against a fixed field list, and the model is given one instruction that matters more than the rest: return nothing rather than a guess. A field the page does not state comes back empty and is stored empty. The model is never asked what an amount is likely to be, never asked to normalise a range into a midpoint, and never asked to infer eligibility from the tone of a paragraph.
Extracted fields are sampled by a person before they go live, and a field the extraction was not confident about is published as Not stated rather than as a value with a caveat. A caveat sitting beside a number does not survive being copied into a grant application; an absent number does.
The two money fields are kept apart at every stage. A programme pool and a per-award ceiling are separate columns in the database, separate labels on the page and separate keys in the structured data. They routinely differ by more than an order of magnitude, and reading one as the other is the most expensive mistake in grant research.
Three states, three sentences
An empty field is not one thing. Three different situations produce one, they mean different things to an applicant, and the site uses a different sentence for each.
None of the three is ever rendered as a blank cell, a dash or a zero. A zero is a claim — that the programme requires no match, that the ceiling is nothing — and it is a claim no source made.
What verification means
A record is verified when a check has confirmed the live source page still says what we stored. An automated check compares the stored fields against the page. A human review is used when the page changed structurally or a user disputed a field. Both carry a date and a method; neither is ever expressed as a percentage of confidence.
That check is published on the record as a stamp, and the stamp is four facts and nothing else — when, against which source domain, by which method, and a link to the page we read:
- Last verified
- 2026-08-29
- Source
- grants.ca.gov
- Method
- Automated check
| Record kind | Recheck window | Usual method |
|---|---|---|
| State portal, deadline inside 30 days | 7 days | Automated |
| State portal, deadline beyond 30 days | 30 days | Automated |
| Rolling or ongoing | 30 days | Automated |
| Closed record kept for history | 180 days | Automated |
| Disputed field | 1 business day | Human review |
A record past its window reads Not confirmed. It is never removed and never silently re-dated.
A sweep that cannot reach a portal is not evidence that its grants closed. A record is only marked closed after three consecutive failures spanning at least 48 hours, and when a whole source fails past a threshold the sweep stops and changes nothing at all. An outage on our side must never look like a deadline on yours.
Where the data lags
Foundation records derive from the most recent 990-PF filing available, which lags 12–18 months. Every foundation figure is therefore published with its filing year attached, and a funder page with no parsed filing shows no giving total at all.
State portals update continuously and carry no filing lag, but they do republish pages without changing a visible date — which is why our check date matters more than the page’s own timestamp.
What we will not do
Corrections
Tell us a field is wrong and the record is rechecked within one business day. Write to corrections@granttrove.com with the record’s address; the source link on the record page is the fastest thing to send with it.
If the source page has changed, the record is updated and the check date moves. If the source page has not changed, we correct our stored field and say so in the record’s history. Either way you get the answer, not a ticket number.