# Grants.gov API Scraper: Federal Grants Search, Deadline Monitor (`neverempty/grants-gov-opportunities-monitor`) Actor

For grant writers, nonprofits and funding research: federal grants from the Grants.gov API, one row per opportunity with title, number, agency, posted and close dates, status, ALN, award ceiling and floor, eligibility. Monitoring returns only new grants and changed deadlines. No API key.

- **URL**: https://apify.com/neverempty/grants-gov-opportunities-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 grant opportunity returneds

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/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

## Grants.gov API Scraper: Federal Grants Search, Deadline Monitor

**US federal grants from Grants.gov, one row per funding opportunity**: title, opportunity number, agency, posted date, close date, status (forecasted, posted, closed, archived), Assistance Listing numbers (ALN / CFDA), the Grants.gov page, and with details on: description, award ceiling and floor, estimated total funding, expected number of awards, cost sharing, funding instrument, category and eligible applicants.

- **Search** by keyword, agency, eligible applicant type, funding category, funding instrument, ALN, status and posted date.
- **Monitoring mode** remembers a search between runs and returns only the opportunities that are **new** and the ones whose **close date or status changed**, with the previous close date and status in the same row.
- Reads the **official Grants.gov API** (`search2` and `fetchOpportunity`). No API key, no login, no proxy, no browser.

> This product uses the Grants.gov API but is not endorsed or certified by the U.S. Department of Health and Human Services.

### What one row looks like

A real row from 2026-10-05 (search: keyword `climate`, status posted):

| Field | Value |
|---|---|
| `title` | `Climate Program Office (CPO) FY2026 Earth System Science and Services Partnership` |
| `opportunityNumber` | `NOAA-OAR-CPO-2026-33326` |
| `agencyCode` / `agencyName` | `DOC-DOCNOAAERA` / `DOC NOAA - ERA Production` |
| `topAgencyCode` / `topAgencyName` | `DOC` / `Department of Commerce` |
| `opportunityStatus` | `posted` |
| `postedDate` / `closeDate` | `2026-09-11` / `2026-10-26` |
| `alnNumbers` | `["11.431"]` |
| `awardCeiling` / `awardFloor` | `10000000` / `1` |
| `expectedNumberOfAwards` | `1` |
| `fundingInstruments` | `["Cooperative Agreement"]` |
| `eligibleApplicants` | `["Others (see text field entitled \"Additional Information on Eligibility\" for clarification)"]` |
| `eligibilityNotes` | `Eligible applicants must be academic institutions of higher learning which offer doctoral degrees in NOAA-related sciences; ...` |
| `url` | `https://www.grants.gov/search-results-detail/363852` |

### What this Actor checks for you (measured on the live API on 2026-10-05)

#### 1. A department code is searched as all of its agencies

The Grants.gov API matches agency codes exactly. Opportunities carry the code of the agency that owns them (`HHS-NIH11`, `HHS-FDA`, `DOD-AMC`), so a search for `HHS` on its own returned **0** posted opportunities, while the 11 HHS agencies together had **402**. This Actor first asks Grants.gov which agencies have opportunities for your search, turns each department code into the codes of its agencies, and sends those. A code that belongs to no opportunity of the search is named in the log and in the result, not silently dropped.

#### 2. A filter the API did not take is reported, not sold as a result

The API ignores an argument it does not know and returns the unfiltered list with HTTP 200. Its own documentation names the Assistance Listing argument `aln`; sent that way on 2026-10-05, the API returned all 859 posted opportunities instead of the 101 under ALN 93.866 (the argument it reads is `cfda`). Unknown values behave the other way: an unknown status, sort or category returns 0 with HTTP 200.

So every value is checked against the list of codes before it is sent, and after every answer the Actor compares the search that Grants.gov reports back with the search it sent. If one filter is missing, or a returned opportunity has a status that was not asked for, the run returns a `source-unreadable` row that says which filter was not taken, and charges nothing.

#### 3. Monitoring returns changes, not a date window

Running a "posted in the last 7 days" search every day returns the same opportunities several times and never tells you that a deadline moved. Monitoring mode keeps, per search, the close date and status of every matching opportunity and compares them at the next run. See "Monitoring mode" below.

#### 4. Amounts and dates are not invented

- `awardCeiling`, `awardFloor`, `estimatedTotalFunding` and `expectedNumberOfAwards` are the numbers the agency typed. When the field is empty or not a number, the value is `null`, never `0`. Agencies do type placeholders: an `awardFloor` of `0` or `1` is common and is returned as written.
- `closeDate` is `null` when the opportunity has none (forecasts, and opportunities that stay open). For forecasts, `estimatedPostDate`, `estimatedCloseDate`, `estimatedAwardDate` and `estimatedProjectStartDate` hold the agency's estimates.
- A row whose details could not be read has `detailsIncluded: false` and `null` in the detail columns, and a `details-not-read` row says how many.

