Querying USAspending for Federal Contract Awards With a POST Search and No API Key
USAspending publishes every federal award the United States government makes, and the API needs no key, no registration and no authorization header. That combination is rare enough on government data that it is worth saying twice.
The thing that stops most people is the shape of the request. The interesting endpoints are POST, the body is a nested object, and a wrong field name gets you a 400 with little explanation. Here is the working version.
The Endpoint Is POST Only
POST https://api.usaspending.gov/api/v2/search/spending_by_award/
Send a GET and you get a bare 405 Method Not Allowed, which is the first place people give up. There is no GET equivalent for award search.
A Request That Works
Two fields are required, filters and fields. Everything else has a default.
{
"subawards": false,
"limit": 25,
"page": 1,
"sort": "Award Amount",
"order": "desc",
"filters": {
"award_type_codes": ["A", "B", "C", "D"],
"time_period": [
{ "start_date": "2026-08-01", "end_date": "2026-09-21" }
],
"agencies": [
{
"type": "awarding",
"tier": "toptier",
"name": "Department of Defense"
}
]
},
"fields": [
"Award ID",
"Recipient Name",
"Award Amount",
"Start Date",
"End Date",
"Awarding Agency",
"Awarding Sub Agency",
"Contract Award Type",
"NAICS",
"PSC"
]
}
With curl:
curl -s -X POST \
https://api.usaspending.gov/api/v2/search/spending_by_award/ \
-H "Content-Type: application/json" \
-d @query.json
The Parts That Trip People
fields takes display names, not snake_case keys. They are literal strings with spaces and capitals: "Award ID", "Recipient Name", "Awarding Sub Agency". Pass award_id and the request fails. This is the single most common mistake with this API, because every other part of the payload looks like normal JSON conventions.
award_type_codes decides which universe you are searching. A, B, C and D are contracts. IDV_A through IDV_E are indefinite delivery vehicles, which are ordering agreements rather than awards of a fixed amount, and mixing them into a contract query distorts any total you compute. Assistance has its own codes: 02 through 06 cover grants and direct payments, 07 through 11 cover loans and other categories.
If you want defense contracts, ["A","B","C","D"] is the set. Add the IDV codes only if you specifically want ceilings rather than obligations.
sort must be a field you asked for. It defaults to the first entry in fields, so if you want to order by value, "Award Amount" has to appear in both places.
Agency filters are objects, not strings. Each needs type (awarding or funding), tier (toptier or subtier) and name. For a subtier like a specific military branch, add toptier_name to scope it under its parent department.
Pagination
The response carries page_metadata:
"page_metadata": { "page": 1, "hasNext": true }
There is no total count. You walk pages until hasNext is false.
import requests
URL = "https://api.usaspending.gov/api/v2/search/spending_by_award/"
def fetch_all(payload, max_pages=40):
page, out = 1, []
while page <= max_pages:
payload["page"] = page
r = requests.post(URL, json=payload, timeout=60)
r.raise_for_status()
data = r.json()
out.extend(data["results"])
if not data["page_metadata"].get("hasNext"):
break
page += 1
return out
Keep the max_pages guard. A broad filter over a full fiscal year will happily page for a very long time.
For deep pagination the contract also exposes last_record_unique_id and last_record_sort_value, which are Elasticsearch search-after cursors. Use them if you are pulling tens of thousands of rows, because offset paging degrades badly at depth.
Checking Your Filter Before You Pull
There is a companion endpoint, /api/v2/search/spending_by_award_count/, that takes the same filters object and returns counts by award category. Call it first with a new filter set. If the number is wildly larger or smaller than you expected, your filter is wrong, and you find out in one request rather than after forty pages.
Worth Knowing
The documentation that actually matters is not the rendered docs site. It is the API contract files in the project’s own repository under usaspending_api/api_contracts/, written in API Blueprint, one markdown file per endpoint. They list every accepted field and enum value, and they are the source the service is built from.
No authorization, no published rate limit, and a full record of federal spending. For anyone building tooling around government contracts, this is close to the best-case starting point.