SAM.gov Scraper — Federal Contracts, Attachments & Contacts avatar

SAM.gov Scraper — Federal Contracts, Attachments & Contacts

Pricing

Pay per event

Go to Apify Store
SAM.gov Scraper — Federal Contracts, Attachments & Contacts

SAM.gov Scraper — Federal Contracts, Attachments & Contacts

Scrape public U.S. federal contract opportunities from SAM.gov: solicitations, awards, sources sought, agency and contact details, the full solicitation text, and the solicitation documents themselves. No API key, no login, no session cookies.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Oswaldo Carabano

Oswaldo Carabano

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Share

SAM.gov Federal Contract Opportunities & Attachments

Every U.S. federal contract opportunity, as clean JSON — including the solicitation documents.

No API key. No login. No session cookies. You paste nothing and configure nothing before your first run. Hit Start with the defaults and you get contract notices.


Why that first line matters

Several SAM.gov scrapers ask you to bring your own api.sam.gov API key. That means registering with SAM.gov, waiting for approval, and then owning the problem when the key's quota runs out or it expires — the actor stops working because of something you did correctly.

This one talks to the same public endpoint the SAM.gov search page itself uses. There is nothing to register and nothing to renew.


What you get

One row per notice, 62 fields, snake_case, null where a value is genuinely absent — never a missing key, so row.award_amount is safe on every row.

Identitynotice_id, solicitation_number, title, notice_type, url
Lifecycleis_active, is_canceled, published_at, modified_at, archive_at, modification_count
Deadlineresponse_due_local, response_due_utc, response_due_precision, response_timezone
Classificationnaics_code, naics_title, psc_code, psc_title, set_aside_code, set_aside
Agencyagency_name, agency_id, agency_code, plus the full agency_hierarchy
Contactcontact_name, contact_email, contact_phone, plus contacts_all
Place of performancepop_country, pop_state, pop_city, pop_zip
Awardaward_number, award_date, award_amount, awardee_name, awardee_uei, awardee_cage_code
The solicitation textdescription_html and description_text
Documentsattachments[] with name, size_bytes, mime_type and a stable url

Fill rates, measured — not estimated

On a sample of 13,434 notices across five days spread over ten months:

100.0 % notice_id, title, notice_type, published_at, modified_at, archive_at
99.6 % contact_name 99.2 % contact_email
99.5 % solicitation_number 99.4 % agency_name
97.8 % psc_code 96.1 % naics_code
84.3 % description_html 75.2 % response_due_*
46.1 % set_aside_code 32.7 % contact_phone

Award fields sit at ~23% of all notices — but that is because most notices are not awards. Within award notices (notice_type_code = "a", about a quarter of the index), award_amount and awardee_name are effectively always present.

We do not list a field here unless we have measured it.


The documents, and the honest number

Turn on "Also fetch solicitation documents" and every notice gains an attachments[] array. There are two kinds and the row tells you which:

kindWhat it isFields
fileThe real document — the PDF, the SOW, the price matrix, the amendmentname, size_bytes, mime_type, url
linkA URL to the agency's own portal, when that is all the agency postedname (its label), url

Measured on a stratified sample of 829 notices across 70 agency × notice-type cells, covering 96.7% of the active index:

36.7% ± 4.6 of active notices carry at least one downloadable file, and the rate varies a lot by notice type:

Notice typeShare of indexCarries a file
r Sources Sought7%60%
s Special Notice6%56%
u Justification1%52%
o Solicitation18%49%
k Combined Synopsis/Solicitation34%39%
p Presolicitation8%32%
a Award Notice24%14%

And it varies even more by agency, which is the part worth knowing before you plan a run:

AgencySolicitations carrying a file
Veterans Affairs~100%
General Services Administration~90%
Department of Defense (63% of the whole index)~28%

DoD posts a large share of its documents on its own portals — you get a link to neco.navy.mil instead of the file. That is why the DoD number drags the overall rate down, and why filtering by agency changes what you get more than filtering by type does.

Average file size: 1.5 MB.

file_count and link_count are separate fields on purpose. A link to neco.navy.mil is not the solicitation, and we are not going to count it as one.

