USASpending Federal Awards Scraper
Scrape US federal awards: recipient, amount, awarding agency, period and place of performance. Filter by agency, state, NAICS, amount or date. No API key.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
searchTerms,awardTypes,maxItems(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.0005 per award = $0.5 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Award scraped | One federal award with its recipient, amount and period of performance. Searches that match nothing are never charged. | $0.0005 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-20, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
searchTerms | Free-text search across the award record - the description, the recipient, the award id and several fields this Actor does not return. Each term is a separate search and the rows are pooled. Minimum three characters per term, up to 10 terms. Because the search reaches fields you cannot see in the output, a row can be a genuine hit with the word nowhere visible on it. | array |
awardTypes | Which families of award to search. Federal spending is filed in six families and the service will not mix them in one query, so each one you tick is a separate search and the row budget is split between them. Leave it empty and you get contracts plus grants, which is what most people mean by federal awards. | array |
maxItems | Total awards to return across every kind of award you ticked. The budget is split between them and whatever one family leaves unused is handed to the next. Hard ceiling 10,000. Keep it low while you are testing - you pay per row. | integer |
agencies | Limit to awards handed out by these departments, written exactly as the source writes them: "Department of Defense", "Department of Energy", "National Aeronautics and Space Administration". This is the cabinet-level department, not the office inside it - the office still comes back on every row. A name the service does not recognise returns nothing rather than everything. | array |
recipientSearch | Match the organisation that received the money - a company name, or a UEI. Partial names work. Several entries widen the search rather than narrowing it. | array |
placeOfPerformanceStates | Two-letter US state codes for where the work was actually done, for example TX or CA. This is not the same as where the recipient is registered - a Virginia contractor doing work in Guam is a Guam row. Note that indefinite-delivery vehicles carry only a country for place of performance - no state and no city - so ticking that award family alongside this filter returns nothing for it. | array |
recipientStates | Two-letter US state codes for the recipient's own address, for example VA or MD. Use this when you want the companies in a state rather than the work in a state. | array |
minAwardAmountUsd | Drop awards below this figure. Useful for cutting out the long tail of small purchase orders, which is most of the row count on a contract search. Leave it empty and nothing is dropped, including the negative rows that record money being taken back off an award. | integer |
maxAwardAmountUsd | Drop awards above this figure. Leave it empty for no upper limit. | integer |
dateFrom | Only awards with activity on or after this date, as YYYY-MM-DD. The earliest the service accepts is 2007-10-01 and anything earlier is moved up to it. Read the warning on "Date meaning" below - this window is about when money moved, not about the period of performance on the row. | string |
dateTo | Only awards with activity on or before this date, as YYYY-MM-DD. Leave empty for today. | string |
dateType | What the date window is measured against. "Action date" is when money moved, including a modification to an old award - so a 2019 contract amended last month appears in a window covering last month, still showing its 2019 start date. "New awards only" restricts it to awards first signed inside the window, which is what you want for a feed of new business. | string |
naicsCodes | Six-digit NAICS codes, such as 541512 for computer systems design. Contracts and IDVs carry these; grants and other assistance mostly do not, so a NAICS filter on a grant search will return very little. | array |
pscCodes | Four-character PSC codes such as R425 for engineering support or D310 for IT services. Contracts only - this is how the government classifies what it bought. | array |
assistanceListingNumbers | Programme numbers such as 93.778 or 20.205, for grants, loans, direct payments and other assistance. This follows one federal programme across every award it has ever made, which is the tightest filter here. Contracts have no assistance listing, so this will return nothing on a contract search. | array |
awardIds | Look up specific awards by the id printed on them, for example W912DQ24C4003. An id that does not exist returns an uncharged row saying so, not a wrong match. | array |
sortBy | Biggest awards first, or most recent first. Either way you get the top of that ordering, so if you ask for 100 rows on a search that matches 40,000 you get the 100 largest or the 100 newest - not a random hundred. | string |
proxyUrls | Leave this empty. The run brings its own addresses and the cost of them is already inside the price. Fill it in only if you specifically want the traffic to leave through servers you already pay for, as http://user:pass@host:port. | array |
What you get
A structured dataset — each result includes fields like:
awardIdrecipientNameawardAmountUsdawardTypeawardingAgencyawardingSubAgencyperiodOfPerformanceStartperiodOfPerformanceEndplaceOfPerformanceStateplaceOfPerformanceCityrecipientStateurlExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Related tools in Developer & Research Tools
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
GitHub Scraper
Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.
Stack Overflow / Stack Exchange Scraper
Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.
Package Registry Scraper (npm + PyPI)
Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.
arXiv Scraper
Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.
OpenAlex Scholarly Works Scraper
Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.
Crossref Scholarly Works Scraper
Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.
Where this tool sits
- Categories
- Developer & Research Tools
USASpending Federal Awards Scraper
Money the US federal government has already handed over, one row per award: the award id, who received it, how much, which agency signed it, the period of performance, where the work was actually done, and what kind of award it is. Filter by agency, state, industry code, amount band or date window. No account, no API key, no login, no browser.
- Awards, not opportunities. Every row is money that has been committed — the recipient is known and the amount is real.
- Contracts, grants, cooperative agreements, loans, direct payments and indefinite-delivery vehicles, each in its own clearly labelled family.
- Place of performance comes back separately from the recipient address, so "work done in Texas" and "company registered in Texas" are two different questions you can ask.
- Rows that do not match the filters you set are dropped before they are charged, and the run tells you how many and why.
- Run it with empty input and you get a labelled sample row, free, so you can see the shape first.
Price
$0.50 per 1,000 awards, plus a $0.0005 start fee per run.
This is a flat rate on every plan, free or paid. There are no volume tiers, no minimum spend, no subscription and no add-on fees. What you read here is what you pay on day one and on day four hundred.
| Awards | Total cost |
|---|---|
| 100 | $0.0505 |
| 1,000 | $0.5005 |
| 10,000 | $5.0005 |
| 100,000 | $50.0005 |
What is actually charged
- One
award-scrapedevent per award row written to the dataset. Nothing else is metered per row. - Free: the sample row an empty run returns, and every diagnostic row — a blocked target, a dead URL, a search that matched nothing. Those rows all carry
"charged": false. - Awards already returned earlier in the same run are skipped before they are charged.
- Rows that came back not matching your filters are dropped, not billed.
- A run that finds nothing costs the start fee and nothing more.
- Rows never leave the dataset without a charge, and are never charged without a row. The billed event is a named one, so there is no price quietly attached to
apify-default-dataset-item— the trick that makes some scrapers bill you for their own error messages.
Input
{
"agencies": [
"Department of Energy"
],
"placeOfPerformanceStates": [
"TX",
"NM"
],
"awardTypes": [
"contracts"
],
"minAwardAmountUsd": 1000000,
"dateFrom": "2026-01-01",
"maxItems": 200
}
| Field | What it does |
|---|---|
searchTerms | Free text across the award record. Minimum three characters, up to 10 terms. The search reaches fields this Actor does not return, so a row can be a genuine hit with the word nowhere visible on it. |
awardTypes | Which families of award to search: contracts, grants, idvs, loans, direct_payments, other. The source will not mix families in one query, so each one you tick is a separate search and the row budget is split between them. Leave it empty for contracts plus grants. |
maxItems | Total awards across every family you ticked. Whatever one family leaves unused goes to the next. Hard ceiling 10,000. Keep it low while testing — you pay per row. |
agencies | Cabinet-level department names, spelled as the source spells them: Department of Defense, Department of Energy. A name the service does not recognise returns nothing rather than everything. |
recipientSearch | The organisation that got the money — company name or UEI. Partial names work. |
placeOfPerformanceStates | Two-letter state codes for where the work was done. Not the same as where the recipient is registered. |
recipientStates | Two-letter state codes for the recipient's own address. |
minAwardAmountUsd / maxAwardAmountUsd | An amount band in dollars. The minimum is the useful one — it cuts the long tail of small purchase orders, which is most of the row count on any contract search. |
dateFrom / dateTo | A date window as YYYY-MM-DD. The earliest the service accepts is 2007-10-01; anything earlier is moved up to it. |
dateType | action_date (any money moved in the window), date_signed, last_modified_date, or new_awards_only for a feed of genuinely new business. Read the note below about what this window measures. |
naicsCodes | Six-digit industry codes. Contracts and IDVs carry them; assistance awards mostly do not. |
pscCodes | Four-character product and service codes — how the government classifies what it bought. Contracts only. |
assistanceListingNumbers | Programme numbers such as 93.778, for grants and other assistance. Follows one federal programme across every award it has made. Contracts have none. |
awardIds | Exact award ids. One that does not exist returns an uncharged row saying so. |
sortBy | amount for the largest first, date for the most recent first. |
proxyUrls | Leave empty. Fill it in only if you want traffic to leave through proxy servers you already pay for. |
Run it with empty input and you get one clearly labelled sample row, free, so you can see the output shape before you spend anything.
Output
One row per award. A real row from a real run:
{
"ok": true,
"charged": true,
"recordType": "award",
"awardId": "75N93026C00017",
"awardUniqueKey": "CONT_AWD_75N93026C00017_7529_-NONE-_-NONE-",
"awardTypeGroup": "contracts",
"awardType": "DEFINITIVE CONTRACT",
"recipientName": "LEIDOS BIOMEDICAL RESEARCH, INC.",
"recipientUei": "HGZDJ6MJ7DL5",
"recipientCity": "FREDERICK",
"recipientState": "MD",
"recipientCountry": "UNITED STATES",
"awardAmountUsd": 1057293000,
"totalOutlaysUsd": 0,
"loanSubsidyCostUsd": null,
"awardingAgency": "Department of Health and Human Services",
"awardingAgencyCode": "075",
"awardingSubAgency": "National Institutes of Health",
"awardingSubAgencyCode": "7529",
"fundingAgency": "Department of Health and Human Services",
"fundingSubAgency": "National Institutes of Health",
"periodOfPerformanceStart": "2026-01-01",
"periodOfPerformanceEnd": "2032-12-31",
"placeOfPerformanceCity": "FREDERICK",
"placeOfPerformanceState": "MD",
"placeOfPerformanceZip": "21702",
"placeOfPerformanceCountry": "UNITED STATES",
"naicsCode": "541715",
"naicsDescription": "RESEARCH AND DEVELOPMENT IN THE PHYSICAL, ENGINEERING, AND LIFE SCIENCES",
"pscCode": "AN11",
"pscDescription": "R&D- HEALTH CARE: BIOMEDICAL (MANAGEMENT/SUPPORT)",
"assistanceListingNumber": null,
"assistanceListingTitle": null,
"description": "OPERATION AND TECHNICAL SUPPORT OF THE FREDERICK NATIONAL LABORATORY FOR CANCER RESEARCH",
"baseObligationDate": "2026-01-01",
"lastModified": "2026-09-02 14:18:33",
"url": "https://www.usaspending.gov/award/CONT_AWD_75N93026C00017_7529_-NONE-_-NONE-",
"scrapedAt": "2026-09-20T11:04:22.118Z"
}
Field notes
awardId— the id printed on the award. It is unique within an agency, not across the whole government — useawardUniqueKeyif you need a primary key.awardUniqueKey— the canonical key for the award, and whaturlis built from. Stable across runs, so it is the right thing to diff on.awardTypeGroup— which of the six families this row came from:contracts,grants,idvs,loans,direct_payments,other. It decides which of the money and date fields are populated.awardAmountUsd— the current total obligated on the award, after every modification. On a loan row this is the face value of the loan instead, andloanSubsidyCostUsdcarries what it cost the government. It can be negative, which means money was taken back off an award.totalOutlaysUsd— money actually paid out so far, as against committed. Frequently 0 on a new award, and null on loans.periodOfPerformanceStart/periodOfPerformanceEnd— the contracted work window. An indefinite-delivery vehicle has no end date in the source — it has a last date to order, which is what appears here. A loan has an issue date and no end date at all.placeOfPerformanceState— where the work was done. Blank on every indefinite-delivery vehicle, and city is blank on most grants; the source does not record it for those.recipientState— where the recipient is registered. This is the one that is almost always populated.assistanceListingNumber— the ALN, formerly the CFDA number — it identifies the federal programme rather than the individual award. Assistance awards only.description— the agency's own text for what the award is for. Cut at 2,000 characters. Often terse, occasionally an internal code, sometimes empty.url— the public award page. Everything in the row is on it, plus the transaction history this Actor does not return.
Every real row carries "charged": true. Sample rows carry "_sample": true and diagnostic rows carry "_diagnostic": true with an errorCode you can filter on, and neither is ever billed.
How it works
- It calls the government's own award search service directly and pages through the answer. No key, no cookie, no session and no browser — the service answers anyone who asks properly.
- The six award families are searched one at a time, because the service refuses a query that mixes a contract code with a grant code. Each family gets a share of your row budget and hands back whatever it does not use.
- Paging uses the service's cursor rather than page numbers. Page numbers are capped and, worse, the "is there more?" flag turns false at exactly 10,000 rows while real rows keep arriving — a deep pull that trusted it would stop early and never say so.
- Every row is checked against the filters you set before it is allowed to charge. The service quietly ignores a filter key it does not recognise and answers with the full unfiltered list at HTTP 200, so trusting the status code would mean billing you for the whole country when you asked for one state.
- A row that cannot be checked — a state filter against an award family where the source leaves place of performance blank — is dropped rather than charged, and the run tells you how many and why.
What people use it for
- Working out who already holds the work before bidding on the recompete: filter by agency and industry code, sort by amount, read the top twenty names.
- Tracking one company across the whole federal government — every contract, grant and loan it has received, in one table.
- Economic development and local reporting: every federal dollar performed in a state or a metro area over a window, by agency.
- Grant reporting, by following an assistance listing number across every award that programme has ever made.
- Watching a department's spending month by month on a schedule, diffing on
awardUniqueKeyto see only what is new. - Feeding a dashboard or a database. The rows are flat and typed, dates are ISO, money is numeric, so nothing needs cleaning first.
Awards are not opportunities
This is the one thing to get straight before buying, because the two get confused constantly.
An award is money that has already gone out. It names a recipient, an amount, and a period of performance that may already have started. That is what this Actor returns.
An opportunity is a notice inviting you to apply or bid. It has a deadline, an eligibility list and no recipient, because nobody has won it yet. None of that is here — there is no deadline field, no eligibility field and no application link, because an award does not have them.
If what you need is a list of things you can still apply for, this is the wrong tool and you will get a table with the wrong columns.
What the date window actually measures
The date filter is about when money moved, not about the period of performance printed on the row. A contract signed in 2019 and modified last month is a legitimate hit for a window covering last month, and it will come back showing its 2019 start date. That is correct, not a bug — federal awards are amended for years.
If you want genuinely new business, set dateType to new_awards_only. Then only awards first signed inside the window come back.
For the same reason, the row-level checks below deliberately do not test the period of performance against your date window — doing so would throw away correct rows.
How deep a single search can go
A search walks the results with the service's own cursor, which keeps going well past the point where page numbers stop working. In testing a single cursor walk passed 11,000 rows with the cursor still being handed back, and a cursor request at the equivalent of page 5,000 answered normally where the same page without a cursor is refused outright.
Occasionally the service stops handing out the cursor. When that happens the run writes an uncharged row saying the search could not go deeper, rather than quietly returning a short answer. Narrow the search — by agency, by state, by a tighter date window, or by a minimum amount — and run it again.
The hard ceiling in this Actor is 10,000 rows per run. That is a deliberate cap, not a limit of the source: split a bigger job across runs so a single run can never bill you for more than you expected.
Reading the output
Every run writes three kinds of row and they are easy to tell apart:
- Real rows carry
"charged": trueand"recordType": "award". One billed event each. - The sample row carries
"_sample": trueand"charged": false. There is exactly one, it only appears when the input had nothing to search for, and it is there so you can see the shape before you spend anything. - Diagnostic rows carry
"_diagnostic": true,"charged": falseand anerrorCodeworth switching on:NO_RESULTSwhen a search ran fine and matched nothing,BAD_INPUTwhen rows came back that did not match your filters or the service refused the query,NETWORKwhen it could not be reached at all,SERVER_ERRORwhen it answered with an error of its own,TIME_BUDGETwhen the run ran out of time before a family was searched.
The difference between NO_RESULTS and NETWORK is the one that matters: the first means there is genuinely nothing there, the second means nobody looked. Neither is charged, and confusing them is how people conclude an agency has stopped spending when the service was simply down.
If you only want data, filter on charged == true. The count of those rows always equals the number of events you were billed for, so the dataset is its own invoice.
People, and what is deliberately left out
Federal recipients are mostly organisations, but not all of them. Sole traders and individuals appear in this data under their own names, particularly in direct payments and small purchase orders, and the government publishes them that way.
This Actor hands over the award record as the government published it and stops there. The recipient's city, state and country are on the row because place is what the data is for. The street address lines the source also carries are not emitted, no contact details of any kind are produced, and nothing is joined against any other source to build a profile of anybody. If you need the full registration address, open the url — it is on the public award page.
Limitations
- Awards only. Opportunities, solicitations, bids and anything with a deadline are a different dataset and are not returned.
- Federal only. State, county, city and school-district spending is not published here.
- The earliest date the source accepts is 2007-10-01. Older awards exist but are not reachable through this route.
- One row per award, not per transaction. A contract amended fourteen times is one row carrying the current total, not fourteen rows.
- Sub-awards are not returned. A prime recipient who subcontracts most of the work still shows the whole amount against their own name.
- Indefinite-delivery vehicles carry no state or city for place of performance — only a country — and no end date. They have a last date to order instead, and that is what the end field shows. Measured on 40 of them: country on 40, state on 0, city on 0.
- Loans carry no award amount, no outlays and no end date. They have a face value, a subsidy cost and an issue date instead, and the row maps them onto the same fields.
- City is blank on most grant rows and on every indefinite-delivery vehicle. That is the source, not the scraper.
- Description is often terse and sometimes an internal agency code rather than English. Some awards have none at all.
- Search terms reach fields that are not returned, so you cannot always see why a row matched.
- Each award family is a separate search, so ticking all six splits your row budget six ways.
- The hard ceiling is 10,000 rows per run and 10 search terms. Split bigger jobs across runs.
- Figures are as current as the source. An agency that has not filed a modification leaves a stale total on the award, and the row repeats it faithfully.
Questions
Is this the same thing as a list of contracts I can bid on?
No, and this is the most common mix-up. Every row here is an award that has already been made — there is a recipient and an amount, and no deadline, because the money is gone. A list of things you can still bid on or apply for is a different dataset with different columns.
Why does a row have a start date from years ago when I asked for this month?
Because the date filter is about when money moved, not about the period of performance. Federal awards get modified for years after they are signed, and a modification last month makes the award a correct hit for a window covering last month. If you want only genuinely new awards, set dateType to new_awards_only.
I asked for one state and got rows from somewhere else. What now?
You will not. Rows that do not match what you asked for are dropped before they are charged, and you get an uncharged diagnostic row saying how many and why. The usual cause is a code in the input the service does not recognise — check the state codes and the agency spelling.
Why is place of performance empty on some rows?
Because the government does not record it for those award types. Indefinite-delivery vehicles carry a country and nothing more — no state, no city — and most grants have a state but no city. If you filter on a state and tick an award family that has no state on it, those rows are dropped uncharged rather than guessed at.
Can I get the sub-awards, or the individual transactions?
Not from this Actor. It returns one row per prime award with its current totals. Transaction history and sub-award detail are separate datasets with a different row for every line, and mixing them into this table would leave most of it empty.
What happens if my search matches nothing?
You get one uncharged diagnostic row with errorCode: "NO_RESULTS" and the run finishes as succeeded. You are never billed for a search that returned nothing.
Will the run fail if the service is down?
No. You get an uncharged diagnostic row saying it could not be reached, and the run still finishes as succeeded. A failed run would still bill the start fee, which would mean paying to be told something was broken.
Do I need an account or an API key?
No. The service this reads is open to anyone, and the Actor holds no account, login or cookie for it.
Can I run this on a schedule?
Yes. Nothing is held between runs. Pair a daily schedule with a tight dateFrom and dateType: "new_awards_only", and diff on awardUniqueKey to see only what is new since yesterday.
How do I get exactly the rows I paid for?
Filter the dataset on "charged": true. Sample and diagnostic rows are always false, and the number of charged rows always equals the number of billed events.