DEV Community

CBLU2005
CBLU2005

Posted on

Federal contracts: see who actually WON — post-award intel from USAspending's free API

SAM.gov tells you what the government wants to buy (I covered tracking it without an API key earlier). But the pre-award side is only half the picture. The other half — the half your competitors' business developers are quietly living in — is who won, for how much, from which agency, and when that contract runs out.

That data is USAspending.gov: every federal contract, grant, loan, and direct payment, published under the DATA Act. It answers questions SAM.gov can't:

  • Competitive intel — who actually wins IT-services contracts in Texas? At what dollar sizes? For which agencies?
  • Recompete pipeline — an award with an end date 9 months out is a solicitation waiting to happen. Incumbents know. Now you do too.
  • Teaming targets — small primes winning work in your NAICS are potential partners (or acquisition targets).
  • Grant prospecting — universities and nonprofits: see exactly which programs fund organizations like yours, and how much.

And unlike SAM.gov's official API, USAspending's API needs no key, no account, no registration. It's genuinely open. Here's the honest DIY, where it bites, and the shortcut.

The DIY: one POST endpoint does most of it

The workhorse is spending_by_award — POST your filters, get awards back:

curl -s -X POST "https://api.usaspending.gov/api/v2/search/spending_by_award/" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "award_type_codes": ["A", "B", "C", "D"],
      "time_period": [{"start_date": "2026-05-01", "end_date": "2026-08-12"}],
      "naics_codes": ["541511"],
      "place_of_performance_locations": [{"country": "USA", "state": "TX"}]
    },
    "fields": ["Award ID", "Recipient Name", "Award Amount", "Awarding Agency",
               "Start Date", "End Date", "Description", "generated_internal_id"],
    "sort": "Award Amount", "order": "desc", "limit": 25, "page": 1
  }'
Enter fullscreen mode Exit fullscreen mode

That call works today, unauthenticated (custom-programming contracts performed in Texas, biggest first — the top hit as I write this is a $37.7M DoD award). Every result's generated_internal_id gives you a shareable page: https://www.usaspending.gov/award/<id>.

Where it bites

The API is free and stable — and its query grammar was clearly designed for USAspending's own frontend, not for you:

  1. award_type_codes is mandatory and cryptic. Contracts are AD, contract IDVs are IDV_AIDV_E, grants are 02/03/04/05, direct payments 06/10, loans 07/08. Send an empty filter set and you get a 422, not "everything."
  2. Contracts and grants can't be queried together properly. Several filters (naics_codes, PSC) only apply to contracts; CFDA program numbers only to assistance. Real coverage means separate queries per award group, merged and deduplicated yourself.
  3. fields are magic display strings. You request columns by their UI names — "Recipient Name", "Award Amount" — and misspellings just silently vanish from the response. Contract and grant responses also name overlapping things differently (Award Type vs Contract Award Type).
  4. The good stuff needs a second call per award. Recipient mailing address, business categories, executive compensation — all on the award detail endpoint (/api/v2/awards/<generated_internal_id>/), one round trip each. A 500-award pull becomes 501 requests with polite pacing.
  5. Occasional nested surprises — a field that's a string on one award is an object on the next. Your flattener finds out in production.

None of this is a dealbreaker; it's a long afternoon plus perpetual maintenance. If you enjoy that afternoon, the official docs are decent.

The shortcut: filters in, flat records out

I maintain an Apify actor that wraps all of the above — award-group fan-out, field normalization, optional per-award enrichment, paging, dedupe:

USAspending Federal Awards Scraper

{
    "awardTypes": ["contracts"],
    "naicsCodes": ["541511"],
    "placeOfPerformanceStates": ["TX"],
    "timePeriodStart": "2026-05-01",
    "minAwardAmount": 250000,
    "includeRecipientAddress": true,
    "maxResults": 500
}
Enter fullscreen mode Exit fullscreen mode

Every record comes out flat and consistently named — recipientName, recipientUei, awardAmount, awardingAgency, startDate, endDate, naics, usaspendingUrl, plus the recipient's address and business categories when you flip includeRecipientAddress — with JSON/CSV/Excel export and pricing per record returned (a 500-award competitive-landscape pull costs about a dollar and a half).

Grants work the same way — swap awardTypes to ["grants"] and filter by cfdaNumbers (e.g. 93.778 for Medicaid) or awardingAgency.

The play that pays: pre-award + post-award together

The two datasets are a pincer:

  1. USAspending (this post): find every contract in your NAICS ending in the next 12 months. That's your recompete target list, with the incumbent, the agency, and the dollar value attached.
  2. SAM.gov: watch for the recompete solicitation to drop — with the contracting officer's name and email on it.

Schedule both weekly on Apify Schedules and you've rebuilt the core of a $300/month GovCon intelligence subscription for a few dollars of data charges.

Recap

  1. USAspending.gov's API is free, keyless, and covers every federal award — the post-award half of GovCon intel.
  2. DIY is one POST endpoint plus real friction: cryptic mandatory type codes, split contract/grant querying, magic field names, N+1 enrichment.
  3. The actor flattens all of it behind one input; pair it with SAM.gov monitoring for the full pre-award + post-award picture.

If there's a specific slice you're trying to pull — a NAICS, an agency, a grant program — drop a comment and I'll sketch the exact input.

Top comments (0)