# SAM.gov Government Contract Opportunities Monitor (`lindenwerk/sam-gov-contract-matcher`) Actor

SAM.gov government contracts monitor: US federal contract opportunities (solicitations, RFPs, sources sought, awards) scored to your NAICS, PSC, keywords and set-asides. Amendments merged into one row per solicitation, deadline filters, daily bid alerts. No API key needed.

- **URL**: https://apify.com/lindenwerk/sam-gov-contract-matcher.md
- **Developed by:** [Lindenwerk Data](https://apify.com/lindenwerk) (community)
- **Categories:** Lead generation, Business, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 scored match returneds

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

### What is SAM.gov Government Contract Opportunities Monitor?

**Find US federal government contract opportunities on SAM.gov that actually fit your company.** This Actor reads the official
SAM.gov Contract Opportunities public extract (no API key, no login), merges amendments into **one row per
solicitation**, filters by NAICS, PSC, set-aside, agency, state and response deadline, and gives every opportunity a
**relevance score from 0 to 100 with plain-English reasons**. Contracting officer names, e-mails and phone numbers are
**never** output.

- **Daily federal bid alerts**: schedule `monitor` mode and get only new or amended solicitations since the last run.
- **30-day market scan**: `search` mode returns every match posted in the last N days, sorted by score or deadline.
- **Competitor and award tracking**: `awards` mode lists who won what, for how much, in your NAICS codes.
- **Small-business set-asides**: filter by SBA, 8(a), HUBZone, SDVOSB, WOSB/EDWOSB, VA veteran set-asides or unrestricted.
- **Cheap and explainable**: USD 0.003 per scored match, no LLM, every score explained in `matchReasons`.

### Why this SAM.gov scraper alternative?

| | This Actor | Typical SAM.gov scrapers |
|---|---|---|
| Source | Official public extract published by GSA for reuse (sam.gov/data-services) | Website or internal search API |
| API key | Not needed (your own key optional, for same-day notices) | Often required |
| Amendments | Collapsed: one row per solicitation, older versions in `relatedNoticeIds` | One row per notice version |
| Relevance | `relevanceScore` 0-100 + `matchReasons` across NAICS, PSC and keywords | Keyword hit or none |
| Monitor | `changeType` = `new` / `updated` per solicitation | Re-sends everything or per notice |
| Personal data | Contracting officer names, e-mails, phones removed, also from descriptions | Often included |
| Price | USD 0.003 per scored match, USD 0.0015 per unscored row | USD 0.001-0.10 per row |

### What it does

1. Downloads the daily **SAM.gov Contract Opportunities extract** (`ContractOpportunitiesFullCSV.csv`, about 210 MB,
   \~75,000 active notices). The file is sorted by posting date, newest first, so the Actor **stops reading as soon as
   it has passed your date window**: a daily monitor reads about 6 MB, a 30-day scan about 72 MB.
2. In monitor mode it compares the file's ETag with the last run. **If SAM.gov has not published a new file, the body
   is not downloaded at all** and the run ends in about a second.
3. Groups all notice versions of the same solicitation (same solicitation number and agency) and keeps the newest one.
   Awards are grouped per contract, so multiple-award contracts list every awardee.
4. Applies your hard filters (notice type, set-aside, agency, state, deadline window, award amount, exclude keywords,
   competitor names). Filtered rows are never charged.
5. Scores the rest against your NAICS codes, PSC codes and keywords and keeps rows at or above `minScore`.
6. Removes personal contact data, adds a link to the public SAM.gov notice page and pushes flat rows to the dataset.

### Use cases

- **Federal BD teams and GovCon consultants**: a daily shortlist of solicitations, presolicitations and sources-sought
  notices in your NAICS codes, with deadlines and reasons, instead of hand-searching SAM.gov.
- **Small businesses**: a feed limited to your set-aside (SDVOSB, 8(a), WOSB, HUBZone) and states.
- **Capture planning**: sources-sought and presolicitation notices give you weeks of lead time before the RFP.
- **Competitive intelligence**: recent awards in your market with awardee, amount and agency.
- **AI agents and automations**: clean, explainable JSON for Make, Zapier, n8n, Slack or an LLM via MCP.

### How to get daily SAM.gov bid alerts

1. Click **Start** with the prefilled example (IT and cybersecurity, NAICS 5415, posted in the last 7 days). It returns
   up to 25 scored opportunities, so you see real output before you change anything.
2. Replace the keywords and NAICS codes with your own. Optionally add PSC codes, set-asides, agencies, states and a
   response deadline window.
3. Pick a mode: `search` for a market scan, `monitor` for daily alerts that return only new or amended solicitations,
   or `awards` for competitor and award tracking.
4. Look at the results in the **Output** tab (views "Matches" and "Awards") or download them as JSON, CSV or Excel.
5. For daily alerts, switch `mode` to `monitor`, save the input as a **task** and add a **schedule** (for example
   every weekday at 08:00 US Eastern).
6. Send new matches on with Apify **integrations** (Slack, Google Drive, Make, Zapier, n8n or a webhook), or call the Actor from your own code through the **Apify API**.

### Quick start

Daily alert for an IT small business (schedule it once a day, e.g. 08:00 US Eastern):

```json
{
  "mode": "monitor",
  "profileName": "it-small-business",
  "keywords": ["cybersecurity", "software", "cloud", "network", "help desk"],
  "naicsCodes": ["541511", "541512", "541519", "518210"],
  "pscCodes": ["D", "7A", "7B"],
  "setAsides": ["small-business", "8a", "sdvosb", "none"],
  "minDaysUntilDeadline": 3,
  "minScore": 45
}
```

30-day market scan for construction in Virginia, Maryland and DC, earliest deadline first:

```json
{
  "mode": "search",
  "keywords": ["renovation", "roof", "HVAC", "paving"],
  "naicsCodes": ["2362", "2382", "237310"],
  "pscCodes": ["Z2", "Y1"],
  "states": ["VA", "MD", "DC"],
  "postedWithinDays": 30,
  "deadlineWithinDays": 45,
  "sortBy": "deadline"
}
```

Unscored feed of SDVOSB set-asides at Veterans Affairs from the last 7 days (no NAICS or keywords = plain feed):

```json
{ "mode": "search", "setAsides": ["sdvosb"], "agencies": ["Veterans Affairs"], "postedWithinDays": 7, "sortBy": "postedDate" }
```

IT awards over USD 100,000 from the last 30 days:

```json
{ "mode": "awards", "naicsCodes": ["5415"], "minAwardAmount": 100000, "postedWithinDays": 30 }
```

More examples are in the `examples/` folder of the source.

#### The Console form vs. API defaults

- **The Console form opens with a short trial:** `mode` = search over the last 7 days (`postedWithinDays` 7), the
  prefilled IT profile (cybersecurity / network / software, NAICS 541512 / 5415) and `maxResults` = 25. One click shows
  the 25 best open opportunities for at most USD 0.075. This prefill is also what Apify's daily automated Store test
  runs. It keeps no state and skips no unchanged files, so it returns rows on any day, US weekends and federal holidays
  included.
- **Before you schedule it**, switch `mode` to **monitor** and raise `maxResults`, or set it to 0 for no limit.
- **API calls, tasks and schedules that leave a field out get the defaults:** `mode` = monitor, `lookbackDays` = 3,
  `postedWithinDays` = 30, `maxResults` = 200. The prefill values only fill the form.
- **First monitor run on a quiet day:** a monitor run with no saved state (the first run for a `stateKey`, or one with
  `resetState`) that finds nothing new in its `lookbackDays` window looks back `firstRunBackfillDays` (default 7)
  posting days once. Everything it returns is remembered, so the next run is a normal monitor run. Set it to 0 to turn
  this off. It isn't used when you set `postedFrom`.
- **Quiet days and outages:** a monitor run with nothing new, or one whose extract is unchanged since the last run,
  still ends *Succeeded*, with 0 rows and a status message. Nothing is charged. If the sam.gov download link fails, the
  Actor uses the public S3 copy of the same file and reports this as a warning. If neither can be read (or the file is
  malformed), the run ends *Failed* with a clear message. Nothing is charged and the monitor state is left unchanged.

### Input

| Field | What it does |
|---|---|
| `mode` | `monitor` (default: only new or updated since the last run), `search` (last `postedWithinDays` days; the Console form opens with this) or `awards` (Award Notices). |
| `keywords` | Matched in the title (full weight) and description (lower weight). `"cyber"` also matches `cybersecurity`; quote a word for whole-word matching. |
| `naicsCodes` | 6-digit codes (strongest evidence) or prefixes such as `5415`. |
| `pscCodes` | Product Service Code prefixes: `D` = IT services, `R4` = professional support, `Z2` = real property repair, `S2` = housekeeping. |
| `setAsides` | Families (`small-business`, `8a`, `hubzone`, `sdvosb`, `wosb`, `veteran`, `indian`, `local`, `none`) or exact SAM.gov codes (`SBA`, `8AN`, `SDVOSBC` ...). |
| `agencies` | Text in department, sub-tier or office (`"Army Corps of Engineers"`), or a CGAC / AAC code (`"036"`, `"W912DY"`). |
| `states` | Place of performance, 2-letter codes. Falls back to the contracting office state if none is given. |
| `noticeTypes` | Default: solicitation, presolicitation, sources-sought. Also special-notice, justification, intent-to-bundle, surplus-sale, ... |
| `onlyOpen`, `deadlineWithinDays`, `minDaysUntilDeadline` | Deadline window: drop closed notices, keep only those due soon, or drop those due too soon to bid. |
| `excludeKeywords` | Drop notices containing these words. |
| `competitorNames`, `minAwardAmount`, `maxAwardAmount` | Awards mode filters. |
| `minScore` | Default 40. Guide: exact NAICS only = 50, one title keyword only = 46, NAICS + one title keyword = 73. |
| `lookbackDays`, `postedWithinDays`, `postedFrom` | Date window. `postedWithinDays: 0` reads all active notices. |
| `includeUpdates` | Monitor: also return a solicitation again when a newer version is posted (`changeType: "updated"`). |
| `maxResults`, `sortBy` | Cap (best matches first; default 200, the Console form prefills 25) and order of the output. |
| `includeDescription`, `descriptionMaxLength` | Description text (personal data removed), default 600 characters. |
| `stateKey`, `resetState` | Separate "already seen" lists per profile or schedule. |
| `firstRunBackfillDays` | Monitor only: how far a first run with no saved state looks back if its normal window is empty. Default 7, 0 = off. |
| `samApiKey` | Optional: your own SAM.gov public API key, to add notices posted after the daily extract was built. |

### Output

One flat row per solicitation. A real row from a 30-day IT scan on 5 Oct 2026 (some fields left out). Three earlier versions of this solicitation
were merged into it:

```json
{
  "noticeId": "07889806966c44b385181aa0cf7c70bf",
  "solicitationNumber": "N0018926RL011",
  "title": "Enterprise Cloud Support Services",
  "noticeType": "solicitation",
  "noticeTypeLabel": "Solicitation",
  "department": "DEPT OF DEFENSE",
  "subTier": "DEPT OF THE NAVY",
  "office": "NAVSUP FLT LOG CTR NORFOLK",
  "postedDate": "2026-10-01T15:50:30",
  "responseDeadline": "2026-10-27T14:00:00-04:00",
  "daysUntilDeadline": 23,
  "isOpen": true,
  "setAsideCode": "SBA",
  "setAside": "Small Business Set Aside - Total",
  "setAsideFamily": "small-business",
  "naicsCode": "518210",
  "naicsSector": "Information",
  "pscCode": "DG01",
  "pscCategory": "IT and Telecom Services",
  "placeOfPerformanceState": "VA",
  "noticeUrl": "https://sam.gov/opp/07889806966c44b385181aa0cf7c70bf/view",
  "relatedNoticeIds": ["1d700fce3d2345f4bf05f7901bf7ad3a", "b8f1b788e44746a8a9fb73367bec0dc1", "292be4c5d0f545f2bbf687e8681af724"],
  "versionCount": 4,
  "relevanceScore": 89,
  "matchReasons": [
    "NAICS 518210 matches profile NAICS 518210",
    "PSC DG01 matches profile PSC D",
    "Keyword 'cloud' in title",
    "Keyword 'network' in description",
    "Set-aside: SBA",
    "Responses due in 23 day(s)"
  ],
  "source": "SAM.gov Contract Opportunities public extract",
  "extractUpdatedAt": "2026-10-05T03:30:41+00:00"
}
```

#### Output fields

| Group | Fields |
|---|---|
| Identity | `noticeId`, `solicitationNumber`, `noticeType`, `noticeTypeLabel`, `baseType`, `changeType`, `relatedNoticeIds`, `versionCount` |
| Content | `title`, `description` (personal data removed) |
| Agency | `department`, `subTier`, `office`, `agencyCode` (CGAC), `officeCode` (AAC), `officeCity`, `officeState`, `officeZip`, `officeCountry` |
| Dates | `postedDate`, `responseDeadline`, `daysUntilDeadline`, `isOpen`, `archiveDate` |
| Classification | `setAsideCode`, `setAside`, `setAsideFamily`, `naicsCode`, `naicsSector`, `pscCode`, `pscCategory` |
| Place of performance | `placeOfPerformanceCity`, `placeOfPerformanceState`, `placeOfPerformanceZip`, `placeOfPerformanceCountry` |
| Awards | `awardNumber`, `awardDate`, `awardAmount`, `awardee` (company name only) |
| Links and matching | `noticeUrl`, `additionalInfoLink`, `relevanceScore`, `matchReasons`, `profileName`, `source`, `extractUpdatedAt`, `retrievedAt` |

Each run also writes a `RUN_SUMMARY` record (bytes read, rows read, early stop, ETag skip, filter counts, matches).
The dataset has two views: **Matches** (score, deadline, link, reasons) and **Awards** (awardee, amount, date).

### Pricing (pay per event)

| Event | Price |
|---|---|
| Actor start | USD 0.00005 |
| `scored-match`: a solicitation that matched your profile at or above `minScore` | **USD 0.003** |
| `opportunity`: a row of an unscored feed (no NAICS, PSC, keyword or competitor profile) | **USD 0.0015** |

Filtered notices, notices below `minScore`, older versions and solicitations already delivered by an earlier monitor
run are **never charged**. Platform compute is included. Cap your cost with `maxResults` and the run's *maximum total
charge*: the Actor stops cleanly when the limit is reached and, in monitor mode, delivers the rest next run.

**Real runs on 5 Oct 2026** (inputs in `examples/`):

| Run | Rows returned | Your cost |
|---|---|---|
| Daily `monitor`, IT small business, first run (3 posting days) | 18 | **USD 0.054** |
| Same monitor the next day (weekend postings, nothing new above `minScore`) | 0 | USD 0.00005 |
| Same monitor again before SAM.gov published a new file (ETag skip) | 0 | USD 0.00005 |
| 30-day `search`, IT small business, `minScore` 45 | 68 | **USD 0.20** |
| 7-day unscored feed, SDVOSB set-asides at Veterans Affairs | 77 | **USD 0.12** |
| 30-day `awards`, NAICS 5415, at least USD 100,000 | 114 | **USD 0.34** |

A daily monitor for a typical profile costs **a few US dollars per month at most**. You pay only for matches.

### Use with AI agents (MCP)

The input schema is written so that an LLM can call the Actor without guessing: plain-English descriptions, enums,
defaults and examples. Add it to the [Apify MCP server](https://mcp.apify.com) in Claude Desktop, Cursor, VS Code or
any other MCP client:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=actors,lindenwerk/sam-gov-contract-matcher"
    }
  }
}
```

Then ask, for example: *"Find open SDVOSB set-aside solicitations for janitorial services in Texas posted in the last
two weeks, due in at least 7 days, and list the five best matches."* The agent maps this to `mode: search`,
`naicsCodes: ["561720"]`, `keywords: ["janitorial", "custodial"]`, `setAsides: ["sdvosb"]`, `states: ["TX"]`,
`postedWithinDays: 14`, `minDaysUntilDeadline: 7` and can quote `matchReasons` to explain each result.

You can also call the Actor through the Apify API
(`POST https://api.apify.com/v2/acts/lindenwerk~sam-gov-contract-matcher/run-sync-get-dataset-items`), the Python or
JavaScript clients, or connect it to Make, Zapier, n8n, Google Sheets or Slack through Apify integrations.

