# SAM.gov Federal Contracts Scraper + Monitor — No Login, $3/1k (`fortuitous_pirate/sam-gov-scraper`) Actor

Scrape SAM.gov federal contract opportunities: RFPs, solicitations, sources sought, awards. Filter by keyword, NAICS, PSC, set-aside, notice type, agency, state, posted and due dates. Contracting officer contacts, full text. No login or cookies. MCP-ready for AI agents. $3 per 1,000 records.

- **URL**: https://apify.com/fortuitous_pirate/sam-gov-scraper.md
- **Developed by:** [Fortuitous Pirate](https://apify.com/fortuitous_pirate) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 149 total users, 40 monthly users, 89.6% runs succeeded, 7 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## SAM.gov Federal Contracts Scraper + Monitor

Pull federal contract opportunities from **SAM.gov** (solicitations, RFPs, RFQs, sources sought, presolicitations, special notices and award notices) into a clean spreadsheet or JSON feed. Each row has the **contracting officer's name, email and phone**, NAICS and PSC codes, set-aside, place of performance, response deadline (with days left), the full description text, **direct download links for every attachment** (SOWs, PWS, pricing sheets, amendments) and, on award notices, the winner's name, UEI, CAGE code and amount.

No SAM.gov account, no API key, no login, no proxy. Run it with no input and you get the 100 most recently updated active notices.

Four ways to use it, all in one actor:

| Mode | What you give it | What you get |
|---|---|---|
| **Search** | keywords and/or filters (NAICS, PSC, set-aside, notice type, agency, state, date windows) | every matching notice, newest first |
| **Lookup** | solicitation numbers, notice IDs or `sam.gov/opp/.../view` URLs | those notices, active or archived, with every amendment |
| **Bulk** | several keywords, or a CSV / Google Sheet URL (`listUrl`) mixing keywords, solicitation numbers and URLs | one merged, de-duplicated dataset |
| **Monitor** | the same filters plus `onlyNew: true`, on an Apify Schedule | only notices that are **new or updated since the last run**, with an optional webhook |

### Who uses it

- **Government contractors and BD teams**: a daily list of new bids in your NAICS/PSC codes and set-aside, already filtered to deadlines you can still meet.
- **Capture managers (bid/no-bid)**: every notice comes with its documents, contacts, days-to-deadline and due date in one row, so you can triage 50 opportunities without clicking through SAM.gov.
- **Proposal teams**: download the solicitation package (SOW, PWS, pricing template, Q\&A, amendments) straight into the run's storage with `downloadAttachments`.
- **Small businesses (8(a), HUBZone, SDVOSB, WOSB, VOSB)**: filter straight to the set-asides you qualify for.
- **Subcontractors, teaming partners and GovCon lead generation**: award notices with the winner's name, UEI, CAGE code and location, and contracting officer contacts by agency and office, exported to your CRM.
- **Market research and AI agents**: structured, LLM-ready JSON of what the government is buying, who is buying it and who won.

### What you get per notice

| Field | Example | Notes |
|---|---|---|
| `opportunityId`, `solicitationNumber`, `parentNoticeId` | `07ffa9495d33...`, `N0016426R3003` | `parentNoticeId` links an amendment to the original notice |
| `title`, `type`, `typeCode`, `status` | `Presolicitation`, `p`, `Published` | |
| `postedDate`, `modifiedDate`, `responseDeadline`, `responseTimeZone`, `daysUntilDeadline` | `2026-10-30T20:00:00+00:00`, `9` | `daysUntilDeadline` is computed at scrape time; negative = already closed |
| `isActive`, `isCanceled`, `archiveDate`, `archiveType` | `true`, `false`, `2026-11-30` | |
| `agencyName`, `subAgencyName`, `officeName`, `officeCode`, `officeAddress`, `organizationPath` | `DEPT OF DEFENSE > DEPT OF THE NAVY > NAVSEA > ... > NSWC CRANE` | full federal hierarchy |
| `naicsCode`, `naicsCodes`, `pscCode` | `541330`, `R425` | |
| `setAsideType`, `setAsideDescription` | `SBA`, `Total Small Business Set-Aside (FAR 19.5)` | |
| `placeOfPerformance` | `{ city, state, stateCode, zip, country, streetAddress }` | |
| `description`, `descriptionText` | | SAM.gov summary (HTML) and the full notice text as plain text |
| `contacts[]`, `primaryContactName`, `primaryContactEmail`, `primaryContactPhone` | `Logan Kelley`, `logan.b.kelley.civ@us.navy.mil` | every point of contact, plus the primary one as flat columns for CSV |
| `award` | `{ amount, date, number, awardee, awardeeUei, awardeeCageCode, awardeeCity, awardeeState, awardeeZip, awardeeCountry }` | award notices only, otherwise `null` |
| `attachments[]`, `attachmentCount` | `filename, mimeType, size, type, accessLevel, postedDate, downloadUrl` (+ `storedKey`, `storedUrl` when downloaded) | `type: "link"` entries point off-site (e.g. DIBBS, PIEE) |
| `samGovLink`, `scrapedAt` | `https://sam.gov/opp/<id>/view` | |
| `matchedBy` | `N4008426R1026` | lookups and multi-keyword runs: which number / keyword produced the row |
| `changeType`, `firstSeenAt`, `matchedKeywords`, `monitorId` | `new` / `updated` | monitor mode only |

### Filters

Every filter is passed to SAM.gov's own search, so results match what you would see on sam.gov. All filters combine (AND). Multiple values inside one filter are OR-ed: `["VA", "MD"]` means Virginia or Maryland.

| Input | What it does | Example |
|---|---|---|
| `keywords` + `keywordMode` | Free-text search. Any word (default), all words, or exact phrase. One search per line; `keywordList` is the array form for API users | `"cybersecurity"`, `EXACT` |
| `listUrl` | CSV / TSV / text file or Google Sheet (shared as "anyone with the link"). First column; rows are detected as keyword, solicitation number or notice URL | `https://docs.google.com/spreadsheets/d/.../edit` |
| `naicsCodes` | NAICS codes. Prefixes work: `5415` matches 541511, 541512, ... | `["541511", "541512"]` |
| `pscCodes` | Product Service Codes (classification codes) | `["DA10", "R425", "S201"]` |
| `setAsideTypes` | Set-aside codes | `["SBA", "8A", "SDVOSBC", "WOSB", "HZC", "VSA"]` |
| `opportunityTypes` | Notice types, by code or name | `["Solicitation", "Sources Sought", "k"]` |
| `agencies` | Department, agency or office by name, acronym or SAM.gov org ID | `["Department of Veterans Affairs", "DEPT OF THE NAVY"]` |
| `states` | Place-of-performance state codes | `["VA", "MD", "DC"]` |
| `status` / `activeOnly` | `active` (default), `inactive` (archived only) or `all` | `"all"` for award history |
| `postedWithinDays` / `postedFrom` / `postedTo` | Posted (or last updated) window. Dates or relative values | `"7 days"`, `"2026-10-01"` |
| `responseDeadlineFrom` / `responseDeadlineTo` | Offer due-date window. Relative values count forward from today | `"today"` to `"30 days"` |
| `solicitationNumbers` | Lookup by solicitation number (active or archived) | `["N4008426R1026", "36C26026Q0576"]` |
| `noticeIds` | Lookup by notice ID or `https://sam.gov/opp/<id>/view` URL | |
| `maxOpportunities` | Stop after this many rows (default 100) | `500` |
| `sortBy` | Recently modified (default) or relevance | `-relevance` |
| `fetchDetails` | Full details (contacts, NAICS/PSC, set-aside, full text, award). Off = faster listing-only pull | `true` |
| `includeDescription` | Off = leave the description fields empty for slim CSV exports | `false` |
| `includeAttachmentUrls` | Attachment list with download URLs | `true` |
| `downloadAttachments` + `maxAttachmentSizeMb` + `maxAttachmentDownloads` | Also save the public files into the run's key-value store | `true`, `25`, `200` |
| `onlyNew` + `monitorId` + `firstRunMode` + `webhookUrl` | Monitor mode (see below) | `true`, `"it-services-va"`, `"baseline"` |
| `proxyConfiguration` | Optional. SAM.gov answers Apify's cloud IPs directly, so none is used by default | |

Date fields accept `YYYY-MM-DD`, `MM/DD/YYYY`, `today`, or relative values like `7 days`, `2 weeks`, `3 months` (counted back for posted dates, forward for deadlines; `-3 days` / `+3 days` force a direction). Input names used by other SAM.gov tools are accepted as aliases: `noticeTypes`, `setAsideCodes`, `maxResults`, `fetchFullDetails`, `includeAttachments`, `responseDueFrom`/`To`, `modifiedFrom`/`To`, `lookbackDays`.

### Input examples

**Small-business IT bids due in the next 30 days**

```json
{
    "naicsCodes": ["541511", "541512", "541519"],
    "setAsideTypes": ["SBA", "8A"],
    "opportunityTypes": ["Solicitation", "Combined Synopsis/Solicitation"],
    "responseDeadlineFrom": "today",
    "responseDeadlineTo": "30 days",
    "maxOpportunities": 200
}
```

**Engineering support (PSC R425) still open, with the solicitation package downloaded**

```json
{
    "pscCodes": ["R425", "D302"],
    "responseDeadlineFrom": "today",
    "responseDeadlineTo": "30 days",
    "downloadAttachments": true,
    "maxAttachmentSizeMb": 25
}
```

**SDVOSB opportunities at the VA, with contacts**

```json
{
    "agencies": ["Department of Veterans Affairs"],
    "setAsideTypes": ["SDVOSBC", "VSA"],
    "postedWithinDays": 14
}
```

**Who won? Janitorial award notices from last month, archived included**

```json
{
    "pscCodes": ["S201"],
    "opportunityTypes": ["Award Notice"],
    "status": "all",
    "postedFrom": "2026-09-01",
    "postedTo": "2026-09-30"
}
```

**Bulk: several keyword searches in one run, merged and de-duplicated**

```json
{
    "keywords": "zero trust\nSOC as a service\nvulnerability assessment",
    "opportunityTypes": ["o", "k", "r"],
    "maxOpportunities": 300
}
```

**Bulk from a sheet**: `{ "listUrl": "https://docs.google.com/spreadsheets/d/1AbC.../edit#gid=0" }` reads the first column; keywords, solicitation numbers and sam.gov URLs can be mixed in one list.

**Track the solicitations you are bidding on (every amendment, active or archived)**

```json
{
    "solicitationNumbers": ["N4008426R1026", "36C26026Q1017", "W912C326CA008"]
}
```

**Monitor: daily feed of only new or updated notices, posted to Slack / Make / n8n**

```json
{
    "naicsCodes": ["236220", "238210"],
    "states": ["TX", "OK"],
    "opportunityTypes": ["o", "k", "p"],
    "postedWithinDays": 3,
    "onlyNew": true,
    "monitorId": "construction-tx",
    "firstRunMode": "baseline",
    "webhookUrl": "https://hooks.slack.com/services/..."
}
```

### Monitor mode

Set `onlyNew: true` and put the actor on an [Apify Schedule](https://docs.apify.com/platform/schedules). The actor remembers every notice it has returned in a key-value store named `samgov-monitor-<hash of monitorId>` in your account, and on each later run returns only:

- `changeType: "new"`: a notice it has not returned before, with `firstSeenAt`;
- `changeType: "updated"`: a notice whose `modifiedDate` moved since it was last returned (amendment, deadline change, Q\&A posted).

`firstRunMode` decides what the very first run does: `emitAll` (default) returns everything that currently matches, `baseline` records it silently so the next run reports only changes. Notices beyond `maxOpportunities` stay unseen and come out on the next run, so nothing is lost. Use a different `monitorId` per watchlist to run several profiles side by side. Monitor mode also works with `solicitationNumbers`: you get a row only when one of your tracked bids is amended.

If `webhookUrl` is set, every run POSTs a JSON summary `{ monitorId, runId, newCount, updatedCount, itemsPushed, opportunities[first 50] }` to it (Slack incoming webhooks, Zapier, Make, n8n or your own endpoint).

Migrating from the retired *SAM.gov Opportunity Monitor* actor: the store name is identical, so the same `monitorId` keeps your seen-state; `noticeTypes`, `setAsides` and `lookbackDays` work as-is and the `keywords` array becomes `keywordList`. Output: `noticeId` is `opportunityId`, `samUrl` is `samGovLink`, `agency` / `subAgency` / `office` are `agencyName` / `subAgencyName` / `officeName`.

### Attachment downloads

`includeAttachmentUrls` (default on) lists every attachment with a direct `downloadUrl` that opens in any browser. With `downloadAttachments: true` the actor also saves each public file into the run's default key-value store and adds `storedKey` and `storedUrl` to the attachment, so Make/Zapier/n8n can pick the file up or you can fetch it with the Apify API. Files above `maxAttachmentSizeMb` (default 25), external links and controlled (login-only) files are listed with a `skipReason` instead. `maxAttachmentDownloads` (default 200) caps the number of files per run. Downloads are included in the row price; there is no extra charge per file.

A downloaded file gets, for example, `"storedKey": "66becf06-Performance_Work_Statement_(PWS)_-_MARFORRES_FDET_Engineer_Support_Services_Oct2026.docx"` and `"storedUrl": "https://api.apify.com/v2/key-value-stores/OhDstkbmAkDoki5PR/records/66becf06-Performance_Work_Statement_%28PWS%29_-_MARFORRES_FDET_Engineer_Support_Services_Oct2026.docx?signature=..."` (a signed link that works without a token).

### Output

One row per notice. A real row from a run with `pscCodes: ["R425","D302"]`, `responseDeadlineFrom: "today"`, `responseDeadlineTo: "30 days"` (description trimmed):

```json
{
    "opportunityId": "07ffa9495d334260ab03488c5b5ee856",
    "solicitationNumber": "N0016426R3003_DRAFT",
    "title": "Draft RFP for DRPM RAS ME RAS 1.0 and MCM RAS 2.0 Professional Support Services",
    "description": "THIS IS A DRAFT SEAPORT-NxG SOLICITATION TO REQUEST INPUT FROM INDUSTRY - THE GOVERNMENT MAY NOT RES ...",
    "descriptionText": "THIS IS A DRAFT SEAPORT-NxG SOLICITATION TO REQUEST INPUT FROM INDUSTRY - THE GOVERNMENT MAY NOT RESPOND TO ALL QUESTIONS POSTED BUT WILL CONSIDER EACH FOR FINAL SOLICITATION. This Advance Notice/Draft is issued by Naval ...",
    "type": "Presolicitation",
    "typeCode": "p",
    "status": "Published",
    "postedDate": "2026-10-21T14:54:46+00:00",
    "modifiedDate": "2026-10-21T14:54:46+00:00",
    "responseDeadline": "2026-10-30T20:00:00+00:00",
    "responseTimeZone": "America/New_York",
    "daysUntilDeadline": 9,
    "isActive": true,
    "isCanceled": false,
    "agencyName": "DEPT OF DEFENSE",
    "subAgencyName": "DEPT OF THE NAVY",
    "officeName": "NSWC CRANE",
    "officeCode": "N00164",
    "officeAddress": { "streetAddress": "WARFARE CENTER", "streetAddress2": "300 HIGHWAY 361", "city": "CRANE", "state": "IN", "zip": "47522-5001", "country": "USA" },
    "organizationPath": "DEPT OF DEFENSE > DEPT OF THE NAVY > NAVSEA > NAVSEA WARFARE CENTER > NSWC CRANE",
    "parentNoticeId": "f5771a27a0d4424f9e8e3ce9b4a884ef",
    "naicsCode": "541330",
    "naicsCodes": ["541330"],
    "pscCode": "R425",
    "setAsideType": "SBA",
    "setAsideDescription": "Total Small Business Set-Aside (FAR 19.5)",
    "archiveDate": "2026-11-30",
    "archiveType": "autocustom",
    "placeOfPerformance": { "streetAddress": "Washington Navy yard, DC 20376", "city": null, "state": null, "stateCode": null, "zip": "20376", "country": null, "countryCode": null },
    "contacts": [{ "name": "Logan Kelley", "email": "logan.b.kelley.civ@us.navy.mil", "phone": "", "fax": "", "title": null, "type": "primary" }],
    "primaryContactName": "Logan Kelley",
    "primaryContactEmail": "logan.b.kelley.civ@us.navy.mil",
    "primaryContactPhone": "",
    "award": null,
    "attachmentCount": 4,
    "attachments": [
        {
            "filename": "PIEE Solicitation Module Link for N0016426R3003_DRAFT",
            "mimeType": "",
            "size": 0,
            "resourceId": "2366e246dbe84ec1a4c483c775cfb005",
            "accessLevel": "public",
            "postedDate": "2026-10-16T11:51:56.747+00:00",
            "type": "link",
            "description": "PIEE Solicitation Module Link for N0016426R3003_DRAFT",
            "exportControlled": false,
            "downloadUrl": "https://piee.eb.mil/sol/xhtml/unauth/search/oppMgmtLink.xhtml?noticeId=N0016426R3003_DRAFT&noticeType=Presolicitation"
        }
    ],
    "samGovLink": "https://sam.gov/opp/07ffa9495d334260ab03488c5b5ee856/view",
    "scrapedAt": "2026-10-21T16:10:27+00:00"
}
```

Award notices fill `award`, for example `{ "amount": "114511.00", "date": "2026-10-21", "number": "15BGCS27D10900001", "awardee": "Young Veterans Electrodiagnostic Ser., PLLC", "awardeeUei": "Q2HBKZ3BPNG7", "awardeeCageCode": "9XHF5", "awardeeCity": "Versailles", "awardeeState": "KY", "awardeeZip": "40383", "awardeeCountry": "UNITED STATES" }`.

The dataset has three ready-made table views: **Opportunities** (triage columns incl. days left), **Contacts** (agency, office, contracting officer, email, phone) and **Monitor changes**.

Each run also saves a summary in the key-value store record `OUTPUT`: rows returned, total matches on SAM.gov per keyword, the agencies your names were matched to, files downloaded, monitor counts, and any notices that failed. If something breaks mid-run, every row already collected stays in the dataset and the summary says `PARTIAL`.

### Pricing

Pay per result: **$3 per 1,000 notices** plus a $0.005 start fee per run (per GB of memory; the actor runs at 1 GB). Contacts, full text, attachment URLs, attachment downloads and the monitor are all included in the row price; there are no add-on events. Monitor runs only pay for the new or updated rows they return, so a daily watchlist that finds 5 new bids a day costs about $0.60 a month (150 rows + 30 start fees).

Examples: a daily feed of 50 notices = $0.155 per run; a 2,000-row market pull = $6.

### Limits and tips

- **SAM.gov caps a single query at 10,000 notices.** Split larger pulls into date windows with `postedFrom` / `postedTo` or into several keywords.
- **"Posted" dates**: SAM.gov's search index dates a notice by its latest publication, so an amended notice counts as recently posted. That is usually what a bid feed wants; use `parentNoticeId` to group amendments with their original.
- **Faster runs**: turn off `fetchDetails` and `includeAttachmentUrls` when you only need titles, numbers, dates and agencies (one request per page instead of three per notice).
- **Agency names**: check the log or the `OUTPUT` summary to see which SAM.gov organization each name matched. If a name is ambiguous, use the numeric organization ID it shows.
- **Controlled attachments**: files with `accessLevel` other than `public` need a SAM.gov login; the actor lists them but cannot download them.
- **Notice IDs vs solicitation numbers**: a solicitation number can have several notices (original, amendments, award). `solicitationNumbers` returns all of them; `noticeIds` returns exactly one.
- **Timeouts**: the default run timeout is 5 minutes. Raise it in the run options for big pulls with details and attachment downloads.

### Integrations and AI agents

Results export as JSON, CSV, Excel, XML or HTML from the dataset tab, or through the Apify API, webhooks and SDKs.

```python
from apify_client import ApifyClient

client = ApifyClient("MY_APIFY_TOKEN")
run = client.actor("fortuitous_pirate/sam-gov-scraper").call(run_input={
    "naicsCodes": ["541511", "541512"],
    "setAsideTypes": ["SBA", "8A"],
    "responseDeadlineFrom": "today",
    "maxOpportunities": 200,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["solicitationNumber"], row["daysUntilDeadline"], row["primaryContactEmail"])
```

The JavaScript client (`apify-client`) works the same way: `client.actor('fortuitous_pirate/sam-gov-scraper').call(input)` then `client.dataset(run.defaultDatasetId).listItems()`.

- **Schedules**: run it hourly or daily; combine with `onlyNew` so each run only returns changes.
- **Webhooks**: Apify run webhooks fire when a run finishes; `webhookUrl` additionally posts the new rows themselves.
- **Make, Zapier, n8n**: use the Apify app to start the actor and read the dataset, or point `webhookUrl` at a scenario trigger.
- **Google Sheets, Slack, Airbyte, GitHub**: available through [Apify integrations](https://docs.apify.com/platform/integrations).
- **MCP / AI agents**: the output is clean JSON that assistants can use through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp). Ask: "find open SDVOSB construction solicitations in Texas due in the next three weeks and list the contracting officers".

### FAQ

**Do I need a SAM.gov account or API key?** No. The actor reads the same public data the sam.gov website shows to anonymous visitors.

**Can I get archived and awarded notices?** Yes. Set `status` to `all` or `inactive`, or look them up directly with `solicitationNumbers` / `noticeIds`.

**Are the attachment files downloaded?** The URLs are always included. Set `downloadAttachments: true` to also store the public files in the run's key-value store (`storedUrl` on each attachment).

**How do I get only new opportunities each day?** `onlyNew: true` plus a schedule. The first run returns (or, with `firstRunMode: "baseline"`, silently records) everything that currently matches; later runs return only new and updated notices.

**Why does a notice show up again in monitor mode?** Its `modifiedDate` changed (amendment, Q\&A or deadline change); the row says `changeType: "updated"`. A run whose filters match nothing finishes successfully with an empty dataset.

**Is this data legal to use?** SAM.gov contract opportunity data is published by the U.S. government for public use. Notices name government points of contact; if you are in the EU or UK, you are responsible for having a lawful basis for processing personal data.

This actor is an independent tool. It is not affiliated with or endorsed by the U.S. General Services Administration (GSA) or SAM.gov. Feature requests and problems: open an issue on the Issues tab.

# Actor input Schema

## `keywords` (type: `string`):

Search terms, e.g. 'software development', 'cybersecurity', 'janitorial'. One search per line: several lines run as separate searches and the results are merged (de-duplicated). Leave empty to match everything within the other filters.

## `keywordMode` (type: `string`):

ANY = any of the words (SAM.gov's default), ALL = every word, EXACT = the exact phrase.

## `keywordList` (type: `array`):

Alternative to the Keywords box for API users: an array of search terms, each run as its own search.

## `listUrl` (type: `string`):

URL of a CSV, TSV or text file, or a Google Sheet shared as 'anyone with the link'. The first column is read; each row is treated as a keyword search, a solicitation number (e.g. N4008426R1026) or a notice ID / sam.gov URL, detected automatically.

## `naicsCodes` (type: `array`):

Filter by industry codes, one per line. Prefixes work too (5415 matches 541511, 541512, ...). Common IT: 541511 (Programming), 541512 (Systems Design), 541519 (Other IT), 518210 (Data/Hosting), 541690 (Consulting)

## `pscCodes` (type: `array`):

Filter by Product Service Code / classification code, one per line, e.g. DA10 (IT software), R425 (engineering support), S201 (custodial), Z1DA (maintenance of hospitals), 6515 (medical supplies).

## `setAsideTypes` (type: `array`):

Filter by set-aside code: SBA (Total Small Business), SBP (Partial SB), 8A, 8AN (8(a) Sole Source), HZC / HZS (HUBZone), SDVOSBC / SDVOSBS (Service-Disabled Vet), WOSB / WOSBSS, EDWOSB / EDWOSBSS, VSA / VSS (VA Veteran-Owned), LAS (Local Area), IEE, ISBEE, BICiv.

## `opportunityTypes` (type: `array`):

Filter by notice type. Use codes or names (case-insensitive): o = Solicitation, k = Combined Synopsis/Solicitation, p = Presolicitation, r = Sources Sought, s = Special Notice, a = Award Notice, u = Justification, g = Sale of Surplus Property, i = Intent to Bundle. Leave empty for all types.

## `agencies` (type: `array`):

Filter by contracting department, agency or office, one per line. Names, acronyms or SAM.gov organization IDs (e.g. 'Department of Veterans Affairs', 'DEPT OF THE NAVY', 'GSA', 100006568). Each name is matched against SAM.gov's federal hierarchy; the match is shown in the log and in the OUTPUT summary.

## `states` (type: `array`):

Filter by state codes where work will be performed (e.g., CA, TX, VA, DC)

## `activeOnly` (type: `boolean`):

On (default): only notices still open on SAM.gov. Off: include archived/inactive notices too (useful for award history and research).

## `status` (type: `string`):

active = open notices only (default), inactive = archived notices only, all = both. Overrides 'Active Opportunities Only' when set.

## `postedWithinDays` (type: `integer`):

Only notices published or updated within this many days. Ignored when 'Posted From' is set.

## `postedFrom` (type: `string`):

Only notices published or updated on/after this date. YYYY-MM-DD, 'today', or relative like '7 days' (= 7 days ago). Overrides 'Posted Within (Days)'.

## `postedTo` (type: `string`):

Only notices published or updated on/before this date. YYYY-MM-DD, 'today', or relative like '2 days'.

## `responseDeadlineFrom` (type: `string`):

Only notices whose response/offer due date is on/after this date. YYYY-MM-DD, 'today', or relative like '3 days' (= 3 days from now). Use 'today' or '5 days' to skip solicitations you can no longer bid on.

## `responseDeadlineTo` (type: `string`):

Only notices whose response/offer due date is on/before this date. YYYY-MM-DD or relative like '30 days' (= within the next 30 days).

## `solicitationNumbers` (type: `array`):

Look up specific solicitations by number, one per line (e.g. N4008426R1026, 36C26026Q0576). Returns every notice carrying that number (original, amendments, award), active or archived. When set, search filters are ignored.

## `noticeIds` (type: `array`):

Fetch specific notices by 32-character notice ID or URL like https://sam.gov/opp/<id>/view, one per line. When set, search filters are ignored.

## `maxOpportunities` (type: `integer`):

Maximum number of opportunities to scrape. A single SAM.gov query returns at most 10,000 notices; split bigger pulls with date windows.

## `sortBy` (type: `string`):

Recently modified (best for daily feeds and monitoring) or relevance (best with keywords).

## `fetchDetails` (type: `boolean`):

Fetch each notice's detail record: full description text, contracting officer contacts (name, email, phone), NAICS/PSC, set-aside, place of performance, award data. Turn off for a faster, listing-only pull.

## `includeAttachmentUrls` (type: `boolean`):

Include direct download URLs for RFPs, SOWs, pricing templates, and other documents. URLs can be opened in browser to download.

## `downloadAttachments` (type: `boolean`):

Also save each public attachment file (PDF, DOCX, XLSX...) into the run's key-value store and add storedKey / storedUrl to the attachment. Download URLs are listed either way. No extra charge.

## `maxAttachmentSizeMb` (type: `integer`):

Skip downloading files larger than this. Only used with 'Download Attachment Files'.

## `maxAttachmentDownloads` (type: `integer`):

Stop saving files after this many downloads (URLs are still listed).

## `includeDescription` (type: `boolean`):

On (default): include the summary and the full notice text. Off: leave both empty for slimmer CSV/Excel exports (contacts, codes and attachments are still included).

## `onlyNew` (type: `boolean`):

Remembers every notice it has returned (in a key-value store named after Monitor ID) and on later runs returns only notices that are new or were updated since. Schedule the actor daily to get a change feed. Works with search filters and with solicitation-number lookups (tracks amendments to the bids you follow).

## `monitorId` (type: `string`):

Name of this watchlist. Each ID keeps its own seen-state (store samgov-monitor-<hash>), so several profiles can run side by side. Only used with monitor mode.

## `firstRunMode` (type: `string`):

When the monitor has no saved state yet: emitAll returns every current match (good for a first test); baseline records every current match silently and returns nothing, so the next scheduled run reports only what changed since.

## `webhookUrl` (type: `string`):

Optional. After each run a JSON summary (monitorId, runId, newCount, updatedCount, itemsPushed, first 50 opportunities) is POSTed here: Slack, Zapier, Make, n8n or your own API.

## `proxyConfiguration` (type: `object`):

SAM.gov answers Apify's cloud IPs directly, so no proxy is needed and none is used by default. Set one only if your own network policy requires it.

## Actor input object example

```json
{
  "keywords": "",
  "keywordMode": "ANY",
  "keywordList": [],
  "naicsCodes": [],
  "pscCodes": [],
  "setAsideTypes": [],
  "opportunityTypes": [],
  "agencies": [],
  "states": [],
  "activeOnly": true,
  "postedWithinDays": 30,
  "solicitationNumbers": [],
  "noticeIds": [],
  "maxOpportunities": 100,
  "sortBy": "-modifiedDate",
  "fetchDetails": true,
  "includeAttachmentUrls": true,
  "downloadAttachments": false,
  "maxAttachmentSizeMb": 25,
  "maxAttachmentDownloads": 200,
  "includeDescription": true,
  "onlyNew": false,
  "monitorId": "default",
  "firstRunMode": "emitAll"
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset of SAM.gov contract opportunities (JSON).

## `summary` (type: `string`):

OUTPUT record: items pushed, total matches on SAM.gov, resolved agencies, errors.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": "",
    "keywordList": [],
    "naicsCodes": [],
    "pscCodes": [],
    "setAsideTypes": [],
    "opportunityTypes": [],
    "agencies": [],
    "states": [],
    "postedWithinDays": 30,
    "solicitationNumbers": [],
    "noticeIds": [],
    "maxOpportunities": 100,
    "includeAttachmentUrls": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("fortuitous_pirate/sam-gov-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": "",
    "keywordList": [],
    "naicsCodes": [],
    "pscCodes": [],
    "setAsideTypes": [],
    "opportunityTypes": [],
    "agencies": [],
    "states": [],
    "postedWithinDays": 30,
    "solicitationNumbers": [],
    "noticeIds": [],
    "maxOpportunities": 100,
    "includeAttachmentUrls": True,
}

# Run the Actor and wait for it to finish
run = client.actor("fortuitous_pirate/sam-gov-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": "",
  "keywordList": [],
  "naicsCodes": [],
  "pscCodes": [],
  "setAsideTypes": [],
  "opportunityTypes": [],
  "agencies": [],
  "states": [],
  "postedWithinDays": 30,
  "solicitationNumbers": [],
  "noticeIds": [],
  "maxOpportunities": 100,
  "includeAttachmentUrls": true
}' |
apify call fortuitous_pirate/sam-gov-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fortuitous_pirate/sam-gov-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/hlp87huuyH6YLO5TX/builds/KDiAhzSj9uHhLW4U5/openapi.json