Downloading a document

The url we give you is stable and unsigned — it keeps working long after the run finishes, and you can store it. It is a 303 redirect to a short-lived signed S3 link, so your client has to follow redirects and finish the download promptly:

$curl -L -o sow.pdf "<attachments[0].url>" # -L is required

Verified: the bytes you get back match the size_bytes we report, exactly.

What we do not return

Attachments that SAM.gov marks as restricted, export-controlled, deleted, or non-public are not returned at all — not even their URL, because publishing the URL to a controlled document is publishing the document. This is checked against six separate flags with an allowlist, not a blocklist.


The deadline field, and why there are three of them

This is the one place where being precise costs an extra column, and it is worth it.

responseDate from SAM.gov is not always a real instant. Measured on 1,566 notices with a deadline:

response_due_precisionShareWhat it means
instant61.0%Real offset. response_due_utc is a true UTC instant you can convert
date_only38.8%The notice says "due on the 25th" with no time. response_due_utc is null
local_naive0.2%A wall-clock time with no zone. response_due_utc is null

SAM.gov stamps midnight UTC on the date_only ones. We do not pass that through as an instant, because it would look like a precise deadline and it is not one. Use response_due_local for display and response_due_utc only when response_due_precision is instant.


Also available

Exclusion records (federal debarment list, ~168,000 records) go to a separate exclusions dataset. Set "Maximum exclusion records" above 0.

⚠️ We deliberately do not offer a "is this vendor debarred?" lookup. 63% of the list is individuals and only ~20% of those carry a UEI, so matching by name alone manufactures false positives — and a false positive here tells you a real person is debarred when they are not.


Coverage

~5.6 million notices in the index, back to 2000
~50,000 active at any given time (the default; turn off "Only active
notices" to reach the archive)
~2,500 published per calendar day

Those are rounded on purpose. The exact counts move every day — the index grew by several hundred notices while this page was being written — so a precise figure here would be wrong tomorrow. Every fill rate below is a fixed measurement with its sample size, which is the number that does not drift.

Sweeping is done by publication date, one calendar day per request, newest first. Rows are deduplicated by notice_id before they are delivered, so you are never charged for a duplicate.


Pricing

EventPrice
Actor start$0.00001
Notice delivered$0.0025
Attachment listed$0.0005
Exclusion record$0.0005

You are never charged for an error row. Charging happens right after a row is delivered, never in a batch at the end — so a run that stops early has charged you for exactly what you received, and a run that crashes has not charged you for rows you did not get.

Duplicates, precisely. Within a run, rows are deduplicated by notice_id before they are charged, so you never pay twice for the same notice in one run — and SAM.gov's own pagination does return duplicates, which is why this matters. A new run is a new order: it fetches again and charges again, even for notices you already have.

Cached rows cost the same, and we would rather tell you than have you find out. A shared cache is what makes a repeat run finish in 20 seconds instead of 4 minutes, and it means this actor barely touches SAM.gov's servers for data someone already pulled. You are paying for the row, not for our bandwidth. Two things follow, and both are on every row so you can check them yourself:

  • from_cache tells you which rows came from the cache, and data_age_hours tells you exactly how old each one is. Nothing is hidden behind an average.
  • The cache is shared across all users of this actor, and it holds only the normalized public row — never your input, never your filters, never anything that identifies your run.

If you want everything fetched fresh regardless, set "Maximum cache age (days)" to 0. It is slower and it costs the same.


Notes

  • Runs are deliberately polite: 4 concurrent requests with a 1-second delay. SAM.gov is public infrastructure paid for by taxpayers, and we are not going to hammer it because we can.
  • robots.txt is fetched on every single run and enforced in the request layer. The actor never requests a disallowed path.
  • Notices are immutable once published: a SAM.gov amendment creates a new notice that links back to the previous one. That is why cached rows stay correct, and why from_cache, fetched_at and data_age_hours are on every row.
  • U.S. Government works are not subject to copyright (17 U.S.C. § 105), and FAR Part 5 requires agencies to publish these notices so that businesses can find them.

Data removal: https://samgov.actorstack.dev · privacy@actorstack.dev