Follow federal grant money past the prime recipient. Every FFATA sub-grant from FY2025 onward, keyed by prime grant FAIN, CFDA assistance listing, or UEI.
Federal grant money frequently does not stop at the prime recipient. A state education department, a university, or a research institute takes a large assistance award and passes most of it down to sub-recipients. Above the FFATA reporting threshold that pass-down is public, reported by the prime under the same statute that governs sub-contracts, and republished by USAspending. This API exposes those records as queryable JSON keyed by prime grant FAIN, CFDA assistance listing, awarding agency, or UEI, on either side of the flow. Coverage today is FY2025 onward (action dates from October 2024), roughly 600,000 sub-grants worth about $265B. This is the assistance half of FFATA. The contract half lives at the Federal Subaward API, and the two are kept apart on purpose: a sub-grant is not contracting revenue, and mixing the two would misstate both.
The Federal Funding Accountability and Transparency Act of 2006 (FFATA) requires prime recipients of federal awards above the reporting threshold to disclose the first-tier sub-recipients they pass money to. That obligation runs on both sides of federal spending: procurement (a prime contractor sub-contracting work) and assistance (a prime grant recipient sub-granting money). The assistance side is the sub-grant. Reporting flows through the FFATA reporting system and is republished by USAspending.gov as machine-readable records.
Each row is one FFATA report on the assistance side. Fields cover the sub-recipient (UEI, name, parent name, state, country), the prime grant recipient passing money down (UEI, name, state), the underlying prime grant (FAIN, CFDA assistance listing number and title, awarding agency and sub-agency, the prime grant amount), and the sub-grant itself (action date, amount, description, place of performance).
This is the only public dataset that maps prime-grant-to-sub-recipient flow at the individual-payment level. Prime grant award data tells you who won the grant. It does not tell you who the prime passed the money to downstream. FFATA does. And because grant money frequently passes through a large intermediary before it reaches the organizations that actually spend it, the sub-grant layer is where the real recipients show up.
This is the single thing that trips up an integrator moving from /subawards to /subgrants. A sub-contract hangs off a prime contract and is identified by that contract's PIID. A sub-grant hangs off a prime grant, which has no PIID at all, and is identified by its FAIN (Federal Award Identification Number) plus its CFDA / assistance listing number. So prime_award_piid is not returned by this endpoint, fain replaces piid as the filter, and the program dimension is the CFDA assistance listing, not a NAICS code.
/subawards (contracts) |
/subgrants (grants) |
|
|---|---|---|
| prime award is a | contract | grant |
| prime identified by | prime_award_piid |
prime_award_fain |
| program code | NAICS | CFDA / assistance listing |
subaward_type |
sub-contract |
sub-grant |
Same statute, same reporting form, opposite side of the house. The two datasets live on separate endpoints because they answer different questions, and unioning them would misstate both. If you do union them, subaward_type is the discriminator.
If you only have prime grant award data, three real questions go unanswered:
Free on any GovCon API key. Same Authorization: Bearer <key> header as everything else.
Filter-based search (at least one of prime_uei, sub_uei, fain, agency, cfda, sub_name is required):
GET https://govconapi.com/api/v1/subgrants/search
Everything one prime grant passed down, plus a roll-up (keyed on FAIN, because a grant has no PIID):
GET https://govconapi.com/api/v1/grants/{fain}/subgrants
Search paginates like the rest of the API (limit 1-250, offset ≥ 0, exact total) and returns the same response envelope (data, pagination, filters_applied, window, _sources). Sort keys: subaward_action_date (default), subaward_amount, subaward_sam_report_last_modified_date; sort_order is asc or desc (default desc). The fields parameter takes lean (default), full, or a comma-separated column list. Full parameter and field reference in the API guide.
The /grants/{fain}/subgrants roll-up is the assistance mirror of the subaward_rollup on /contracts/{piid}. It is not date-windowed: it is a lookup by the prime award's identifier, not a search, so you get the full history carried for that FAIN. It returns 404 when nothing was sub-granted under the FAIN, which is common (most grants are never sub-granted, and FFATA only requires reporting above a dollar threshold). That 404 is not an error in your request.
The search and roll-up endpoints return a lean row by default; use ?fields=full for the full record or ?fields= with a column list for a narrower projection. A single sub-grant row from /subgrants/search:
{
"subaward_sam_report_id": "…",
"subaward_number": "…",
"subaward_type": "sub-grant",
"subaward_action_date": "2026-04-08",
"subaward_amount": 403098.00,
"subaward_description": "SECURING HIGH-DENSITY URBAN AIRSPACES",
"subaward_sam_report_last_modified_date": "2026-05-13T04:00:00+04:00",
"prime_award_fain": "80NSSC24M0070",
"prime_award_cfda_numbers_and_titles": "43.002: AERONAUTICS",
"prime_award_amount": 4000000.00,
"prime_award_base_transaction_description": "SECURING HIGH DENSITY URBAN AIRSPACES",
"prime_award_awarding_agency_name": "National Aeronautics and Space Administration (NASA)",
"prime_award_awarding_sub_agency_name": "National Aeronautics and Space Administration",
"prime_awardee_uei": "ECR5E2LU5BL6",
"prime_awardee_name": "GEORGE WASHINGTON UNIVERSITY (THE)",
"prime_awardee_state_code": "DC",
"subawardee_uei": "SWBKVD2EKUH7",
"subawardee_name": "ROCKWELL COLLINS, INC.",
"subawardee_parent_uei": null,
"subawardee_parent_name": null,
"subawardee_state_code": "IA",
"subawardee_country_code":"USA",
"subaward_pop_state_code":null,
"usaspending_permalink": "https://www.usaspending.gov/award/ASST_NON_80NSSC24M0070_080/"
}
Read that row carefully: a defense contractor (Rockwell Collins, in Iowa) receiving $403,098 of a $4M NASA aeronautics grant, passed down by a university in DC. It is real, and it is not contracting revenue. Two field notes carry most of the porting friction from the contract side: prime_award_fain replaces the prime_award_piid that does not exist for assistance, and the assistance extract has no prime_award_project_title, so prime_award_base_transaction_description is its equivalent.
The /grants/{fain}/subgrants endpoint wraps the same row shape with a subgrant_rollup aggregating the whole flow under one prime grant:
{
"fain": "251PA305N1199",
"prime_awardee_uei": "HBNJBZWWLE29",
"prime_awardee_name": "PA DEPARTMENT OF EDUCATION OMB",
"prime_award_amount": 983041517.00,
"subgrant_rollup": {
"subgrant_count": 20060,
"total_subgranted": 833887909.00,
"distinct_subrecipients":1557,
"first_action_date": "2024-11-13",
"latest_action_date": "2026-04-24"
},
"data": [ { "subaward_amount": 8475891.00,
"subawardee_name": "SCHOOL DISTRICT OF PHILADELPHIA",
"prime_award_cfda_numbers_and_titles": "10.555: NATIONAL SCHOOL LUNCH PROGRAM",
"prime_award_awarding_agency_name": "Department of Agriculture (USDA)", "…": "…" } ],
"pagination": { "limit": 2, "offset": 0, "total": 20060, "has_next": true },
"_sources": ["usaspending_ffata"]
}
Read that as: the Pennsylvania Department of Education passed $833,887,909 down to 1,557 sub-recipients across 20,060 sub-grants under a single $983M prime award. The subgrant_rollup block gives you those totals in one call; data[] is the sub-grants themselves, largest first, each with the same field set as a /subgrants/search row (the example above is abbreviated to the key fields). Full field-by-field reference in the API guide.
On the contract side, subaward dollars are stitched into company and vendor-risk profiles, because a sub-contract is federal contracting revenue that a firm earned as a subcontractor. Sub-grants are deliberately handled the opposite way. A sub-grant is assistance, not procurement. It is not contracting revenue. So sub-grant dollars are kept in their own table and out of the contracting lenses.
/companies/{uei} and /vendor-risk/{uei} report a firm's sub-contract footprint: their subaward figures mean "money earned as a subcontractor." Sub-grants living in a separate table is what guarantees those figures cannot silently blend in assistance dollars. That $403,098 NASA sub-grant to Rockwell Collins never inflates its apparent contracting revenue, because it is never counted there in the first place.
To see sub-grant flow you query the sub-grant endpoints directly, or the per-grant roll-up. The separation is a correctness guarantee, not a missing feature: it is the reason a number labeled "contracting revenue" on this API means only that, and never quietly folds in grant money that happened to reach the same UEI.
Same passthrough philosophy as the rest of our federal data: we do not sanitize source-data structure, because the structure is sometimes the signal. Quirks that have surprised callers moving over from the contract side:
prime_award_fain plus its prime_award_cfda_numbers_and_titles (the CFDA assistance listing). If you are porting code from /subawards, prime_award_piid is gone and fain is the filter; the program dimension is the CFDA number, not a NAICS code.
"subaward_type": "sub-grant", just as every row on /subawards carries "sub-contract". If you union the two datasets, that field is the only reliable discriminator between assistance and procurement.
prime_award_base_transaction_description, which is its equivalent and describes what the prime grant is for.
prime_award_amount ($983,041,517 in the PA example, against $833,887,909 actually sub-granted). Aggregations like SUM(prime_award_amount) GROUP BY prime_awardee_uei will multi-count. Group by prime_award_fain for per-grant sums, or use the roll-up.
subaward_number as a display label rather than a primary key.
Each recipe is the actual workflow for one side of the flow. Copy-paste ready; the Python uses plain requests with no custom client.
Given a prime grant's FAIN, one call returns the whole pass-down plus the roll-up totals.
import requests
KEY = "gca_…"
H = {"Authorization": f"Bearer {KEY}"}
BASE = "https://govconapi.com/api/v1"
def grant_flow(fain):
r = requests.get(f"{BASE}/grants/{fain}/subgrants",
headers=H, params={"limit": 250}).json()
roll = r["subgrant_rollup"]
return {
"prime_name": r["prime_awardee_name"],
"prime_grant_amount": r["prime_award_amount"],
"passed_down_total": roll["total_subgranted"],
"distinct_subrecipients": roll["distinct_subrecipients"],
"subgrant_count": roll["subgrant_count"],
"date_range": (roll["first_action_date"],
roll["latest_action_date"]),
"largest_subgrants": r["data"][:5],
}
# >>> grant_flow("251PA305N1199") # PA Department of Education
# {'prime_name': 'PA DEPARTMENT OF EDUCATION OMB',
# 'prime_grant_amount': 983_041_517.0, 'passed_down_total': 833_887_909.0,
# 'distinct_subrecipients': 1557, 'subgrant_count': 20060, …}
A 404 means nothing was sub-granted under that FAIN, which is common. Handle it as an empty flow, not an error.
Screening an organization by its prime grant awards alone undercounts. Page the search endpoint by sub_uei to see everything it received downstream:
def as_subrecipient(sub_uei):
rows, offset = [], 0
while True:
page = requests.get(f"{BASE}/subgrants/search", headers=H,
params={"sub_uei": sub_uei,
"limit": 250, "offset": offset}).json()
rows += page["data"]
if not page["pagination"]["has_next"]:
break
offset += 250
return {
"subgrant_count": len(rows),
"received_total": sum(r["subaward_amount"] for r in rows),
"paying_primes": sorted({r["prime_awardee_name"] for r in rows}),
}
# Rockwell Collins (SWBKVD2EKUH7) as a sub-recipient includes a $403,098
# slice of a $4M NASA aeronautics grant, passed down by George Washington
# University. That is assistance money; it never counts as contracting revenue.
To scope to one program instead of one firm, swap the filter for cfda=<assistance-listing>, or use agency= for one funder's grants. At least one of prime_uei, sub_uei, fain, agency, cfda, or sub_name is required.
The recipes above use Python for clarity. For one-shot lookups, the same calls in curl:
Everything one prime grant passed down, largest first:
curl -H "Authorization: Bearer $KEY" \
"https://govconapi.com/api/v1/grants/251PA305N1199/subgrants?limit=20"
Every sub-grant a firm received as a sub-recipient:
curl -H "Authorization: Bearer $KEY" \
"https://govconapi.com/api/v1/subgrants/search?sub_uei=SWBKVD2EKUH7&limit=50"
All sub-grants under one assistance listing (CFDA), largest first:
curl -H "Authorization: Bearer $KEY" \
"https://govconapi.com/api/v1/subgrants/search?cfda=10.555&sort_by=subaward_amount&sort_order=desc"
What one prime grant recipient passed down since FY2025, above $100K:
curl -H "Authorization: Bearer $KEY" \
"https://govconapi.com/api/v1/subgrants/search?prime_uei=ECR5E2LU5BL6&date_from=2024-10-01&amount_min=100000"
Every sub-grant funded by one agency's grants:
curl -H "Authorization: Bearer $KEY" \
"https://govconapi.com/api/v1/subgrants/search?agency=NASA&limit=50"
USAspending publishes the same source data this API serves. Their bulk_download interface is the right tool if you want one fiscal year of assistance-subaward CSVs and have a place to load them. Their per-record award API surfaces sub-recipients only through award detail; there is no "search sub-grants by CFDA" and no "roll up everything one prime grant passed down" cursor keyed on FAIN. This API adds those, keyed by FAIN, CFDA assistance listing, and UEI, with the roll-up computed for you. Grant-tracking tools carry assistance data behind paid tiers.
| Approach | Search by prime FAIN | Search by CFDA | Roll-up per prime grant | Reverse lookup by sub UEI | Starting price |
|---|---|---|---|---|---|
| USAspending bulk_download | Manual: download CSV, load, query | Manual | Manual | Manual | Free |
| USAspending per-record API | Partial: by award detail only | No | Partial | Partial | Free |
| GovCon API | Yes (REST) | Yes (REST) | Yes (REST) | Yes (REST) | Free |
The fit: developers and analysts who want sub-grant relationships programmatically, keyed by FAIN, CFDA, or UEI, alongside the rest of the federal award surface, without building the joins and the roll-up by hand. If you only need the bulk download once and have a warehouse to load it into, the USAspending CSV is the right tool. If you need sub-recipient relationships at request time, this is the shortcut.
The sub-grant endpoints (/subgrants/search and /grants/{fain}/subgrants) are free on any GovCon API key. Same key, same Authorization: Bearer header as the rest of the API. Sub-grants are kept on their own endpoints, and out of the paid contracting lenses, on purpose: a sub-grant is assistance, not contracting revenue, so it is served as its own free surface rather than folded into a firm's contract profile.
Coverage today is FY2025 onward (action dates from October 2024), matching the prime /grants layer these sub-grants hang off. As the prime grant coverage extends backward, the sub-grant coverage tracks it, without breaking your integration.
Where does this data come from?
USAspending.gov, the public republication of FFATA reports filed by prime grant recipients on the assistance side. Each record's _sources is usaspending_ffata, and every row carries a usaspending_permalink back to the source award.
How is a sub-grant different from a sub-contract?
A sub-contract is procurement money a prime contractor passes down, identified by the prime's PIID. A sub-grant is assistance money a prime grant recipient passes down, identified by the prime grant's FAIN plus its CFDA assistance listing. Same statute (FFATA), opposite side of the house. The contract half is the Federal Subaward API.
Why is there no PIID?
Grants have no PIID; a grant is identified by its FAIN (Federal Award Identification Number). So prime_award_piid does not exist on this endpoint, and fain is the filter that replaces it. See A sub-grant has no PIID.
Do sub-grant dollars count as a company's contracting revenue?
No. Sub-grants are assistance, not procurement, and are kept in their own table, out of the /companies and /vendor-risk contracting figures. That separation is deliberate; see Why sub-grant dollars stay out of the contracting views.
How far back does the data go?
FY2025 onward (every sub-grant with an action date from October 2024), matching the prime grant layer these hang off. Roughly 600,000 sub-grants, about $265B. Older fiscal years track the prime grant coverage as it extends backward.
Why did /grants/{fain}/subgrants return 404?
Most grants are never sub-granted, and FFATA only requires reporting above a dollar threshold. A 404 means nothing is recorded under that FAIN, not that your request was malformed. Handle it as an empty result.
Are sub-grants bigger or smaller than sub-contracts?
Sub-grants are the larger half of FFATA: more records and more dollars than sub-contracts. Grant money passing through state agencies and universities compounds downstream in a way procurement sub-contracting does not.
Can I redistribute the data?
The underlying data is public-record federal data. Your API access covers internal use and product integration. For redistribution as a commercial product, contact us for a custom arrangement.
Is it really free?
Yes. /subgrants/search and /grants/{fain}/subgrants are free on any GovCon API key. The paid tiers are for the contracting surface (companies, vendor risk, contract subawards), which is where sub-grant dollars are deliberately kept out.
Ready to integrate? Sub-grant search is free on any GovCon API key. Read the full API reference, or ask about fit: [email protected].