### Data source, terms and attribution

- **Source: SAM.gov**, U.S. General Services Administration (GSA), Data Services, extract "Contract Opportunities /
  datagov" (`privacy=Public`), https://sam.gov/data-services. GSA publishes it as a daily electronic download "in order
  to promote data sharing". The Actor downloads **one public file per run** (often only its first few MB), honours the
  file's ETag and never touches the SAM.gov website, its internal search API or any login. No scraping, no proxies.
- SAM.gov's terms of use point to the public extracts and APIs for downloading data. Works of federal employees are in
  the public domain in the U.S.; SAM.gov asks for a citation and a link back, so **every row links to its public
  SAM.gov notice page** and carries `source`. Data: SAM.gov, U.S. General Services Administration.
- **Dun & Bradstreet restriction**: award notices with an award date before 4 April 2022 may contain D\&B data that
  must not be used commercially. For these rows `awardee` is left empty and `matchReasons` says why.
- The extract is rebuilt once a day (`extractUpdatedAt`) and contains active notices only, so the newest notices can
  be about one day old. With your own SAM.gov public API key (`samApiKey`, generated in your SAM.gov account under
  Account Details) the Actor also fetches notices posted since the file was built (at most 2 API requests per run).
  Your key is stored encrypted by Apify, used only inside your run and never logged or output. SAM.gov's terms say
  you must not share your key with others.
