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.

$1,250,000
The source states a value and we have stored it. Figures are typeset in mono with tabular figures so a column of them aligns digit for digit.
Not stated
The source page is silent on this field. This is the grantmaker's silence, not an omission of ours, and it is often meaningful — an unstated application effort usually means no rubric was published.
Not confirmed
We hold a value but its recheck window has passed, so we are no longer willing to assert it is current. The stored value stays visible with its old check date attached rather than disappearing.
Not linked yet
The field exists on the source but we have not connected it. This is our gap. It is set at the same size and weight as a filled field so it cannot be mistaken for a missing value on the grantmaker’s side.

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:

Verification record
Last verified
2026-08-29
Source
grants.ca.gov
Method
Automated check
View the official source page
The date
Always written 2026-08-29, never “recently” or “this week”. A relative date cannot be cited and quietly ages into a lie.
The source
The bare domain we checked against, and a link to the exact page. If the link is dead, the record is marked for recheck rather than left standing.
The method
Automated check or human review. The two are never merged into one word, because they are not equally strong evidence.
Recheck due
Past its window the stamp drops its colour and says so. It never re-dates itself, and the record is never quietly removed.
Recheck windows5 rows
Record kindRecheck windowUsual method
State portal, deadline inside 30 days7 daysAutomated
State portal, deadline beyond 30 days30 daysAutomated
Rolling or ongoing30 daysAutomated
Closed record kept for history180 daysAutomated
Disputed field1 business dayHuman 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

Fill a gap
No inferred amounts, no averaged ranges, no eligibility carried over from a similar programme. An inferred value is indistinguishable from a real one once it is on the page.
Publish a confidence score
A record was checked on a date by a method, or it was not. A percentage would imply a measurement nobody made.
Paywall the core fields
Amounts, deadlines, eligibility, the source link and the verification stamp are readable without an account, permanently. The paid tiers add alerts, tracking and workspaces, never access to a field.
Estimate a rival’s numbers
Comparison pages carry only figures we can source to a public page, each with the date we checked it. Where we would need a number we have not measured, the page says so.

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.