### Input

| Field | What it does |
|---|---|
| `keyword` | Words as in the Grants.gov search box. Several words match any of them; a phrase in double quotes matches exactly (`rural health` matched 434 posted opportunities, `"rural health"` matched 2). |
| `statuses` | `forecasted`, `posted`, `closed`, `archived`. Nothing chosen = forecasted and posted. On 2026-10-05: 610 forecasted, 859 posted, 8,692 closed, 73,359 archived. |
| `agencies` | Department codes (`HHS`, `DOD`, `USDA`, `NSF` ...) or agency codes (`HHS-NIH11`, `USDA-NIFA` ...). |
| `eligibilities` | Applicant types (state governments, nonprofits with 501(c)(3), small businesses, individuals ...). |
| `fundingCategories` | Category of funding activity (Health, Education, Environment, Science and Technology ...). |
| `fundingInstruments` | Grant, cooperative agreement, procurement contract, other. |
| `aln` | One Assistance Listing number, for example `93.866`. |
| `opportunityNumber` | A funding opportunity number, for example `PAR-25-283`. |
| `postedWithinDays` | Posted date within the last N days, as counted by Grants.gov. |
| `sortBy` | Newest posted (default), oldest posted, closing soonest, closing latest. |
| `maxItems` | Maximum rows (default 100). Not used in monitoring mode. |
| `includeDetails` | On by default. One more request per opportunity. |
| `includeAgencyContact` | Off by default. Adds the grantor contact (name, email, phone) published on the opportunity. |
| `monitoringMode`, `resetMonitoringState` | See below. |

Every row has `searchedFor`, the search exactly as it was sent, so you can see what a row is an answer to.

### Monitoring mode

1. **First run** with a set of filters: every matching opportunity is remembered with its close date and status. No opportunity row is returned and nothing is charged. The result is one `baseline-saved` row.
2. **Later runs** with the same filters read every matching opportunity again and return:
   - `change: new-opportunity` - not in the search before;
   - `change: close-date-changed`, `status-changed` or `close-date-and-status-changed` - with `previousCloseDate`, `previousStatus` and `changedFields`.
3. A run that finds nothing new and nothing changed returns one `no-new-or-changed-opportunities` row and is charged one monitoring check.

What to know:

- An opportunity that moves out of your search is not reported. With `statuses` set to posted only, an opportunity that closes simply stops matching. To be told about it, include `closed` in `statuses`: the move from posted to closed is then a `status-changed` row. A forecast that becomes posted is reported with the default statuses.
- Only the close date and the status are compared. A changed description or amount alone is not reported.
- Only what was delivered is remembered. If the maximum total charge of a run stops it early, the rest still counts as new or changed at the next run.
- One monitoring run compares up to 10,000 opportunities. A broader search returns a `search-too-broad` row and remembers nothing.
- Opportunities are remembered per set of filters (sort and `maxItems` do not matter). Two runs with the same filters at the same moment can return the same opportunity twice.
- Grants.gov posts new opportunities on working days. Once a day is enough for most uses.

### What is charged

- **`grant-returned`**: each opportunity row. The price per row is the same with or without details.
- **`search-checked`**: one per monitoring run that compared the search and found nothing new or changed.
- Not charged: the first monitoring run, rows that explain an empty or stopped run (`no-opportunities`, `baseline-saved`, `limit-reached`, `budget-reached`, `source-unreadable`, `source-blocked`, `invalid-input`, `details-not-read`, `not-read`, `search-too-broad`), and there is no start fee.

### How it reads

- One request at a time, with more than one second between requests. A search page holds up to 1,000 opportunities; details are one request per returned opportunity (about 1.3 seconds each: 100 rows take about 2.5 minutes, 1,000 about 22 minutes). Turn `includeDetails` off for a list in seconds.
- A failed connection or a 5xx answer is retried once. A 403 or 429 is not retried and not worked around: the run stops reading and says so.
- If the detail endpoint fails three times in a row, the rest of the run is returned without details instead of asking again and again.

### Limits

- The data is what agencies entered on Grants.gov. Titles, amounts and eligibility are not corrected.
- With `includeAgencyContact` off, email addresses and US phone numbers written in the usual formats are removed from `description`, `eligibilityNotes` and `closeDateNote`. A phone number typed as ten digits without separators is not recognised.
- Attachments are counted (`attachmentCount`), not downloaded.
- The Grants.gov API terms set no published request limit and allow Grants.gov to limit or block use. If that happens, runs return `source-blocked` rows and charge nothing.
- Grants.gov lists federal funding opportunities. State, local and foundation grants are not in it.

### Related Actors

