# Grants.gov Federal Grant Opportunities Scraper (`hridayrungta/grants-gov-scraper`) Actor

Extract US federal grant opportunities from Grants.gov via its official public API: title, agency, ALN/CFDA, open and close dates, status, and optionally synopsis, award ceiling/floor, eligibility and attachments. Pay per result.

- **URL**: https://apify.com/hridayrungta/grants-gov-scraper.md
- **Developed by:** [Hriday Rungta](https://apify.com/hridayrungta) (community)
- **Categories:** Lead generation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$1.50 / 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## Grants.gov Federal Grant Opportunities Scraper

Pull US federal funding opportunities from **Grants.gov** into a clean dataset you can filter, schedule and export. The Actor talks to the official, public Grants.gov REST API (the same `search2` and `fetchOpportunity` endpoints that power the Grants.gov search page), so results are complete, structured and fast: no browser, no HTML parsing, no login.

You pay only for the opportunities you receive, and `maxItems` caps every run.

> **Not affiliated with or endorsed by Grants.gov or HHS; the underlying data is free and public domain at grants.gov.** This is an independent tool that saves you the work of collecting, filtering and structuring that public data.

### What it extracts

One flat record per opportunity:

| Field | Always | Description |
|---|---|---|
| `id`, `number`, `title` | yes | Grants.gov opportunity ID, funding opportunity number, title |
| `agencyCode`, `agency` | yes | Agency code (e.g. `HHS-NIH`) and organisation name |
| `openDate`, `closeDate` | yes | Posting date and application deadline, `YYYY-MM-DD` |
| `oppStatus`, `docType` | yes | `posted` / `forecasted` / `closed` / `archived`; `synopsis` or `forecast` |
| `alns[]` | yes | Assistance Listing Numbers (formerly CFDA) |
| `sourceUrl`, `scrapedAt` | yes | Link to the Grants.gov detail page; ISO timestamp |
| `synopsis` | details | Plain-text description |
| `awardCeiling`, `awardFloor`, `expectedAwards`, `estimatedFunding` | details | Award range, expected number of awards, total program funding (USD) |
| `costSharing` | details | Cost sharing / matching requirement (`true`/`false`) |
| `fundingInstruments[]`, `eligibleApplicants[]`, `eligibilityDescription`, `categoryOfFunding[]`, `opportunityCategory` | details | Grant / cooperative agreement, applicant types, category of funding activity |
| `department`, `archiveDate`, `lastUpdated` | details | Top-level department, archive date, synopsis last-updated date |
| `attachments[]` | details | `{ name, url }` for every NOFO, FAQ or related document |

"Details" fields are added when **Include full details** is on (one extra API call per opportunity, same price per record).

Only organisation-level data is collected. Agency contact names, e-mail addresses and phone numbers published on Grants.gov are deliberately **not** extracted. Agencies sometimes write a programme officer's e-mail or desk number into the description text itself, so every free-text field (`synopsis`, `eligibilityDescription`) is additionally scanned and any e-mail address or phone number is replaced with `[redacted]`.

### Use cases

- **Grant writers and consultants** - watch every new NOFO in your field the morning it appears, with deadlines, award ceilings and attachments ready to triage.
- **Universities and research offices** - feed sponsored-programs newsletters and internal funding portals with NIH, NSF, DOE and USDA opportunities filtered by ALN or category.
- **Nonprofits and local governments** - track opportunities open to 501(c)(3)s, tribes, counties or school districts using the eligibility filter.
- **Market and policy intelligence** - measure federal funding flows by agency, category and status over time; join on ALN with USAspending award data.
- **Product teams** - power grant-matching apps, CRMs and AI assistants with a structured, deduplicated feed instead of scraping HTML.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `keyword` | string | - | Full-text search. Grants.gov OR-matches multiple words; one specific term works best. |
| `oppStatuses` | array | `["posted","forecasted"]` | Any of `posted`, `forecasted`, `closed`, `archived`. |
| `agencies` | array | all | Agency codes such as `HHS`, `NIH`, `NSF`, `USDA-FS`, `DHS-FEMA`. |
| `fundingCategories` | array | all | Category codes (`HL` Health, `ED` Education, `ENV` Environment, `ST` Science and Technology, ...). |
| `eligibilities` | array | all | Applicant type codes (`00` State governments, `12` 501(c)(3) nonprofits, `06` public universities, `99` unrestricted, ...). |
| `aln` | string | - | One Assistance Listing / CFDA number, e.g. `93.243`. |
| `postedSince` | date | - | Keep only opportunities opened on/after this date (client-side, never billed). |
| `includeDetails` | boolean | `false` | Fetch the full record for every result. |
| `maxItems` | integer | `200` | Hard cap on saved (and billed) records. |
| `requestsPerSecond` | integer | `2` | Throttle for API calls (1-5). |
| `maxRetries` | integer | `4` | Retries with backoff for network errors, 5xx and 429. |
| `sortBy` | string | `openDate\|desc` | search2 sort expression. |
| `proxyConfiguration` | object | none | Optional; the API is public and normally needs no proxy. |

Example:

```json
{
  "keyword": "wildfire",
  "oppStatuses": ["posted", "forecasted"],
  "agencies": ["USDA-FS", "DHS-FEMA", "DOI"],
  "eligibilities": ["00", "01", "12"],
  "includeDetails": true,
  "maxItems": 100
}
```

### Sample output

```json
{
  "id": "358123",
  "number": "USDA-FS-2026-WF-001",
  "title": "Community Wildfire Defense Grant Program",
  "agencyCode": "USDA-FS",
  "agency": "Forest Service",
  "department": "Department of Agriculture",
  "openDate": "2026-08-15",
  "closeDate": "2026-11-30",
  "oppStatus": "posted",
  "docType": "synopsis",
  "alns": ["10.720"],
  "synopsis": "The Community Wildfire Defense Grant program assists at-risk communities.\nPlanning grants\nImplementation grants",
  "awardCeiling": 10000000,
  "awardFloor": 0,
  "expectedAwards": 150,
  "estimatedFunding": 200000000,
  "costSharing": true,
  "fundingInstruments": ["Grant", "Cooperative Agreement"],
  "eligibleApplicants": ["State governments", "Native American tribal governments (Federally recognized)", "Nonprofits having a 501(c)(3) status with the IRS, other than institutions of higher education"],
  "eligibilityDescription": "Local governments, tribes and nonprofits in at-risk communities.",
  "categoryOfFunding": ["Disaster Prevention and Relief", "Environment"],
  "opportunityCategory": "Discretionary",
  "archiveDate": "2026-12-30",
  "lastUpdated": "2026-08-20",
  "attachments": [
    { "name": "CWDG_NOFO_FY26.pdf", "url": "https://apply07.grants.gov/grantsws/rest/opportunity/att/download/401001" }
  ],
  "detailsFetched": true,
  "sourceUrl": "https://www.grants.gov/search-results-detail/358123",
  "scrapedAt": "2026-09-11T06:00:02.114Z"
}
```

Export from the Apify Console or API as JSON, CSV, Excel or XML, or push straight to Google Sheets, Airtable, Slack or a webhook with an integration.

### Daily "new since" runs

1. Create a **Schedule** in Apify Console (e.g. every morning at 06:00).
2. Set `postedSince` to yesterday's date, or leave it empty and rely on the newest-first ordering plus a small `maxItems`.
3. Keep `sortBy` at `openDate|desc`. Because results arrive newest first, the Actor stops paging as soon as a whole page is older than `postedSince`, so a daily run costs only the handful of new records, not the full backlog.
4. Add an integration (Slack, e-mail, Google Sheets, webhook) on the schedule to deliver the new opportunities.

Older opportunities that fall outside `postedSince` are skipped before billing, so they cost nothing.

### Pricing

Pay-per-event pricing: a small `actor-start` fee per run plus one `opportunity-scraped` event per saved record. `maxItems` is your hard cost cap, and the run also stops as soon as your Apify "max total charge" limit is reached. Every pushed record is billed exactly once; records skipped by `postedSince` or de-duplicated across pages are free. Fetching full details does not change the per-record price.

### FAQ

**Do I need a Grants.gov account or API key?**
No. The `search2` and `fetchOpportunity` endpoints are public and documented by Grants.gov as requiring no authentication. This Actor never logs in.

**Is this legal?**
Grants.gov publishes federal funding opportunities as US government works in the public domain, through an API intended for programmatic access. This Actor is an independent tool and is not affiliated with, endorsed by, or operated by Grants.gov, HHS or the US government.

**How current is the data?**
Each run queries the live API, so results reflect Grants.gov at the moment the run executes.

**Why did my agency filter return zero results?**
Grants.gov applies agency and ALN filters server-side and returns an empty set for unknown codes instead of an error, so a typo looks like "no matches". Department codes such as `HHS`, `USDA` or `DOD` are expanded automatically to include their sub-agencies, so you can filter by department without listing every child code. A specific sub-agency works too, in the `PARENT-CHILD` form, e.g. `HHS-NIH11` or `USDA-NIFA`.

**Why are multi-word keywords so broad?**
Grants.gov OR-matches each word and does not support quoted phrases. Use one precise term and narrow with `agencies`, `fundingCategories` or `aln`.

**Can I get contact e-mails or phone numbers?**
No. The Actor intentionally omits agency contact persons and returns organisation-level fields only, and it redacts any e-mail address or phone number that appears inside the free-text description fields.

**Can I scrape closed or archived opportunities?**
Yes, add `closed` and/or `archived` to `oppStatuses`. Historical backfills can be large; use `maxItems` and `postedSince` to bound them.

**What happens on errors?**
Network errors, 5xx and 429 responses are retried with exponential backoff. If a `fetchOpportunity` call fails, the basic record is still saved with `detailsFetched: false`.

### Local development

```bash
npm install
npm test                  # offline unit tests against documented fixtures
APIFY_LOCAL_STORAGE_DIR=$PWD/storage node src/main.js
```

# Actor input Schema

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

Full-text search over opportunity titles and descriptions, e.g. `wildfire` or `rural broadband`. Grants.gov OR-matches multiple words and ignores quotes, so one specific term usually works best. Leave empty to list everything that matches the other filters.

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

Which opportunities to include. `posted` = open for applications, `forecasted` = announced but not yet open, `closed` = deadline passed, `archived` = historical.

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

Agency codes, one per line. Empty = all agencies. Department codes are expanded automatically to cover their sub-agencies, so `HHS` also returns NIH, CDC and the rest. Examples: `HHS`, `USDA`, `DOD`, `NSF`, `DOE`, `ED`, `EPA`, `DOI`. A specific sub-agency works too, e.g. `HHS-NIH11`, `USDA-NIFA`, `DOC-NIST`. Unknown codes return zero results rather than an error.

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

Category of funding activity codes, one per line: AG Agriculture, AR Arts, BC Business and Commerce, CD Community Development, CP Consumer Protection, DPR Disaster Prevention and Relief, ED Education, ELT Employment/Labor/Training, EN Energy, ENV Environment, FN Food and Nutrition, HL Health, HO Housing, HU Humanities, IIJ Infrastructure Investment and Jobs Act, IS Information and Statistics, ISS Income Security and Social Services, LJL Law/Justice/Legal Services, NR Natural Resources, O Other, RD Regional Development, ST Science and Technology, T Transportation.

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

Applicant eligibility codes, one per line: 00 State governments, 01 County governments, 02 City or township governments, 04 Special district governments, 05 Independent school districts, 06 Public/State institutions of higher education, 07 Federally recognized tribal governments, 08 Public/Indian housing authorities, 11 Other tribal organizations, 12 501(c)(3) nonprofits, 13 Other nonprofits, 20 Private institutions of higher education, 21 Individuals, 22 For-profit organizations, 23 Small businesses, 25 Others, 99 Unrestricted.

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

Filter to one Assistance Listing Number, formerly CFDA number, e.g. `93.243` or `10.500`. Sent to the API as its `cfda` parameter.

## `postedSince` (type: `string`):

Only keep opportunities whose open (posting) date is on or after this date, `YYYY-MM-DD`. Filtered client-side and never billed. Combine with a scheduled daily run to get only what is new.

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

Call fetchOpportunity for every result to add synopsis, award ceiling/floor, expected number of awards, estimated funding, cost sharing, funding instruments, eligible applicants, category of funding, last-updated date and attachments. One extra API request per opportunity (same price per record).

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

Stop after this many opportunities have been saved. This is also your cost cap: you are billed once per saved record.

## `requestsPerSecond` (type: `integer`):

Throttle for calls to api.grants.gov (1-5). The public API has no published rate limit; 2 is a polite default.

## `maxRetries` (type: `integer`):

Retries with exponential backoff for network errors, 5xx and 429 responses before a request is given up.

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

Sort expression, `<field>|asc` or `<field>|desc`. Accepted fields: `openDate`, `closeDate`, `agency`, `oppNum`. Keep `openDate|desc` (newest first) so `postedSince` runs can stop early. The direction is required, and an unrecognised value fails the run up front because Grants.gov answers it with zero results instead of an error.

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

Optional. The Grants.gov API is public and works without a proxy; enable Apify Proxy only if your runs are rate limited.

## Actor input object example

```json
{
  "keyword": "climate resilience",
  "oppStatuses": [
    "posted",
    "forecasted"
  ],
  "agencies": [],
  "fundingCategories": [],
  "eligibilities": [],
  "includeDetails": false,
  "maxItems": 50,
  "requestsPerSecond": 2,
  "maxRetries": 4,
  "sortBy": "openDate|desc",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

# 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 resilience",
    "oppStatuses": [
        "posted",
        "forecasted"
    ],
    "agencies": [],
    "fundingCategories": [],
    "eligibilities": [],
    "maxItems": 50
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "keyword": "climate resilience",
    "oppStatuses": [
        "posted",
        "forecasted",
    ],
    "agencies": [],
    "fundingCategories": [],
    "eligibilities": [],
    "maxItems": 50,
}

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

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

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

```

## CLI example

```bash
echo '{
  "keyword": "climate resilience",
  "oppStatuses": [
    "posted",
    "forecasted"
  ],
  "agencies": [],
  "fundingCategories": [],
  "eligibilities": [],
  "maxItems": 50
}' |
apify call hridayrungta/grants-gov-scraper --silent --output-dataset

```

## MCP server setup

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

```

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

## OpenAPI specification

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