# US Government Grants & Contract Opportunities Feed (`owlabee/gov-opportunity-feed`) Actor

Monitor federal grants and contract opportunities via the official Grants.gov and SAM.gov APIs, plus recent USAspending awards. Keyword search, normalized JSON, new/updated flags for scheduled runs and webhooks. Pay per opportunity record ($0.001).

- **URL**: https://apify.com/owlabee/gov-opportunity-feed.md
- **Developed by:** [Owlabee](https://apify.com/owlabee) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 opportunity scrapeds

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

## US Government Grants & Contract Opportunities Feed

### What it does

This Actor turns the official US federal opportunity APIs into one clean, keyword-filtered feed. It queries **Grants.gov** (federal grants, no key needed), **SAM.gov** (federal contract opportunities, free personal API key) and optionally **USAspending.gov** (who recently *won* similar grants and contracts), normalizes every record to the same field names, and flags what is **new or updated since your last run** so a daily schedule only emits changes.

It is built for grant writers, government-contracting business developers, SBIR/STTR applicants, nonprofits, universities, and anyone who wants an RFP alert feed without logging into three portals. Results land in an Apify dataset you can download as JSON, CSV or Excel, read over the API, push to a webhook, or query from an AI agent through the Apify MCP server.

Only official public APIs are used — no HTML scraping of sam.gov or grants.gov, no proxies, no login.

### How to use

1. Enter your **Keywords** (one per line), choose **Sources** (Grants.gov is on by default; add SAM.gov and paste your free **SAM.gov API key**), and set **Posted within (days)**.
2. Optionally turn on **Fetch Grants.gov details** (award ceiling/floor, funding, eligibilities, description), **Include recent USAspending awards**, and **Only new or updated since last run** for scheduled monitoring with a **Webhook URL**.
3. Click **Start**. Records appear in the dataset as they are saved; schedule the Actor daily to get a change feed.

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `keywords` | string\[] | yes | Search terms; each is searched in every enabled source |
| `sources` | string\[] | no | `grantsGov`, `samGov` (default `["grantsGov"]`) |
| `oppStatuses` | string\[] | no | Grants.gov statuses: `forecasted`, `posted`, `closed`, `archived` (default `["posted"]`) |
| `postedWithinDays` | integer | no | Only records posted in the last N days, 1–365 (default 30) |
| `maxResults` | integer | no | Cap on dataset items per run across all keywords and sources (default 500) |
| `fetchDetails` | boolean | no | Call Grants.gov `fetchOpportunity` for each emitted grant (default false) |
| `maxDetails` | integer | no | Cap on detail fetches per run (default 100) |
| `samApiKey` | string (secret) | for SAM.gov | Free personal SAM.gov public API key; SAM.gov is skipped when missing |
| `samFilters` | object | no | `{ "naics", "setAside", "state", "ptype" }` passed to SAM.gov as `ncode`, `typeOfSetAside`, `state`, `ptype` |
| `includeRecentAwards` | boolean | no | Add recent USAspending grant/contract awards for the keywords (default false) |
| `maxAwards` | integer | no | Cap on USAspending awards per keyword (default 50) |
| `onlyNewSinceLastRun` | boolean | no | Monitor mode: emit only new/updated records, flagged `isNew` / `isUpdated` (default false) |
| `stateStoreName` | string | no | Named key-value store holding monitor snapshots (default `gov-opportunity-feed-state`) |
| `webhookUrl` | string | no | POST target for the run summary and emitted items |

### Output example

Grants.gov opportunity (with `fetchDetails` on):

```json
{
  "source": "grantsGov",
  "recordType": "opportunity",
  "keyword": "small business",
  "opportunityId": "351083",
  "number": "SBA-OED-26-001",
  "title": "Small Business Development Centers Program",
  "agency": "Small Business Administration",
  "agencyCode": "SBA",
  "status": "posted",
  "docType": "synopsis",
  "postedDate": "2026-10-10",
  "closeDate": "2026-12-15",
  "lastUpdated": "2026-10-12T13:30:00.000Z",
  "assistanceListings": ["59.037"],
  "awardCeiling": null,
  "awardFloor": 0,
  "expectedAwards": 62,
  "estimatedTotalFunding": 125000000,
  "costSharing": true,
  "fundingCategories": ["Business and Commerce"],
  "fundingInstruments": ["Cooperative Agreement"],
  "eligibilities": ["Public and State controlled institutions of higher education"],
  "agencyContactName": "Jane Doe",
  "agencyContactEmail": "grants@sba.gov",
  "description": "The SBDC program provides counseling & training to small businesses. …",
  "url": "https://www.grants.gov/search-results-detail/351083",
  "detailsFetched": true,
  "isNew": true,
  "isUpdated": false,
  "fetchedAt": "2026-10-16T12:00:00.000Z"
}
```

SAM.gov notices carry `noticeId`, `solicitationNumber`, `department` / `subTier` / `office`, `type`, `responseDeadLine`, `naicsCode`, `setAside`, `placeOfPerformance`, `pointOfContact[]` (names, emails and phones exactly as published by the agency), `uiLink` as `url`, and `descriptionUrl`. USAspending awards carry `awardId`, `recipientName`, `recipientUei`, `awardAmount`, `awardType`, `startDate` / `endDate` and a usaspending.gov `url`. Every record shares `source`, `recordType`, `keyword`, `opportunityId`, `title`, `agency`, `status`, `postedDate`, `closeDate`, `url` and `fetchedAt`.

Error items look like `{ "source": "samGov", "keyword": "…", "error": "SAM_AUTH_FAILED", "errorMessage": "…" }` and are charged at the opportunity rate, because the work to discover the problem was done.

### Pricing

Pay per event. You only pay for what the Actor produces; set a **maximum cost per run** in the run options to cap spend.

| Event | Price | Notes |
|---|---|---|
| Opportunity scraped | $0.001 | Per dataset item: Grants.gov opportunity, SAM.gov notice, USAspending award, or error item |
| Opportunity detail fetched | $0.002 | Per successful Grants.gov `fetchOpportunity` call (only with `fetchDetails`) |
| Actor start | $0.00005 | Apify platform event |

**Example:** 1,000 opportunities ≈ **$1.00**. 1,000 opportunities with details ≈ $3.00. In monitor mode the first run is a baseline and is charged for every record it emits; later runs are charged only for new or updated records.

### Limits and fair use

- Requests are rate-limited per host (≤ 2 req/s) with exponential backoff on 429/5xx.
- **SAM.gov quotas:** a personal, non-federal API key has a small daily request allowance. The Actor asks for up to 1,000 notices per request so one keyword normally costs one request; keep the keyword list short on SAM.gov schedules.
- `postedWithinDays` applies to the Grants.gov open date, the SAM.gov `postedFrom`/`postedTo` window (max 1 year) and the USAspending award start date.
- `maxResults` caps records *fetched* per run; in monitor mode unchanged records are not emitted, so the number of dataset items can be lower.
- Grants.gov OR-matches the words of a keyword ("small business" matches "small" or "business"); SAM.gov matches the notice title.
- Memory: runs comfortably at 256 MB.

### How to get a free SAM.gov API key

1. Create or sign in to an account at [sam.gov](https://sam.gov) (any individual can register; no entity registration is needed).
2. Open your profile → **Account Details**.
3. Under **Public API Key**, click **Request** (or **Regenerate**), enter your password, and copy the key shown.
4. Paste it into the **SAM.gov API key** field of this Actor. It is stored encrypted as a secret input and never written to logs.

Keys are personal and tied to the daily quota of your account type; federal-agency and entity-associated accounts get larger allowances.

### Related Actors

Other pay-per-event Actors from the same account, all limited-permission and agent-friendly:

- [US Building Permits Scraper — City & County Permit Records](https://apify.com/owlabee/building-permits-scraper) — city and county building permits for the same construction and infrastructure leads.
- [US Professional License Lookup & Verification API](https://apify.com/owlabee/us-license-verifier) — verify the professional licenses of bidders and awardees from official state records.
- [Company Job Boards Scraper & Hiring Signals (ATS)](https://apify.com/owlabee/ats-hiring-signals) — spot contractors staffing up after an award through their public job boards.

### FAQ

**Does this need proxies or a login?** No. All three sources are official public JSON APIs that work from Apify's standard datacenter infrastructure. Only SAM.gov requires a (free) API key.

**How is cost controlled?** Set `maxResults`, `maxDetails` and `maxAwards`, plus the run's maximum total charge. The Actor charges each record the moment it is saved and stops cleanly when any limit is reached.

**How does "only new or updated" work?** For every source + keyword the Actor stores a compact fingerprint per opportunity in the named key-value store. On the next run, unseen ids are emitted with `isNew: true`; ids whose title, status, dates, set-aside or award fields changed are emitted with `isUpdated: true`; unchanged ones are skipped and not charged. Delete the store (or change `stateStoreName`) to reset.

**Can I run it on a schedule or call it from an AI agent?** Yes — use Apify Schedules with `onlyNewSinceLastRun` and a `webhookUrl`, the API, or the Apify MCP server. The Actor is pay-per-event and uses limited permissions, so agents can discover and call it.

**What happens on errors?** A failing source/keyword produces one error item (`FETCH_FAILED`, `SAM_AUTH_FAILED`, `SAM_RATE_LIMITED`, `SAM_HTTP_<status>`) instead of failing the run. A missing SAM.gov key skips that source with a log line.

**Why is `awardCeiling` null for some grants?** Grants.gov publishes "none" or leaves it empty when the agency did not set a ceiling; the Actor returns `null` rather than guessing.

### Legal

This Actor uses only the official public APIs of Grants.gov, SAM.gov and USAspending.gov; it does not scrape their websites. Use the data in compliance with each source's terms (SAM.gov data is subject to GSA's API terms of service and your own API key agreement) and applicable law. Points of contact are government employees' business contact details as published by the issuing agency. The Actor is not a consumer reporting agency and its output must not be used for employment, credit, insurance, housing or tenant screening decisions (FCRA).

# Actor input Schema

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

Search terms, one per line. Each keyword is searched in every enabled source. Grants.gov OR-matches the words of a keyword; SAM.gov matches the notice title. The prefilled value is the cheap example used by Apify's daily automated test.

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

Which official APIs to query. Grants.gov needs no key. SAM.gov needs the free personal API key in "SAM.gov API key" below and is skipped (with a log line) when the key is missing.

## `oppStatuses` (type: `array`):

Grants.gov statuses to include. "posted" = open for applications, "forecasted" = announced but not yet open.

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

Only opportunities posted in the last N days (1–365). For USAspending awards this is the award start-date window.

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

Upper bound on dataset items across all keywords and sources per run (opportunities, awards and error items). Use together with the run's maximum cost to control spend.

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

Call the Grants.gov fetchOpportunity endpoint for each emitted grant to add award ceiling/floor, estimated funding, expected awards, eligibilities, funding categories and the description. Each detail fetch is a separate paid event.

## `maxDetails` (type: `integer`):

Cap on Grants.gov detail fetches per run (only used when "Fetch Grants.gov details" is on).

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

Your free personal SAM.gov public API key (sam.gov → sign in → Account Details → Public API Key). Required only for the SAM.gov source. Stored encrypted and never logged.

## `samFilters` (type: `object`):

Optional SAM.gov filters: {"naics": "541511", "setAside": "SBA", "state": "VA", "ptype": "o"}. ptype codes: o = Solicitation, k = Combined Synopsis/Solicitation, p = Presolicitation, r = Sources Sought, s = Special Notice, a = Award Notice (comma-separate several). setAside codes: SBA, SBP, 8A, 8AN, HZC, HZS, SDVOSBC, SDVOSBS, WOSB, WOSBSS, EDWOSB, EDWOSBSS, LAS, IEE, ISBEE, BICiv, VSA, VSS.

## `includeRecentAwards` (type: `boolean`):

Also query USAspending.gov for grants and contracts recently awarded for the same keywords — useful to see who recently won similar awards. Each award is a dataset item charged at the opportunity rate.

## `maxAwards` (type: `integer`):

Cap on USAspending award records per keyword (only used when "Include recent USAspending awards" is on).

## `onlyNewSinceLastRun` (type: `boolean`):

Monitor mode for schedules: remember what was seen per source and keyword in the state store and emit only opportunities that are new or whose key fields changed, flagged with isNew / isUpdated. The first run is a baseline and emits (and charges for) everything.

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

Named key-value store that holds the monitor snapshots. Use a different name per schedule if two schedules share keywords.

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

If set, the Actor POSTs a JSON run summary plus the emitted items (max 500) to this URL when it finishes. Failures are logged, never fatal.

## Actor input object example

```json
{
  "keywords": [
    "small business"
  ],
  "sources": [
    "grantsGov"
  ],
  "oppStatuses": [
    "posted"
  ],
  "postedWithinDays": 14,
  "maxResults": 25,
  "fetchDetails": false,
  "maxDetails": 100,
  "samFilters": {},
  "includeRecentAwards": false,
  "maxAwards": 50,
  "onlyNewSinceLastRun": false,
  "stateStoreName": "gov-opportunity-feed-state"
}
```

# Actor output Schema

## `results` (type: `string`):

One item per grant, contract opportunity or award in the default dataset (source, title, agency, status, postedDate, closeDate, isNew, url; error items carry error and errorMessage). Fields are described in dataset_schema.json.

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

SUMMARY record in the run's default key-value store: sources queried, records produced, new/updated counts, charges and webhook delivery status, written when the run finishes.

# 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": [
        "small business"
    ],
    "sources": [
        "grantsGov"
    ],
    "oppStatuses": [
        "posted"
    ],
    "postedWithinDays": 14,
    "maxResults": 25,
    "fetchDetails": false,
    "includeRecentAwards": false,
    "onlyNewSinceLastRun": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("owlabee/gov-opportunity-feed").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": ["small business"],
    "sources": ["grantsGov"],
    "oppStatuses": ["posted"],
    "postedWithinDays": 14,
    "maxResults": 25,
    "fetchDetails": False,
    "includeRecentAwards": False,
    "onlyNewSinceLastRun": False,
}

# Run the Actor and wait for it to finish
run = client.actor("owlabee/gov-opportunity-feed").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": [
    "small business"
  ],
  "sources": [
    "grantsGov"
  ],
  "oppStatuses": [
    "posted"
  ],
  "postedWithinDays": 14,
  "maxResults": 25,
  "fetchDetails": false,
  "includeRecentAwards": false,
  "onlyNewSinceLastRun": false
}' |
apify call owlabee/gov-opportunity-feed --silent --output-dataset

```

## MCP server setup

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

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/eXuDRKy3VKemb8R8g/builds/wIdMxLfiNIeEEJ2vt/openapi.json
