USASpending.gov API: How to Pull Federal Contracts, Grants, and Awards Programmatically
USASpending.gov tracks every federal dollar spent. The API is public and free but the endpoint structure is non-obvious. Here is how to actually use it in Python.
The actor referenced in this article is live on Apify. Pay only for results delivered.
The US federal government spends roughly $6 trillion per year. Every contract awarded, every grant disbursed, and every loan made is recorded in a public database. USASpending.gov is the official source for this data and it has a REST API.
The API is genuinely free, requires no authentication for most endpoints, and covers the full history of federal awards going back to 2000 for some categories. It is also one of the most confusing public APIs to use, because the endpoint structure is non-obvious, the POST body filters are deeply nested, and pagination works differently depending on which endpoint you are hitting.
This post covers the three main endpoints, how to construct filters, how pagination actually works, and Python code for two common workflows.
TL;DR: USASpending.gov’s API is free and covers $6T+ in federal spending. The main search endpoint is a POST request with a JSON body containing nested filters. Pagination uses a
last_record_unique_idcursor, not page numbers. No API key is required for most endpoints but the rate limit is around 500 requests per hour from a single IP.
The FPDS vs USASpending Distinction
Before the code: there are two federal procurement data systems, and confusing them is the most common mistake.
FPDS (Federal Procurement Data System) is the authoritative source for federal contracts. It lives at fpds.gov and has its own API. The data is more granular and more frequently updated. The interface is built for federal contracting professionals and the API is verbose.
USASpending.gov consolidates data from FPDS plus grants (from FAADS/FABS), loans, direct payments, and other financial assistance. It is the broader source, updated with a short delay relative to FPDS, and the API is designed to be more accessible. If you want contracts only, FPDS gives you more current data. If you want the full picture of federal spending including grants and loans, USASpending is the right choice.
For most commercial use cases (government sales intelligence, grant research, contractor market analysis), USASpending is the starting point.
The Three Main Endpoints
1. Awards search (/api/v2/search/spending_by_award/)
This is the primary endpoint. It accepts a JSON POST body with filter criteria and returns a paginated list of individual awards. Use this for searching contracts awarded to specific companies, filtering by agency and date range, or finding grants in a specific state.
2. Spending explorer (/api/v2/spending/)
This endpoint is for aggregated spending views. It does not return individual awards; instead it returns rollup totals by agency, object class, program activity, or fiscal year. Use this for dashboards and trend analysis, not for finding individual contracts.
3. Recipient search (/api/v2/recipient/)
This endpoint returns information about recipients (companies, nonprofits, state agencies) that have received federal awards. You can look up a recipient by name, DUNS number, or UEI (the newer Universal Entity Identifier that replaced DUNS in 2022).
The rest of this post focuses on the awards search endpoint, which is what you need for the most common data tasks.
The POST Body Structure
Here is the minimum POST body for a search:
{
"filters": {
"award_type_codes": ["A", "B", "C", "D"]
},
"fields": ["Award ID", "Recipient Name", "Award Amount", "Award Date"],
"page": 1,
"limit": 100,
"sort": "Award Amount",
"order": "desc"
}
The award_type_codes field is required. The codes mean:
| Code | Award Type |
|---|---|
| A | BPA Call |
| B | Purchase Order |
| C | Delivery Order |
| D | Definitive Contract |
| 02 | Block Grant |
| 03 | Formula Grant |
| 04 | Project Grant |
| 05 | Cooperative Agreement |
| 06 | Direct Loan |
| 07 | Guaranteed/Insured Loan |
For grants, use codes 02-09. For contracts, use A-D. The full list is in the USASpending data dictionary.
Filtering: Agency, Date Range, and Award Type
Here is a more realistic filter for pulling all contracts awarded by the Department of Defense in fiscal year 2025:
import requests
BASE_URL = "https://api.usaspending.gov/api/v2"
def get_dod_contracts(fiscal_year=2025, limit=100):
"""Pull contracts from DoD for a given fiscal year."""
payload = {
"filters": {
"award_type_codes": ["A", "B", "C", "D"],
"agencies": [
{
"type": "awarding",
"tier": "toptier",
"name": "Department of Defense"
}
],
"time_period": [
{
"start_date": f"{fiscal_year - 1}-10-01",
"end_date": f"{fiscal_year}-09-30",
"date_type": "action_date"
}
]
},
"fields": [
"Award ID",
"Recipient Name",
"Award Amount",
"Total Outlays",
"Award Date",
"Contract Award Type",
"Awarding Agency",
"Awarding Sub Agency",
"recipient_id",
"naics_code",
"naics_description",
"Place of Performance State Code"
],
"page": 1,
"limit": limit,
"sort": "Award Amount",
"order": "desc"
}
resp = requests.post(
f"{BASE_URL}/search/spending_by_award/",
json=payload
)
resp.raise_for_status()
data = resp.json()
return data["results"], data.get("page_metadata", {})
contracts, meta = get_dod_contracts(fiscal_year=2025)
print(f"Total matching awards: {meta.get('total', 'unknown')}")
print(f"Showing page 1 of {meta.get('last_page', '?')}")
for c in contracts[:5]:
print(f"{c['Recipient Name']}: ${c['Award Amount']:,.0f} — {c['naics_description']}")
Note the fiscal year convention: FY2025 starts October 1, 2024 and ends September 30, 2025.
Pagination with last_record_unique_id
This is where most people get confused. The awards search endpoint uses two different pagination systems depending on what you are doing.
The standard page-based pagination (using the page field) works up to page 200. Beyond that, USASpending caps results at 10,000 records per query (100 per page x 100 pages). If your query matches more than 10,000 awards, you need to use cursor-based pagination.
Cursor pagination requires the last_record_unique_id and last_record_sort_value fields from the response:
def paginate_all_awards(filters, fields, sort_field="Award Amount", sort_order="desc"):
"""Iterate through all pages of awards using cursor pagination."""
all_results = []
last_record_unique_id = None
last_record_sort_value = None
page = 1
while True:
payload = {
"filters": filters,
"fields": fields,
"limit": 100,
"sort": sort_field,
"order": sort_order,
"subawards": False,
}
# After the first page, use cursor instead of page number
if last_record_unique_id:
payload["last_record_unique_id"] = last_record_unique_id
payload["last_record_sort_value"] = last_record_sort_value
else:
payload["page"] = 1
resp = requests.post(
f"{BASE_URL}/search/spending_by_award/",
json=payload
)
resp.raise_for_status()
data = resp.json()
results = data.get("results", [])
if not results:
break
all_results.extend(results)
page += 1
meta = data.get("page_metadata", {})
if not meta.get("hasNext", False):
break
# Extract cursor values from the last item
last = results[-1]
last_record_unique_id = last.get("internal_id")
last_record_sort_value = last.get(sort_field)
print(f"Page {page}: {len(all_results)} awards collected so far...")
return all_results
# Pull all EPA grants in California
epa_ca_grants = paginate_all_awards(
filters={
"award_type_codes": ["02", "03", "04", "05"],
"agencies": [
{"type": "awarding", "tier": "toptier", "name": "Environmental Protection Agency"}
],
"place_of_performance_locations": [
{"country": "USA", "state": "CA"}
]
},
fields=["Award ID", "Recipient Name", "Award Amount", "Award Date", "Description"]
)
print(f"Total EPA grants in CA: {len(epa_ca_grants)}")
Useful Filter Combinations
By specific recipient (using UEI):
filters = {
"award_type_codes": ["A", "B", "C", "D"],
"recipient_search_text": ["Lockheed Martin"],
"time_period": [{"start_date": "2024-01-01", "end_date": "2024-12-31"}]
}
By state and award size:
filters = {
"award_type_codes": ["04"], # Project grants only
"place_of_performance_locations": [{"country": "USA", "state": "TX"}],
"award_amounts": [
{"lower_bound": 1000000, "upper_bound": 10000000}
]
}
By NAICS code (for industry-specific contractor research):
filters = {
"award_type_codes": ["A", "B", "C", "D"],
"naics_codes": {
"require": ["541512"], # Computer Systems Design Services
},
"time_period": [{"start_date": "2025-01-01", "end_date": "2025-12-31"}]
}
Use Cases
Government sales intelligence. If you sell to the government, USASpending tells you which agencies are buying what you sell, which companies are currently winning those contracts, what the contract values look like, and when incumbent contracts are likely to expire. Sorting by Award Date on multi-year contracts expiring in the next 12 months is a standard BD workflow that used to require expensive procurement databases.
Nonprofit grant research. The grants database (award types 02-09) shows every federal grant disbursement by agency, program, and recipient. For a nonprofit, pulling all grants from NEA, NIH, or EPA in your state gives you a complete picture of the funding landscape before you write an application.
Contractor market analysis. Who is winning contracts in a specific NAICS code? What is the average contract value? Which agencies are the biggest buyers? This analysis is 20 lines of Python once you understand the filter structure.
Compliance monitoring. Federal contractors are required to check the System for Award Management (SAM) exclusions list before doing business with subcontractors. USASpending complements this by showing the full award history of any entity by their UEI.
Academic and policy research. Researchers studying government procurement patterns, geographic distribution of federal spending, or industry concentration in government contracting use USASpending as a primary data source.
The managed USASpending Federal Awards scraper handles the pagination, filter construction, and retry logic and delivers results as a structured dataset. For one-off queries, the raw API is straightforward once you understand the POST body format. For scheduled monitoring or bulk exports across many filter combinations, the managed scraper is significantly less infrastructure to maintain.
Try the scraper referenced in this article — live on Apify, pay only for results.
Open usaspending-federal-awards on Apify →How to Scrape AmbitionBox Company Reviews and Ratings
AmbitionBox is India largest employer review platform with 300,000 companies. Learn how to pull ratings, review counts, salary data, and dimension scores as structured JSON without any official API.
AliExpress Product Data API: Prices, Ratings, and Orders in Python
AliExpress affiliate API has restricted coverage. Learn how to scrape AliExpress product listings for prices, ratings, order counts, and seller data as structured JSON — no affiliate approval needed.
ClinicalTrials.gov API v2: How to Search 500,000 Studies and Track Trial Status
ClinicalTrials.gov upgraded to a v2 REST API in 2024. Here is how to use it, what changed from v1, and how to build automated trial monitoring pipelines in Python.