- This is an independent tool, not an official GSA or SAM.gov service. Always read the original notice and its
  attachments on SAM.gov before you bid. The official notice prevails.

### Privacy and responsible use

SAM.gov notices name contracting officers and specialists (name, e-mail, phone) in contact columns and, very often,
again in the description. This Actor:

- never outputs the contact columns;
- removes personal e-mail addresses, all phone and fax numbers, and contact names from titles and descriptions
  (replaced by `[email removed]`, `[phone removed]`, `[name removed]`);
- keeps functional mailboxes such as `contracting@` or `...solicitationinbox@` because bidders need them;
- outputs awardees as company names only, without address.

In our checks on 5 Oct 2026 (about 14,000 output rows, including all 13,811 active award notices) no personal e-mail
address and no phone number remained, and all 808 awards dated before 4 April 2022 had `awardee` withheld. Use the output to find and bid on contracts, not for contact harvesting.

### Limits

- The extract lags about one day and drops archived notices. For older history use `postedWithinDays: 0` (all active
  notices) or a SAM.gov archive.
- Attachments (RFP PDFs) are not downloaded. Open `noticeUrl` for documents and Q\&A.
- API rows (only with `samApiKey`) have no description, so keywords match their titles only.
- Scores are rule-based and depend on the words in each posting window, so a borderline row can score a point or two
  differently between runs. Monitor mode delivers it once it reaches `minScore`.