- [EU Tenders API - TED Public Procurement Notices](https://apify.com/neverempty/eu-tender-monitor) - public procurement notices of the European Union.
- [UK Tenders Scraper - Contracts Finder & Find a Tender](https://apify.com/neverempty/uk-tenders-scraper) - UK public sector tenders and awards.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `keyword` (type: `string`):

Words to search for, as in the Grants.gov search box. Several words match opportunities with any of them; put a phrase in double quotes ("rural health") to match it exactly. Leave empty for every opportunity.

## `statuses` (type: `array`):

Which opportunities to search. When nothing is chosen, forecasted and posted are searched (the same as the Grants.gov search page). Closed holds about 8,700 opportunities and archived about 73,000.

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

Grants.gov agency codes, one per line: a department (HHS, DOD, USDA, DOE, NSF, NASA ...) or one of its agencies (HHS-NIH11, DOD-AMC, USDA-NIFA ...). A department code is searched as all of its agencies; sent on its own, the Grants.gov API returns nothing for most departments. The codes are in the "Agency" filter of the Grants.gov search page and in the agencyCode column of every row. Leave empty for every agency.

## `eligibilities` (type: `array`):

Keep opportunities open to at least one of these applicant types. "Others" and "Unrestricted" are separate types on Grants.gov: an opportunity marked only "Unrestricted" does not match "Small businesses".

## `fundingCategories` (type: `array`):

Keep opportunities in at least one of these categories of funding activity.

## `fundingInstruments` (type: `array`):

Keep opportunities offered as at least one of these: grant, cooperative agreement, procurement contract or other.

## `aln` (type: `string`):

Keep opportunities under this Assistance Listing, for example 93.866. One number.

## `opportunityNumber` (type: `string`):

Search by funding opportunity number, for example PAR-25-283.

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

Keep opportunities whose posted date is within this many days, as counted by Grants.gov. Leave empty for no date filter. Monitoring does not need it: it compares with what it saw before.

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

Order of the rows, and which ones are returned when the search matches more than "Maximum opportunities". Not used in monitoring mode.

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

Stop after this many opportunity rows. Not used in monitoring mode, which returns every new or changed opportunity.

## `includeDetails` (type: `boolean`):

Reads the detail record of every returned opportunity: description, award ceiling and floor, estimated total funding, expected number of awards, cost sharing, funding instruments, categories, eligible applicants and forecast dates. This is one more request per opportunity, about 1.3 seconds each (1,000 opportunities take about 22 minutes). Turn it off for a fast list of title, number, agency, dates, status, ALN and link. The price per row is the same.

## `includeAgencyContact` (type: `boolean`):

Adds the grantor contact published on the opportunity (name, email, phone) as agencyContact, and leaves email addresses and phone numbers in the description as written. Off by default: the contact is often a named person. Needs details.

## `monitoringMode` (type: `boolean`):

Remembers the opportunities of this search between runs. The first run with a given set of filters only remembers them: nothing is returned and nothing is charged. Later runs return an opportunity when it is new to the search or when its close date or status changed, with the previous close date and status. A run that finds nothing new or changed returns one row saying so and is charged one monitoring check (search-checked). Opportunities are remembered per set of filters. Do not run two schedules with the same filters at the same moment.

## `resetMonitoringState` (type: `boolean`):

Clears what is remembered for this set of filters before the run reads anything, so with monitoring on this run is a first run again: the opportunities are remembered and nothing is returned. Use it for one run and turn it off again.

## Actor input object example

```json
{
  "keyword": "climate",
  "statuses": [
    "forecasted",
    "posted"
  ],
  "sortBy": "newest-posted",
  "maxItems": 100,
  "includeDetails": true,
  "includeAgencyContact": false,
  "monitoringMode": false,
  "resetMonitoringState": false
}
```

# Actor output Schema

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

One row per Grants.gov funding opportunity: title, opportunity number, agency, posted date, close date, status, ALN numbers, link, and (with details on) description, award ceiling and floor, estimated total funding, expected number of awards, cost sharing, funding instruments, categories and eligible applicants. Monitoring rows also say what changed and the previous close date and status. Runs that could not read Grants.gov, found nothing or nothing new, or hit a limit return a row that says so and is not charged.

# 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 = {
    "keyword": "climate",
    "statuses": [
        "forecasted",
        "posted"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/grants-gov-opportunities-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 = {
    "keyword": "climate",
    "statuses": [
        "forecasted",
        "posted",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/grants-gov-opportunities-monitor").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 '{
  "keyword": "climate",
  "statuses": [
    "forecasted",
    "posted"
  ]
}' |
apify call neverempty/grants-gov-opportunities-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/grants-gov-opportunities-monitor"
        }
    }
}
```

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/INBgfQ3KhfXqg2M0Q/builds/Etx9Bbg7Zpwto72zS/openapi.json
