# US Government RFP Monitor - SAM.gov + State Portals (`lowlanddata/us-rfp-monitor`) Actor

Get alerted to every new US government RFP that matches your business. Monitors SAM.gov (official API) plus the Virginia, Texas and Florida portals; filters by keywords, NAICS/PSC, agency and set-aside; alerts only on never-seen opportunities.
Clean output, no officer contact data.

- **URL**: https://apify.com/lowlanddata/us-rfp-monitor.md
- **Developed by:** [Lowland Data](https://apify.com/lowlanddata) (community)
- **Categories:** Lead generation, Automation, Agents
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 baseline results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## US Government RFP Monitor — SAM.gov + State Portals

Get every new government RFP that matches your business — and only the new ones.
This actor watches **SAM.gov (all US federal opportunities)** plus the **Virginia,
Texas and Florida** state procurement portals, matches each notice against your
profile (keywords, NAICS/PSC codes, agencies, set-asides, states), and pushes an
alert item for every opportunity it has never shown you before. Schedule it daily
and plug the dataset into email, Slack or your CRM — bid discovery on autopilot.

### Quick start (30 seconds, no account keys needed)

1. Put a few words describing what you sell into **keywords** — e.g. `cybersecurity`.
2. Click **Start**. The first run shows everything currently open that matches
   (your baseline) from the three state portals — no API key needed for those.
3. Add your free **SAM.gov API key** (walkthrough below) to include all US federal
   opportunities, and create a daily **Schedule** so new matches come to you.

### Who uses this

- **GovCon business developers** monitoring "cybersecurity" + NAICS 541512 across
  DoD: a daily run surfaces the three new solicitations that matter instead of
  the ~1,700 notices the government posts every day.
- **Proposal teams** that keep losing recompetes to faster movers: alert on
  `sources-sought` and `presolicitation` stages to engage before the RFP drops,
  when the requirements are still shapeable.
- **B2G consultants** running pipelines for several clients: one scheduled task
  per client profile, each with its own memory — no overlap, no missed notices,
  no copy-pasting from five portal tabs every morning.
- **Small businesses with a set-aside advantage**: filter on "8(a)", "WOSB" or
  "SDVOSB" and see only the opportunities reserved for you.
- **Market analysts** tracking who wins: switch `noticeTypes` to `award` and
  collect award amounts and winning companies per agency.

### What you get

One JSON object per matching opportunity:

```json
{
  "source": "sam.gov",
  "sourceId": "ab12cd34ef56ab12cd34ef56ab12cd34",
  "title": "Cybersecurity Assessment Services for Regional Offices",
  "solicitationNumber": "W912DY-26-R-0042",
  "agency": "DEPT OF DEFENSE.DEPT OF THE ARMY.AMC.ACC.ACC-RSA",
  "noticeType": "solicitation",
  "naicsCode": "541512",
  "pscCode": "D310",
  "setAside": "Total Small Business Set-Aside (FAR 19.5)",
  "postedDate": "2026-07-15",
  "responseDeadline": "2026-08-15T16:00:00-05:00",
  "state": "AL",
  "city": "Huntsville",
  "url": "https://sam.gov/opp/ab12cd34ef56ab12cd34ef56ab12cd34/view",
  "isBaseline": false,
  "isUpdate": false
}
````

### How much does RFP monitoring cost?

Pay per event — you pay for alerts, not for runs that find nothing:

- **New matching opportunity (the alert): $0.02** — also for amendment alerts.
- Baseline item (first run only): $0.002.
- Actor start: $0.005. Everything included - there are no separate platform-usage
  charges, and higher Apify plans get automatic unit-price discounts.

Cost feel: a focused federal profile (one NAICS family) surfaces ~10–30 new
opportunities/day → roughly **$6–18/month** for complete, deduplicated coverage
of your niche — with the one-time baseline (say 300 items) costing about $0.60.
A run with zero new matches costs $0.005 all-in.

#### Why an alert costs more than a scraped row

The store's headline ("from $2.00 / 1,000 baseline results") is what a trial
costs: a one-time 300-item baseline ≈ $0.60. Apify displays every event per
1,000, so in the pricing table the alert event reads "$20.00 / 1,000" next to
raw scrapers at $2–3 per 1,000 records — but that comparison is between two
different things:

- A **raw scraper** sells rows. To monitor with one, you re-download the
  federal feed every day (~1,700 notices/day), then dedupe and match it
  yourself: ~50,000 records/month ≈ **$150/month** in scraping fees before you
  have written a single line of your own filtering code.
- **This monitor** sells outcomes: it remembers what you have seen, matches
  your profile, and charges only for the handful of *new* opportunities that
  concern you — the $6–18/month above for the same end result.

You never buy 1,000 alerts at once; nobody's pipeline produces that. The
per-1,000 figure is a display convention, not a bill you will meet. The one
setup that does get expensive is a profile with **no filters at all** — the
entire federal firehose is ~~500+ matches/day (~~$10+/day). If that's genuinely
what you want, set `maxItems` and your run's maximum cost accordingly;
otherwise add keywords or codes and let the monitor do its job: fewer, better
alerts.

### The SAM.gov API key (free; ~10 minutes the first time)

SAM.gov — the official portal for all US federal contract opportunities — gives
every user a **personal, free API key** for its public data API. This actor uses
the official API (not website scraping), which SAM.gov's terms require, and the
same terms forbid sharing keys — so the actor can't ship with one built in. Each
user brings their own:

1. Go to [sam.gov](https://sam.gov) → **Sign In**. Accounts run through
   **login.gov**, and first-time signup is a real government onboarding: an
   email confirmation link, a password, a second factor (authenticator app,
   security key or backup codes), and a couple of confirmation screens bouncing
   you between login.gov and SAM.gov. Budget ~10 minutes once; no business
   registration is needed.
2. Signed in on **sam.gov** (not on login.gov — that site only handles the
   sign-in), open your profile icon (top right) → **Account Details**
   ([sam.gov/profile/details](https://sam.gov/profile/details)).
3. In the **Public API Key** section, re-enter your password — the key appears
   (it starts with `SAM-`). Copy it into the actor's `samApiKey` input. It's
   stored encrypted and never shown in logs.
4. **The key is valid for 90 days.** SAM.gov emails you before it expires;
   renewing is the same page — copy the fresh key into the actor input.

Your key allows **10 API requests/day** (SAM.gov's limit for standard accounts).
A typical monitor run uses 1–3 requests, so a daily schedule fits comfortably.
Users with a SAM.gov role (e.g. an entity registration) get 1,000/day. The three
state portals need no key at all — without `samApiKey` the actor simply monitors
those and reminds you that federal coverage is off.

### Not technical? Let your AI assistant set it up

Copy the block below into ChatGPT, Claude, or any AI assistant, add one line
about what your company sells, and follow the conversation — it will produce
your exact input settings and walk you through the two accounts you need.

```text
Help me set up the "US Government RFP Monitor" actor on Apify
(https://apify.com/lowlanddata/us-rfp-monitor). Walk me through it one step at
a time, asking me questions where needed.

What my company sells / the contracts I want to find: [DESCRIBE IT HERE]

Steps to guide me through:
1. Ask me about my business, then propose: keywords (matched against notice
   titles, descriptions and commodity-code labels), NAICS codes (my industry,
   2-6 digits), and optionally PSC codes, agencies, set-aside types (like
   "small business", "8(a)", "WOSB"), and US states.
2. Tell me to create a free account at https://sam.gov (sign-in goes through
   login.gov: expect an email confirmation link, a password, a one-time-code
   second factor, and a few confirmation screens - about 10 minutes). Then, back
   on sam.gov: profile icon > "Account Details" > re-enter my password > copy
   the "Public API Key" (starts with SAM-). It is free, it is not a payment
   method, I should keep it private, and it is valid for 90 days (sam.gov
   emails me before it expires; renewing is copying the fresh key from the
   same page).
3. Tell me to create an Apify account at https://apify.com if I don't have one.
4. Give me the final input as a filled-in list matching the actor's input form:
   keywords, naicsCodes, samApiKey (I paste my key there), and leave the other
   fields on their defaults. Explain that the first run shows everything
   currently open that matches (the "baseline"), and every run after that only
   alerts on new opportunities.
5. Tell me how to create a daily Schedule for the actor in the Apify Console
   (Schedules > Create new > pick this actor, paste the input), and how to add
   an email or Slack notification integration on the actor's runs so alerts
   reach me automatically.
6. Warn me: the SAM.gov key allows 10 API calls per day, so one or two
   scheduled runs per day is the right cadence; the key expires every 90 days
   and sam.gov emails me before that - I just copy the fresh key into the
   actor input.
```

### How the monitor works

- **First run (baseline):** the actor fetches everything currently matching your
  profile, pushes it flagged `isBaseline: true` (so you start with today's full
  picture), and memorizes what it saw. Baseline items are charged at a fraction
  of the alert price. Prefer to start silent? Set `emitBaseline: false`.
- **Every following run:** only opportunities the monitor has never seen before
  are pushed — that is the alert you pay for. Re-running twice a day never
  duplicates an alert; deleted or re-fetched runs never replay old notices.
- **Amendments:** with `alertOn: "new-and-updated"` you also get one item
  (flagged `isUpdate: true`) when a seen opportunity changes — deadline moves,
  addenda, reposts.
- **Changing your profile** (keywords, codes, agencies…) starts a fresh baseline
  for the new profile. Cosmetic settings (`maxItems`, `emitBaseline`) don't.
- **Memory** lives in a named key-value store (`rfp-monitor-state`) in your Apify
  account, one record per profile. `resetState: true` wipes the current profile's
  memory and re-baselines.

### Sources and honest coverage notes

| Source       | Coverage                     | Codes            | Description text           | Key needed    |
| ------------ | ---------------------------- | ---------------- | -------------------------- | ------------- |
| SAM.gov      | All US federal opportunities | NAICS + PSC      | Linked (on sam.gov)        | Your free key |
| Virginia eVA | State of Virginia            | Commodity codes  | **Full text included**     | No            |
| Texas ESBD   | State of Texas               | NIGP with labels | Linked (on txsmartbuy.gov) | No            |
| Florida MFMP | State of Florida             | —                | Linked (on the portal)     | No            |

- **"New" means new to your profile's monitor state**, not "posted today". The
  first run baselines; later runs alert on anything matching that appears —
  including notices you'd have missed while the actor wasn't running.
- **NAICS/PSC filters apply to SAM.gov records.** State portals classify with
  NIGP/commodity codes instead, so when you set `naicsCodes`/`pscCodes`, records
  without those codes (all state-portal records) are excluded. For cross-source
  profiles use `keywords` — they match titles, descriptions **and the plain-
  English commodity-code labels** (e.g. "roofing" hits NIGP 91066).
- **Keyword matching runs on what the list APIs return**: full descriptions for
  Virginia, title + code labels for the others. A keyword hiding only deep in a
  SAM.gov description PDF won't match — pair keywords with codes for recall.
- More states are on the roadmap; the four sources above are what you get today.

### Input reference

| Field            | What it does                                               | Default             |
| ---------------- | ---------------------------------------------------------- | ------------------- |
| `keywords`       | Any-of match on title/description/code labels              | `[]` (match all)    |
| `samApiKey`      | Your SAM.gov key (see above)                               | —                   |
| `sources`        | Which of the 4 sources to monitor                          | all                 |
| `naicsCodes`     | NAICS prefix match, e.g. `5415` matches `541511` (SAM.gov) | `[]`                |
| `pscCodes`       | PSC exact match (SAM.gov)                                  | `[]`                |
| `agencies`       | Substring match on the buying organization                 | `[]`                |
| `setAsides`      | Substring match on the set-aside label                     | `[]`                |
| `noticeTypes`    | Notice stages to alert on                                  | pre-award stages    |
| `states`         | Two-letter codes; gates portals + SAM place of performance | `[]` (all)          |
| `lookbackDays`   | SAM.gov fetch window per run; keep ≥ run cadence           | 3                   |
| `alertOn`        | `new` or `new-and-updated`                                 | `new`               |
| `emitBaseline`   | Push the current open set on the first run                 | `true`              |
| `resetState`     | One-shot: wipe this profile's memory, re-baseline          | `false`             |
| `maxItems`       | Per-run push cap; overflow alerts next run                 | 500                 |
| `stateStoreName` | Key-value store holding monitor memory                     | `rfp-monitor-state` |

### Scheduling (the intended way to run this)

Create an Apify **Schedule** that runs the actor daily (e.g. 7:00 AM ET, after
the previous day's federal notices have settled) with your profile as the input.
Then wire the alerts wherever you work:

- **Email/Slack**: add an Apify integration or webhook on run succeeded; the run
  status message always says how many new matches were pushed.
- **CRM/pipeline import**: read the dataset via the API (below) — the output is
  flat, stable and safe to store (see compliance).
- Two or three profiles? Create one saved task per profile, each on its own
  schedule. Their memories never interfere.

### Use it from your code

```bash
curl -s "https://api.apify.com/v2/acts/lowlanddata~us-rfp-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -X POST -H 'Content-Type: application/json' \
  -d '{"keywords": ["janitorial"], "sources": ["virginia-eva", "texas-esbd", "florida-mfmp"], "maxItems": 50}'
```

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('lowlanddata/us-rfp-monitor').call({
  keywords: ['cybersecurity', 'zero trust'],
  naicsCodes: ['541512'],
  samApiKey: process.env.SAM_API_KEY,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("lowlanddata/us-rfp-monitor").call(run_input={
    "keywords": ["roofing", "waterproofing"],
    "states": ["TX", "FL"],
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

Note for agent/sync use: `run-sync-get-dataset-items` waits up to 5 minutes. A
multi-source run typically finishes in 1–3 minutes; keep `maxItems` bounded.

### Use it with AI agents (MCP)

Claude, Cursor and other MCP-capable agents can run this monitor as a tool
through Apify's hosted MCP server: the agent sets the profile, starts the run
and reads the new opportunities — no glue code.

Claude Code:

```bash
claude mcp add apify --transport http "https://mcp.apify.com?actors=lowlanddata/us-rfp-monitor"
```

Cursor or Claude Desktop (add a custom connector / MCP server with this URL):

```text
https://mcp.apify.com?actors=lowlanddata/us-rfp-monitor
```

Sign in with your Apify account when prompted — runs are billed to it. Setup
details per client: [Apify MCP docs](https://docs.apify.com/platform/integrations/mcp),
or start from this actor's own MCP page:
[apify.com/lowlanddata/us-rfp-monitor/api/mcp](https://apify.com/lowlanddata/us-rfp-monitor/api/mcp).

Prompts that work once connected:

- "Check the state portals for new janitorial RFPs matching my profile and summarize deadlines."
- "Run my cybersecurity RFP watch and draft a go/no-go note for each new opportunity."
- "List every new Texas solicitation with NIGP codes related to roofing, sorted by close date."

### Is it legal to monitor SAM.gov and state portals?

Yes — and this actor takes the sanctioned route on purpose. SAM.gov is accessed
through its **official Get Opportunities API with your own free key** (scraping
the SAM.gov website is prohibited by its terms — several competing tools do it
anyway; this one does not). The three state portals are open public-procurement
systems whose data exists precisely to be found by vendors. Government
procurement data is public information about organizations; the personal-data
question is answered structurally — see the compliance section below.

### Compliance: safe to store, safe to CRM-import

- **Zero personal data in the output fields — by architecture, not by promise.**
  Every field is whitelisted; the contracting-officer and buyer contact fields
  (names, emails, phone numbers) present in the raw sources are structurally
  unreachable and covered by an automated test. You can store, share and import
  the dataset without GDPR headaches. (Data about organizations — agencies,
  awardee companies — is not personal data.)
- Honest limit: the guarantee covers data *fields*. Text the publishing agency
  writes itself (title, description) is delivered as-is — occasionally an agency
  types a contact into its own description, exactly as it appears on the public
  notice.
- **Officially sanctioned access.** SAM.gov is queried exclusively through its
  documented public API with your own key, as its terms require — never by
  scraping the website. State portals are queried through the same public,
  login-free endpoints their own search pages use, at a respectful pace.
- Government procurement notices are public records published to reach vendors;
  this actor changes the delivery, not the audience.

### Troubleshooting

| Run status says                                                | What it means / what to do                                                                                                 |
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `SAM.gov is the only selected source but samApiKey is missing` | Add your free key (see the API-key section) or select a state portal source.                                               |
| `SAM.gov skipped: add your free samApiKey`                     | The run worked, but only state portals were monitored. Add the key for federal coverage.                                   |
| `SAM.gov rate limit reached`                                   | Your key's 10 requests/day are spent. Run once daily, reduce `naicsCodes`, or lower `lookbackDays`. Resets daily.          |
| `SAM.gov rejected the API key`                                 | Key typo or expired key — SAM.gov keys rotate every 90 days; copy a fresh one from Account Details.                        |
| `Monitor state record ... is not readable`                     | State written by a newer actor version. Run once with `resetState: true`.                                                  |
| `... returned an unexpected response shape`                    | A portal changed its markup/API. The actor fails loudly rather than alerting nothing — check for an updated actor version. |
| `SAM.gov coverage truncated by the per-run request budget`     | Your profile needs more than 8 API calls per run. Split it into multiple scheduled tasks or reduce codes.                  |
| `N more matched but hit the maxItems cap`                      | Nothing is lost — they alert on the next run. Raise `maxItems` for bigger batches.                                         |

### FAQ

**Do I need a SAM.gov account?** Only for the federal source — a free account
and API key (~10 minutes, walkthrough above). The three state portals work with
zero setup, so you can evaluate the monitor before touching SAM.gov.

**Why did my second run return nothing?** Because nothing new matched — that is
the product working. You pay $0.005 for the run instead of re-buying yesterday's
opportunities. Check the run status line: it says how many were seen and skipped.

**Can I watch multiple profiles?** Yes — one Schedule per profile. Each profile
keeps its own memory, so a cybersecurity watch and a janitorial watch never
cross-alert.

**Does it include contracting-officer contact details?** No — by design. Officer
names, emails and phones are structurally excluded, which is what makes the
output safe to store and CRM-import without review.

**What happens if a state portal goes down?** The run says so loudly and
continues with the other sources; the portal's notices alert automatically once
it recovers. You never silently miss a window.

**Can I export to Excel or my CRM?** Yes — CSV, Excel, JSON or XML from the
Console or API, and the flat, stable field names import cleanly.

### Support

Questions, another state portal you need, or a field you're missing? Open an
issue on the actor's page — reports land directly with the maintainer.

# Actor input Schema

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

Alert on opportunities whose title, description or commodity-code labels contain ANY of these words (case-insensitive). Example: \["cybersecurity", "penetration testing", "SOC"]. Leave empty to match everything and filter by codes or agencies instead.

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

Your personal SAM.gov API key (starts with SAM-), required only for the SAM.gov federal source. Free: sam.gov account (via login.gov) > Account Details > re-enter password > copy the Public API Key. Valid 90 days, renewal is copy-paste. Without it the actor still monitors the state portals and skips SAM.gov with a warning. Stored encrypted, never logged. See the README for the step-by-step.

## `sources` (type: `array`):

Which procurement sources to monitor. SAM.gov covers all US federal opportunities (needs your free API key); the state portals cover Virginia (eVA), Texas (ESBD) and Florida (MFMP) and need no key.

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

NAICS industry codes, 2-6 digits, hierarchical: "5415" also matches 541511. Applies to SAM.gov records (state portals use other code systems - use keywords for them). Example: \["541512", "541519"]. Also narrows the SAM.gov API query, saving your daily request budget.

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

Federal Product Service Codes, exact match, e.g. \["D310", "R425"]. SAM.gov records only. An opportunity matches when it hits ANY of your NAICS or PSC codes.

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

Only alert on these buying organizations (case-insensitive substring of the agency path). Example: \["DEPT OF DEFENSE", "University of Houston"].

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

Only alert on set-aside opportunities whose label contains any of these, e.g. \["small business", "8(a)", "WOSB", "SDVOSB"]. SAM.gov and Virginia (SWaM) carry set-aside labels; records without one are excluded when this filter is set.

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

Which notice stages to alert on. Default: the pre-award stages a seller can still act on (solicitations, combined synopsis, presolicitations, sources sought). Add award notices to track who wins instead.

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

Two-letter US state codes, e.g. \["TX", "VA"]. Filters SAM.gov by place of performance and switches off state portals outside the list. Empty = whole country.

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

How many days back each run asks SAM.gov for notices. Keep it a little above your run cadence (daily run: 2-3 days) - overlap is safe, the monitor never re-alerts a seen notice.

## `alertOn` (type: `string`):

"new" alerts once per opportunity. "new-and-updated" also re-alerts when a seen opportunity is amended (marked isUpdate: true), e.g. a moved deadline or a posted addendum.

## `emitBaseline` (type: `boolean`):

On the very first run of a profile, push the currently open matching opportunities (flagged isBaseline: true, charged at the lower baseline price) so you start with today's full picture. Turn off to seed silently and only ever receive new alerts.

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

Discard this profile's memory of already-seen opportunities and start over with a fresh baseline run. One-shot switch: turn it back off afterwards.

## `maxItems` (type: `integer`):

Upper bound on items pushed per run (baseline or alerts). Matches beyond the cap are not lost: they alert on the next run.

## `stateStoreName` (type: `string`):

Named key-value store holding the monitor's seen-opportunity memory. Only change this to run fully isolated monitor instances.

## Actor input object example

```json
{
  "keywords": [
    "cybersecurity",
    "network security"
  ],
  "sources": [
    "sam.gov",
    "virginia-eva",
    "texas-esbd",
    "florida-mfmp"
  ],
  "noticeTypes": [
    "solicitation",
    "combined",
    "presolicitation",
    "sources-sought"
  ],
  "lookbackDays": 3,
  "alertOn": "new",
  "emitBaseline": true,
  "resetState": false,
  "maxItems": 500,
  "stateStoreName": "rfp-monitor-state"
}
```

# Actor output Schema

## `opportunities` (type: `string`):

Government contract opportunities matching your profile, one JSON object per opportunity. On the first run: the current open baseline; afterwards: only new (or amended) matches.

# 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": [
        "cybersecurity",
        "network security"
    ],
    "sources": [
        "sam.gov",
        "virginia-eva",
        "texas-esbd",
        "florida-mfmp"
    ],
    "noticeTypes": [
        "solicitation",
        "combined",
        "presolicitation",
        "sources-sought"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lowlanddata/us-rfp-monitor").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": [
        "cybersecurity",
        "network security",
    ],
    "sources": [
        "sam.gov",
        "virginia-eva",
        "texas-esbd",
        "florida-mfmp",
    ],
    "noticeTypes": [
        "solicitation",
        "combined",
        "presolicitation",
        "sources-sought",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("lowlanddata/us-rfp-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    "cybersecurity",
    "network security"
  ],
  "sources": [
    "sam.gov",
    "virginia-eva",
    "texas-esbd",
    "florida-mfmp"
  ],
  "noticeTypes": [
    "solicitation",
    "combined",
    "presolicitation",
    "sources-sought"
  ]
}' |
apify call lowlanddata/us-rfp-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=lowlanddata/us-rfp-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "US Government RFP Monitor - SAM.gov + State Portals",
        "description": "Get alerted to every new US government RFP that matches your business. Monitors SAM.gov (official API) plus the Virginia, Texas and Florida portals; filters by keywords, NAICS/PSC, agency and set-aside; alerts only on never-seen opportunities.\nClean output, no officer contact data.",
        "version": "0.1",
        "x-build-id": "pc3tp1VvPIKvUImrJ"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/lowlanddata~us-rfp-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-lowlanddata-us-rfp-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/lowlanddata~us-rfp-monitor/runs": {
            "post": {
                "operationId": "runs-sync-lowlanddata-us-rfp-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/lowlanddata~us-rfp-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-lowlanddata-us-rfp-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "keywords": {
                        "title": "Keywords",
                        "type": "array",
                        "description": "Alert on opportunities whose title, description or commodity-code labels contain ANY of these words (case-insensitive). Example: [\"cybersecurity\", \"penetration testing\", \"SOC\"]. Leave empty to match everything and filter by codes or agencies instead.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "samApiKey": {
                        "title": "SAM.gov API key",
                        "type": "string",
                        "description": "Your personal SAM.gov API key (starts with SAM-), required only for the SAM.gov federal source. Free: sam.gov account (via login.gov) > Account Details > re-enter password > copy the Public API Key. Valid 90 days, renewal is copy-paste. Without it the actor still monitors the state portals and skips SAM.gov with a warning. Stored encrypted, never logged. See the README for the step-by-step."
                    },
                    "sources": {
                        "title": "Sources",
                        "type": "array",
                        "description": "Which procurement sources to monitor. SAM.gov covers all US federal opportunities (needs your free API key); the state portals cover Virginia (eVA), Texas (ESBD) and Florida (MFMP) and need no key.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "sam.gov",
                                "virginia-eva",
                                "texas-esbd",
                                "florida-mfmp"
                            ],
                            "enumTitles": [
                                "SAM.gov (US federal)",
                                "Virginia eVA",
                                "Texas ESBD",
                                "Florida MFMP"
                            ]
                        },
                        "default": [
                            "sam.gov",
                            "virginia-eva",
                            "texas-esbd",
                            "florida-mfmp"
                        ]
                    },
                    "naicsCodes": {
                        "title": "NAICS codes",
                        "type": "array",
                        "description": "NAICS industry codes, 2-6 digits, hierarchical: \"5415\" also matches 541511. Applies to SAM.gov records (state portals use other code systems - use keywords for them). Example: [\"541512\", \"541519\"]. Also narrows the SAM.gov API query, saving your daily request budget.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "pscCodes": {
                        "title": "PSC codes",
                        "type": "array",
                        "description": "Federal Product Service Codes, exact match, e.g. [\"D310\", \"R425\"]. SAM.gov records only. An opportunity matches when it hits ANY of your NAICS or PSC codes.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "agencies": {
                        "title": "Agencies",
                        "type": "array",
                        "description": "Only alert on these buying organizations (case-insensitive substring of the agency path). Example: [\"DEPT OF DEFENSE\", \"University of Houston\"].",
                        "items": {
                            "type": "string"
                        }
                    },
                    "setAsides": {
                        "title": "Set-aside types",
                        "type": "array",
                        "description": "Only alert on set-aside opportunities whose label contains any of these, e.g. [\"small business\", \"8(a)\", \"WOSB\", \"SDVOSB\"]. SAM.gov and Virginia (SWaM) carry set-aside labels; records without one are excluded when this filter is set.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "noticeTypes": {
                        "title": "Notice types",
                        "type": "array",
                        "description": "Which notice stages to alert on. Default: the pre-award stages a seller can still act on (solicitations, combined synopsis, presolicitations, sources sought). Add award notices to track who wins instead.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "solicitation",
                                "combined",
                                "presolicitation",
                                "sources-sought",
                                "special-notice",
                                "justification",
                                "award"
                            ],
                            "enumTitles": [
                                "Solicitation",
                                "Combined synopsis/solicitation",
                                "Presolicitation",
                                "Sources sought",
                                "Special notice",
                                "Justification (J&A)",
                                "Award notice"
                            ]
                        },
                        "default": [
                            "solicitation",
                            "combined",
                            "presolicitation",
                            "sources-sought"
                        ]
                    },
                    "states": {
                        "title": "US states",
                        "type": "array",
                        "description": "Two-letter US state codes, e.g. [\"TX\", \"VA\"]. Filters SAM.gov by place of performance and switches off state portals outside the list. Empty = whole country.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "lookbackDays": {
                        "title": "Lookback window (days)",
                        "minimum": 1,
                        "maximum": 30,
                        "type": "integer",
                        "description": "How many days back each run asks SAM.gov for notices. Keep it a little above your run cadence (daily run: 2-3 days) - overlap is safe, the monitor never re-alerts a seen notice.",
                        "default": 3
                    },
                    "alertOn": {
                        "title": "Alert on",
                        "enum": [
                            "new",
                            "new-and-updated"
                        ],
                        "type": "string",
                        "description": "\"new\" alerts once per opportunity. \"new-and-updated\" also re-alerts when a seen opportunity is amended (marked isUpdate: true), e.g. a moved deadline or a posted addendum.",
                        "default": "new"
                    },
                    "emitBaseline": {
                        "title": "Emit baseline on first run",
                        "type": "boolean",
                        "description": "On the very first run of a profile, push the currently open matching opportunities (flagged isBaseline: true, charged at the lower baseline price) so you start with today's full picture. Turn off to seed silently and only ever receive new alerts.",
                        "default": true
                    },
                    "resetState": {
                        "title": "Reset monitor state",
                        "type": "boolean",
                        "description": "Discard this profile's memory of already-seen opportunities and start over with a fresh baseline run. One-shot switch: turn it back off afterwards.",
                        "default": false
                    },
                    "maxItems": {
                        "title": "Max items per run",
                        "minimum": 1,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Upper bound on items pushed per run (baseline or alerts). Matches beyond the cap are not lost: they alert on the next run.",
                        "default": 500
                    },
                    "stateStoreName": {
                        "title": "State store name",
                        "type": "string",
                        "description": "Named key-value store holding the monitor's seen-opportunity memory. Only change this to run fully isolated monitor instances.",
                        "default": "rfp-monitor-state"
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