### FAQ

**How is `relevanceScore` calculated?**
Code evidence (exact NAICS 0.50, NAICS prefix 0.45, PSC prefix 0.35) and keyword evidence (title hits weigh about
three times description hits, rare words weigh more) are combined as `100 × (1 − (1 − codes) × (1 − keywords))`. With
NAICS codes and keywords in the profile: NAICS only = 50, one title keyword only = 46, NAICS + one title keyword = 73,
NAICS + two title keywords = 85 or more. A NAICS-only profile scores 100 for an exact code. `matchReasons` lists
every contribution and every filter passed.

**What does "one row per solicitation" mean?**
SAM.gov posts a new notice for every amendment and stage (presolicitation, solicitation, amendment 1, 2, ...). In the
active extract about 8,900 solicitation numbers have two or more versions. The Actor returns the newest version once
and lists the older notice ids in `relatedNoticeIds`; `versionCount` says how many versions it saw.

**How does monitor mode work?**
Each `stateKey` (default: `profileName`) keeps an "already seen" list in the named key-value store
`sam-gov-matcher-state` for 180 days. A solicitation is returned once as `new`; if SAM.gov posts a newer version later
it comes back as `updated` (switch off with `includeUpdates: false`). If no new file was published since the last run,
the run ends without downloading anything.

