Why We Built Our Grants Data from Raw 990 Filings
Four capability gaps that follow from the shape of a grants directory — cost, network analysis, rigid taxonomies, and no arbitrary SQL — and what we did instead.
Every US grants dataset starts in the same place: Form 990, 990-EZ and 990-PF, e-filed to the IRS and released as public XML. Nobody has private access to it. So when a team decides whether to license a grants directory or parse the filings themselves, the question is never "who has the data" — it is what shape you need the data to be in.
We parse the filings. That was not a judgment that the directories do their job badly; several do it well, and for the job they are built for — a researcher looking up funders one at a time — a good directory is the right tool and building your own would be absurd. It was a judgment that four specific things we needed follow from the shape of a directory, and no amount of quality inside that shape produces them.
This is an engineering post about those four things. It is deliberately not a comparison table, because the honest version of that table would show a lot of parity.
The starting point: the data is public, the structure is not
The IRS publishes e-filed returns as XML. Anyone can download them. What you get is not a database — it is millions of individual filings with inconsistent field usage, schema changes across years, organizations that appear under several names, grants recorded as free text, and recipients identified by whatever the filer chose to type.
Turning that into something queryable is the actual work: parsing every filing, normalizing financial fields, resolving recipient names to organizations, and reconciling an entity that appears as "St. Mary's Food Bank," "St Marys Food Bank Alliance" and an EIN across three different funders' filings.
Everything in our warehouse derives from those public filings plus our own extraction. An audit in June 2026 confirmed zero directory-derived data anywhere in the pipeline — service-area assignments come only from the filing itself and the foundation's own website. That mattered to us for a practical reason rather than a principled one: data you licensed is data you cannot rebuild, redistribute, or reason about when it turns out to be wrong.
One thing worth being straight about, because it affects how far back any analysis can reach: e-filed coverage runs from fiscal year 2017, but it is patchy before 2020. Mandatory e-filing phased in, so early years under-represent smaller filers and paper filers are simply absent. Any trend line that starts in 2017 is measuring the growth of e-filing as much as the growth of giving. We hold 2017 onward and treat 2020 as the point where the corpus becomes dense enough to trust for year-over-year comparison.
Gap one: the cost of access at scale
Directory pricing is built around seats and lookups, because that is the usage pattern directories are for. As of mid-2026, Candid's Foundation Directory ran a free tier with Premium at $219/month or $1,199/year, Ultimate at $1,699/year, and custom enterprise pricing. Instrumentl ran roughly $179–$499/month depending on plan. Those are reasonable prices for a research seat, and prices change without announcement, so verify before you buy.
They stop being reasonable when the access pattern is not a seat. If a due diligence check runs automatically on every applicant in every round, and a portfolio analysis touches thousands of organizations at once, you are not making lookups — you are running a workload. Per-seat pricing does not describe that, and the enterprise conversation it triggers is priced for a different kind of buyer.
The alternative is to hold the corpus yourself, where the marginal cost of a query is compute rather than licence. That is why our own API can offer a free key at 50 calls a month and a Pro tier at 10,000, and why analysis inside the product does not meter against an external vendor's quota. It is a consequence of owning the pipeline, not a pricing strategy we could have chosen otherwise.
Gap two: no network analysis
This is the gap that decided it.
A directory answers questions about one node: who is this funder, what did they give, to whom. That is the interface. What it cannot naturally answer is questions about edges — and in philanthropy the edges carry most of the interesting information.
Concrete examples of edge questions:
Who funds alongside whom? If a foundation is considering an applicant, the most useful single fact is often which other funders already back that organization, and whether those funders are ones it respects. That is a co-funder graph: derived by taking every grant in the corpus, grouping by recipient, and computing which funder pairs co-occur more than chance. You cannot look it up. It has to be computed across the whole dataset at once.
Which organizations sit at the center of a field? Recipients that many otherwise-unconnected funders converge on are a different kind of signal from recipients that one funder supports heavily. That is a centrality measure over the bipartite funder–recipient graph.
Where does money move more than once? Regranting intermediaries and donor-advised fund flows mean a dollar can appear as a grant several times before reaching delivery. Seeing that requires tracing paths, not reading rows.
Which boards share people? Governance rosters appear in the filings. Resolving them into a person graph reveals board interlocks between funders and grantees — which is both a warm-introduction path and a conflict-of-interest signal a funder is obliged to notice.
Every one of these is a graph computation over the complete corpus. A per-organization API cannot serve them at any price, because the answer is not stored in any organization's record. It only exists once you hold everything.
Gap three: rigid taxonomies
US nonprofit classification runs on NTEE codes — a fixed hierarchy assigned largely at registration and rarely revisited. They are genuinely useful, and we use them. They are also a blunt instrument for the question funders actually ask.
NTEE codes describe what an organization is, not what a grant does. A youth mentoring program, a school-based mental health service and an after-school food program may sit in three different top-level categories while addressing one problem in one neighborhood. Conversely, two organizations sharing a code can do unrelated work at unrelated scale.
The deeper issue is that any fixed taxonomy encodes the questions someone anticipated. A funder rethinking its portfolio is usually asking a question the taxonomy did not anticipate: what themes actually run through our giving, as opposed to the categories we file it under?
Answering that needs a representation that is continuous rather than categorical. We embed organizations from their own mission and program text, then cluster in that space — so themes emerge from what organizations say they do, and two grants can be near each other without sharing a code. It also supports look-alike search, which is the practical form of the question: given these twelve grantees, who else looks like them? A code filter cannot answer that, because "like them" is not a code.
None of this replaces the taxonomy. NTEE is stable, auditable and shared across the sector, which matters for reporting. It sits alongside a semantic layer rather than being replaced by one.
| Question | A fixed taxonomy | A semantic layer |
|---|---|---|
| What sector is this organization in? | Direct answer | Imprecise |
| Which of our grantees resemble each other? | Only if they share a code | Direct answer |
| What themes run through our portfolio? | The themes you filed under | Themes from what organizations describe |
| Who else looks like these twelve grantees? | Not expressible | Direct answer |
| Reporting to a board with stable categories | Direct answer | Needs mapping back |
Gap four: no arbitrary queries
The fourth gap is the most mundane and, day to day, the most limiting: you cannot ask a directory a question its designers did not build a screen for.
Directory interfaces are filter-and-browse. Their APIs expose documented endpoints with documented parameters. Both are perfectly good for the queries anticipated. Neither helps when an analyst wants something specific and slightly odd:
Which funders in this metro increased grantmaking for three consecutive years while their asset base fell?
Among organizations receiving both foundation grants and federal awards, which have foundation support declining while federal support grows?
Which recipients appear in more than five funders' filings but have never received more than $25,000 from any of them?
Those are all single SQL statements against a warehouse. Against a directory, each is either impossible or a bulk export followed by a spreadsheet reconstruction of the join you were denied.
So we exposed the warehouse to SQL directly, alongside the Candid-shaped REST endpoints — the compatibility layer exists so an existing client can point at it without rewriting, not because the endpoints are the interesting part. And because most people with a good question are not going to write the query, there is an "Ask the data" chat that writes its own SQL against the same warehouse and shows you what it ran. The showing matters: a generated answer you cannot inspect is worse than no answer, because you cannot tell whether the question was understood.
One substrate, several front doors
Datasets in this market list their sources, and those lists get long: filings, news, surveys, self-reported profiles, partner feeds. The lists are accurate. They are also easy to misread, because they describe inputs rather than proportions.
For US grants data specifically, the overwhelming majority of records in any dataset — ours included — come from one place: e-filed 990, 990-EZ and 990-PF returns. Supplementary sources add contact details, announcements and self-reported context on top. They do not replace the filings, because nothing else records grant-level detail at national scale.
Two things follow, and both are usually attributed to the wrong cause.
Filing lag is a property of the data, not of a vendor. A 990 arrives months after the fiscal year it covers, sometimes more than a year. Every dataset built on those filings inherits that lag identically — it is not something one vendor has solved and another has not. Treat a claim of current-year grant coverage as a claim about announcements, which are a different and much thinner record.
But the lag only ever applied to other people's grants, and that is the half of the question people keep answering. Your own grantmaking is not lagged at all. You made those decisions; the awards, the pipeline, the payment schedule and the monitoring reports exist in your system the moment they happen. There is no filing to wait for.
Which makes the interesting question not "whose corpus is fresher" but whether the two halves sit in the same place. A directory holds the lagged public record and can never hold your live portfolio. A grants management system holds your live portfolio and has no view of the field. Each answers half of most real questions.
Holding both is what makes the lag stop mattering, because the current half comes from you and the historical half comes from the filings:
- What share of this grantee's income are we? Your live award, divided by their most recent filed total revenue. Neither number is available to a system holding only one side, and the answer changes how you think about exit and dependency.
- Who else funds the organisations we are funding right now? Your current portfolio joined to the co-funder graph — a live list of grantees against a computed view of the field.
- Are we the only one here? Whether a grantee's other funders have been dropping away, which is a filed-record question asked about a live relationship.
The lag is real and worth stating plainly. It is also a constraint on the historical half of an analysis whose current half you already own.
The number of listed sources says little about depth. What determines whether a dataset can answer a question is whether the filings were parsed to grant level, whether recipients were resolved to entities, and whether the result is queryable. A long source list is compatible with all three and with none of them.
We are not making a claim about anyone else's data here — every dataset in this market draws on the same public corpus, and that is the point. The differences worth comparing are what was done to it.
The part that is genuinely hard: entity resolution
The single largest piece of work between raw filings and a usable dataset is deciding which strings refer to the same organization.
Recipient names are free text typed by the filer. The same grantee appears as "St. Mary's Food Bank," "St Marys Food Bank Alliance" and an EIN across three funders' filings in the same year. Resolution runs normalized names against the e-filer set and the Business Master File, disambiguated by ZIP, on an accuracy-first rule: where the match is not confident, drop it rather than guess. A wrong link is worse than a missing one, because it silently corrupts every aggregate built on top.
The harder half is everything with no US EIN at all. US foundations give to foreign NGOs, overseas universities, UN bodies, foreign governments and ministries, and individuals for relief and scholarships. Those grants carry a name and nothing else, and in most datasets they sit unresolved — retained as strings, invisible to any per-entity view, and absent from country-level totals.
We resolve them. Foreign addresses are parsed out of the RecipientForeignAddress and ForeignAddress blocks the parser previously ignored, giving country codes; grants are aggregated into country flows; and a conservative deterministic clusterer resolves non-EIN recipients into stable entities with their own pages. Probabilistic calibration of that clusterer is still open work, so the current pass is deliberately conservative — it under-links rather than over-links.
The same machinery answers a question funders ask constantly and datasets rarely support: is this applicant one organization or several? Affiliated filers under shared control are detected as families, with a corroboration requirement enforced before any two filers are treated as the same entity. That matters for scale correction — a family's combined giving is not the sum of what each filer appears to give if you count them separately — and for due diligence, where two applications can turn out to be one applicant.
What a directory genuinely does better
Three things, and none of them is the data.
Curation. Directories employ people who verify contact details, capture program officer names, record application deadlines, and confirm whether a funder accepts unsolicited proposals. Almost none of that appears in a 990. If your question is "who do I contact and when is the deadline," a filing-derived dataset is the wrong tool.
Track record. This is the honest one. Established directories have been relied on for decades, are embedded in how development teams already work, and have institutional trust that no amount of pipeline quality substitutes for. If you need a source your board already recognizes, that is a legitimate reason to choose one, and we would not argue otherwise.
Not being your problem to maintain. Schemas change, the IRS revises releases, adapters break. A licence has no maintenance burden; a pipeline is permanently somebody's job. We took that cost on because the capabilities above required it, not because it is free.
So which should you use?
If you are a program officer or prospect researcher looking up funders one at a time, wanting contact details and deadlines, a directory is the right tool and the pricing is fair for the job.
If your questions are about the shape of a field rather than the profile of an organization — who funds alongside whom, what themes run through a portfolio, which organizations resemble which — then you need the corpus, and the interface you need is a query language.
Most foundations of any size eventually want both, which is the honest conclusion. They are complements shaped by different assumptions about what a question looks like.
Frequently asked questions
Is IRS 990 data free?
Yes. The IRS publishes e-filed returns as public XML and anyone can download them. What costs money is the work of turning millions of inconsistent filings into something queryable — parsing, normalization, entity resolution and maintenance.
How far back does e-filed 990 data go?
E-filed coverage runs from fiscal year 2017, but it is patchy before 2020 because mandatory e-filing phased in over several years. Trend analysis starting in 2017 partly measures the adoption of e-filing rather than changes in giving.
Why can't a grants directory do network analysis?
Because a directory is organized around the record for a single organization, and network questions are about relationships between organizations. A co-funder graph or a board interlock does not live in any one record — it only exists once the whole corpus is held together and computed over.
What is wrong with NTEE codes?
Nothing, for what they are. They describe what an organization is, are stable and are shared sector-wide. They are simply a poor fit for questions about thematic similarity, because two organizations addressing the same problem often sit in different codes and two sharing a code often do unrelated work.
Can I run my own SQL against Plinth's grants data?
Yes. The warehouse is exposed to SQL alongside Candid-shaped REST endpoints, and there is an "Ask the data" chat that generates SQL and shows the query it ran. API access uses a free key at 50 calls a month, with a Pro tier at 10,000.
Does Plinth have more grant data than Candid?
No, and that would be the wrong claim. Both derive from the same public IRS filings. The difference is the shape the data is held in and the questions that shape makes answerable — not the number of records.
Is one grants dataset more current than another?
Not materially, if both are built on 990 filings — a return arrives months after the year it covers, and every dataset derived from them inherits the same lag. The more useful question is which half of your analysis needs to be current. Field-wide history comes from filings and is lagged for everyone; your own pipeline, awards and monitoring are live because they originate with you. A platform holding both can answer questions that a dataset alone cannot, such as what share of a grantee's filed income your current award represents.
Does listing more data sources mean a deeper dataset?
Not on its own. For US grants the overwhelming majority of records in any dataset come from the e-filed 990 corpus; other sources add contact details, announcements and self-reported context on top rather than replacing it. What determines depth is whether filings were parsed to grant level, whether recipients were resolved to entities, and whether the result is queryable.
What happens to grants to organizations with no EIN?
In most datasets they remain unresolved name strings — retained but invisible to per-entity views and absent from country totals. We parse the foreign address blocks for country codes, aggregate grants into country flows, and resolve non-EIN recipients (foreign NGOs, overseas universities, UN bodies, governments) into stable entities with their own pages. The clusterer is deliberately conservative, so it under-links rather than over-links.
Should we build our own 990 pipeline?
Almost certainly not, unless the network, semantic and arbitrary-query capabilities above are central to your product rather than useful to your research. For research use, licensing a directory is cheaper than the standing maintenance cost of a pipeline.
Recommended next pages
- Candid Foundation Directory alternatives — An honest comparison of the tools in this market
- Co-funder networks — What the co-funder graph reveals in practice
- NTEE codes explained — The taxonomy, and what it is good for
- Foundation portfolio analysis — Reading a portfolio as a whole rather than grant by grant
- What your 990-PF reveals — Reading a single filing closely
Last updated: August 2026