**What does a run with no matches cost?** Only the Actor start event (USD 0.00005).

**Do I need a SAM.gov account or API key?** No. The key is optional and only adds same-day notices.

**Can I monitor several profiles?** Yes. Create one saved task per profile, each with its own `profileName` or `stateKey`.

**Other countries?** See our German & EU Tenders Monitor (Germany and any EU country via TED) and France Tenders Monitor (BOAMP + TED).

### Examples

- [Find SAM.gov IT contract opportunities](https://apify.com/lindenwerk/sam-gov-contract-matcher/examples/sam-gov-it-contract-opportunities): a published example task with a ready-made input. Open it, adjust the input and run it in your own Apify account.

### Other Lindenwerk Data Actors

- [German & EU Tenders: Public Procurement Monitor](https://apify.com/lindenwerk/german-eu-tender-matcher): Ausschreibungen from oeffentlichevergabe.de and TED, deduplicated and scored to your CPV codes and keywords.
- [France Tenders Monitor: BOAMP, TED & Marchés Publics](https://apify.com/lindenwerk/france-tender-matcher): marchés publics from BOAMP and TED in one deduplicated, scored list.
- [UK Tenders Monitor: Government Contracts & Find a Tender Alerts](https://apify.com/lindenwerk/uk-tender-matcher): Find a Tender notices (above and below threshold) as daily tender alerts, scored to your profile.
- [Website Technology & Tech Stack Detector: BuiltWith Alternative](https://apify.com/lindenwerk/tech-stack-detector): CMS, shop system, analytics, consent manager and email provider of any website, in bulk.
- [SEO Audit Crawler: Broken Links, llms.txt & AI Bot Check](https://apify.com/lindenwerk/seo-audit-crawler): on-page SEO, broken links, llms.txt and AI crawler check for whole websites.
- [PDF to Markdown & RAG Chunks: Document Parser (DOCX/PPTX/XLSX)](https://apify.com/lindenwerk/pdf-to-markdown-rag): PDF, Word, PowerPoint and Excel to clean Markdown with inline tables and page-cited RAG chunks.

***

*Made by Lindenwerk Data. Data: SAM.gov, U.S. General Services Administration.*

# Changelog

This Actor's version history is a separate document: https://apify.com/lindenwerk/sam-gov-contract-matcher/changelog.md

# Actor input Schema

## `mode` (type: `string`):

monitor = only solicitations not returned by a previous run with the same stateKey (new or amended); this is the default when the field is omitted, e.g. in API calls. search = every matching opportunity posted in the last postedWithinDays days (or since postedFrom). awards = Award Notices only, with awardee and amount. The form opens with search (last 7 days) so a first try shows results right away; switch to monitor before you schedule it.

## `profileName` (type: `string`):

Free label copied to every row. Also the default stateKey in monitor mode, so different profiles keep separate "already seen" lists. Example: "cyber-sdvosb-va".

## `keywords` (type: `array`):

Words matched in the title (full weight) and in the description (lower weight). Case-insensitive. Keywords with 4+ letters also match longer words starting with them ("cyber" matches "cybersecurity"); shorter ones (IT, AV) only as whole words. Wrap in double quotes to force whole-word matching (""roof"" will not match "roofing").

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

NAICS codes or prefixes. A 6-digit code matches exactly (strongest score); shorter prefixes match every code that starts with them ("5415" = computer systems design, "2382" = building equipment contractors, "5617" = services to buildings). With no keywords, only rows that match a code are returned.

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

Product Service Code prefixes (SAM.gov "ClassificationCode"): "D" = IT services, "DA01", "R4" = professional support, "Z2" = real property repair, "S2" = housekeeping, "70" = IT equipment. Weaker evidence than NAICS.

## `setAsides` (type: `array`):

Keep only opportunities with these set-asides. Use families: "small-business", "8a", "hubzone", "sdvosb", "wosb" (incl. EDWOSB), "veteran" (VA), "indian", "local", "none" (= unrestricted / full and open), or exact SAM.gov codes such as "SBA", "SBP", "8A", "8AN", "HZC", "SDVOSBC", "WOSB", "EDWOSB", "VSA". Empty = all.

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

Keep only opportunities whose department, sub-tier or office contains one of these texts (case-insensitive), e.g. \["Veterans Affairs", "Army Corps of Engineers", "NASA"], or an exact CGAC / office (AAC) code such as "036" or "W912DY".

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

2-letter US state / territory codes, e.g. \["VA", "MD", "DC"]. Matches the place of performance; if a notice gives none, the contracting office state is used (and said so in matchReasons).

## `noticeTypes` (type: `array`):

Which SAM.gov notice types to return. Default: solicitation, presolicitation, sources-sought. Awards mode always uses award.

## `onlyOpen` (type: `boolean`):

Drop opportunities whose response deadline has passed. Notices without a deadline are kept.

## `deadlineWithinDays` (type: `integer`):

Keep only opportunities whose response deadline is at most this many days away (notices without a deadline are dropped). 0 or empty = no limit.

## `minDaysUntilDeadline` (type: `integer`):

Drop opportunities due sooner than this many days (too little time to bid). 0 or empty = off.

## `excludeKeywords` (type: `array`):

Opportunities whose title or description contains any of these words are dropped (never charged).

## `minScore` (type: `integer`):

Only opportunities with relevanceScore >= minScore are returned (and charged as scored-match). Guide for a NAICS + keywords profile: exact NAICS only = 50, one title keyword only = 46, NAICS + one title keyword = 73, NAICS + two title keywords = 85+. A NAICS-only profile scores 100 for an exact code. Ignored for an unscored feed.

## `competitorNames` (type: `array`):

Awards mode: only return awards won by a company whose name contains one of these texts (case-insensitive), e.g. \["Booz Allen", "Leidos"].

## `minAwardAmount` (type: `integer`):

Awards mode: drop awards below this amount (awards without an amount are dropped too).

## `maxAwardAmount` (type: `integer`):

Awards mode: drop awards above this amount.

## `lookbackDays` (type: `integer`):

Monitor mode: how many posting days the first run looks back. Later runs continue from the newest posting day of the previous run (with one day overlap); already delivered solicitations are skipped and not charged.

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

Search and awards mode: only notices posted in the last N days. Default 30 (the form prefills 7). 0 = all active notices in SAM.gov (reads the whole ~210 MB extract, slower). Shorter windows are faster and cheaper.

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

Optional first posting day (YYYY-MM-DD). Overrides postedWithinDays / lookbackDays.

## `includeUpdates` (type: `boolean`):

Monitor mode: also return a solicitation again when SAM.gov posts a newer version (amendment, presolicitation -> solicitation); such rows have changeType = "updated".

## `maxResults` (type: `integer`):

Maximum number of rows to return (and charge) in this run, best matches first. 0 = no limit. Default 200; the form prefills 25 for a cheap first try, so raise it (or set 0) for full results. The run also stops when your maximum total charge for the run is reached. Monitor mode delivers the rest next run.

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

Order of the output rows.

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

Include the notice description (personal names, e-mails and phone numbers removed).

## `descriptionMaxLength` (type: `integer`):

Maximum number of characters of the description per row.

## `stateKey` (type: `string`):

Name of the "already seen" list kept in the named key-value store "sam-gov-matcher-state". Default: the profile name. Use a different key per profile or schedule.

## `resetState` (type: `boolean`):

Forget everything seen by earlier runs with this stateKey (use it after changing the profile).

## `firstRunBackfillDays` (type: `integer`):

Monitor mode: if a run without saved state (the very first run for this stateKey, or after Reset monitor state) finds nothing new in its lookbackDays window, for example because it starts on a US weekend or federal holiday, it looks back this many posting days once instead, so you see the current open opportunities. Later runs are normal monitor runs. Not used with postedFrom. 0 = off.

## `samApiKey` (type: `string`):

Optional. The public extract is updated once a day, so the newest notices can be ~1 day old. With your own SAM.gov public API key (generated in your SAM.gov account under Account Details) the Actor also fetches notices posted since the extract was built (up to 2 API requests per run; API rows have no description, so keywords match their title only). The key is stored encrypted, used only in your run and never logged or output.

## Actor input object example

```json
{
  "mode": "search",
  "keywords": [
    "cybersecurity",
    "network",
    "software"
  ],
  "naicsCodes": [
    "541512",
    "5415"
  ],
  "onlyOpen": true,
  "minScore": 40,
  "lookbackDays": 3,
  "postedWithinDays": 7,
  "includeUpdates": true,
  "maxResults": 25,
  "sortBy": "relevance",
  "includeDescription": true,
  "descriptionMaxLength": 600,
  "resetState": false,
  "firstRunBackfillDays": 7
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset with one flat row per solicitation

## `runSummary` (type: `string`):

Extract download, version collapsing, filter statistics and runtime

# 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 = {
    "mode": "search",
    "keywords": [
        "cybersecurity",
        "network",
        "software"
    ],
    "naicsCodes": [
        "541512",
        "5415"
    ],
    "postedWithinDays": 7,
    "maxResults": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("lindenwerk/sam-gov-contract-matcher").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 = {
    "mode": "search",
    "keywords": [
        "cybersecurity",
        "network",
        "software",
    ],
    "naicsCodes": [
        "541512",
        "5415",
    ],
    "postedWithinDays": 7,
    "maxResults": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("lindenwerk/sam-gov-contract-matcher").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 '{
  "mode": "search",
  "keywords": [
    "cybersecurity",
    "network",
    "software"
  ],
  "naicsCodes": [
    "541512",
    "5415"
  ],
  "postedWithinDays": 7,
  "maxResults": 25
}' |
apify call lindenwerk/sam-gov-contract-matcher --silent --output-dataset

```

## MCP server setup

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

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/8UicZ90IXb4IrFUg2/builds/eyzNUOLdVG6gVd2mp/openapi.json
