# Grant & RFP Fit Scorer (AI) — Grants.gov, SAM.gov Bid/No-Bid (`owlabee/gov-opportunity-fit-scorer`) Actor

Rank federal grants and contract opportunities by fit to your organization. Describe your company once; the Actor fetches Grants.gov, SAM.gov and USAspending records and scores each with reasons and disqualifiers. Free rules mode or Claude tiers.

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

## Pricing

from $25.00 / 1,000 opportunity scored (hosted, fast tier)s

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

## Grant & RFP Fit Scorer (AI) — Grants.gov, SAM.gov Bid/No-Bid

### What it does

Government Grant & Contract Opportunity Fit Scorer ranks federal funding and contract opportunities by how well they fit **your** organization. Describe your company once (what you do, NAICS codes, set-asides and certifications, size, where you work, keywords, past awards, budget range), then give it either specific opportunities (Grants.gov IDs or URLs, SAM.gov notice IDs, or JSON records from any feed) or search terms. For every opportunity it writes one dataset item with the normalized record plus a **fit block**: a `fitScore` from 0 to 100, a `fitVerdict` (strong, possible, weak, disqualified), a `recommendedAction` (pursue, review, skip), `fitReasons`, hard `disqualifiers` (eligibility, set-aside, deadline passed, already awarded), the `keyRequirements` pulled from the synopsis, and a one-paragraph `fitSummary` a grant writer or capture manager can act on. The run's `SUMMARY` record carries the top-10 ranking.

Search terms are run against the official Grants.gov API (no key), the SAM.gov Opportunities API (your free personal key) and USAspending (no key; recent awards, scored as "who won similar work" intelligence). Every fetched opportunity is enriched with its full synopsis or notice text before scoring.

It has three scorers, chosen with the `scorer` input and recorded on every item as `scoreMethod`. **AI scores are the default**: when `scorer` is left empty the Actor uses the hosted Claude model; set `scorer=rules` for a free, model-free run, or `scorer=byok` to use your own key.

- **`hosted`** (default): Claude on the Actor's own key, tokens included, from $0.025 per opportunity. Nothing to set up. Pick a **model tier**: `fast` (Claude Haiku 5.5, $0.025), `balanced` (Claude Sonnet 5.5, $0.30) or `best` (Claude Opus 5.5, $0.60). If the model cannot be reached, the opportunity gets the rules score instead, marked `scoreMethod: "rules-fallback"` with a `scoreWarning`, and is charged the rules price; a run never fails because of the model.
- **`byok`** (bring your own key): Claude scores the opportunity using the `anthropicApiKey` you supply (stored as a secret, sent only to api.anthropic.com), on the same three tiers. You pay Anthropic for the tokens and $0.005 per opportunity here; each item records `tokensIn`, `tokensOut` and `modelCostUsdEstimate`.
- **`rules`**: a deterministic scorer with documented weights (keywords 30, NAICS 20, set-aside 15, eligibility 15, geography 10, deadline 10, budget 10, status 5), scaled by relevance so an open, eligible grant about something else still scores low. No language model, no key, runs in milliseconds, costs $0.003 per opportunity. Every model-mode item also carries this score as `rulesFitScore` with the per-component `rulesBreakdown`.

Built for grant writers, nonprofit development teams, SBIR/STTR applicants, small government contractors and capture managers who get dozens of keyword hits a week and need the ten worth reading, and for AI agents that need a bid/no-bid judgment on demand. Results are available as JSON, CSV, Excel, through the API, webhooks and the Apify MCP server.

### How to use

1. Fill in the **Company profile** (JSON): description, capabilities, NAICS codes, set-asides, organization type, locations, keywords, budget range. Only a description or a few keywords are strictly needed; the more you give, the sharper the score.
2. Either list **Opportunities** (Grants.gov IDs/URLs, SAM.gov notice IDs, or JSON records) or enter **Search terms** and pick **Sources**; add your **SAM.gov API key** to include contract notices.
3. Leave **Scorer** empty for hosted AI scores and pick a **Model tier** (`fast` is the default); or choose `byok` and paste your **Anthropic API key**; or choose `rules` for a free run. Click **Start**. Items appear as each opportunity is scored; the `SUMMARY` record lists the top 10. Schedule the run weekly to triage every new opportunity automatically.

#### Quick start input

The same input the Store runs in its daily automated test, so it is cheap and always available; paste it into the JSON tab of the input form or send it as the body of an API or MCP call:

```json
{
  "companyProfile": {
    "name": "Cascadia Civil Engineering",
    "description": "Civil engineering firm with 40 staff, NAICS 541330, SBA small business, serving the Pacific Northwest; past EPA and USDA rural water and wastewater grants.",
    "capabilities": [
      "water infrastructure design",
      "wastewater treatment",
      "stormwater management",
      "rural water systems",
      "environmental engineering"
    ],
    "naicsCodes": [
      "541330",
      "237110"
    ],
    "setAsides": [
      "SBA"
    ],
    "organizationType": "small business",
    "businessSize": "small",
    "locations": [
      "WA",
      "OR",
      "ID"
    ],
    "keywords": [
      "water",
      "wastewater",
      "infrastructure",
      "drinking water",
      "rural"
    ],
    "pastAwards": [
      "EPA Water Infrastructure Improvements for the Nation",
      "USDA Rural Development Water and Waste Disposal"
    ],
    "budgetRange": {
      "min": 50000,
      "max": 5000000
    }
  },
  "searchTerms": [
    "water infrastructure"
  ],
  "sources": [
    "grantsGov"
  ],
  "oppStatuses": [
    "posted"
  ],
  "postedWithinDays": 30,
  "maxOpportunities": 25,
  "scorer": "rules"
}
```

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `companyProfile` | object | yes | `name`, `description`, `capabilities[]`, `naicsCodes[]`, `setAsides[]` (SBA, 8A, HZC, SDVOSBC, WOSB, EDWOSB, VSA or their names), `organizationType`, `businessSize`, `locations[]` (states, regions or "nationwide"), `keywords[]`, `pastAwards[]`, `budgetRange{min,max}` |
| `opportunities` | array | no | Grants.gov IDs or URLs, SAM.gov notice IDs or URLs, or JSON records (a `gov-opportunity-feed` item, or any object with `title`, `description`, `agency`, `naicsCode`, `setAside`, `closeDate`, `url`…). Supplied records are scored without a fetch |
| `searchTerms` | string\[] | no | Keywords searched in each enabled source; required when `opportunities` is empty |
| `sources` | string\[] | no | `grantsGov` (default), `samGov` (needs `samApiKey`), `usaspending` (recent awards) |
| `oppStatuses` | string\[] | no | Grants.gov statuses for searches (default `posted`) |
| `postedWithinDays` | integer | no | Search window, 1–365 days (default 30) |
| `maxOpportunities` | integer | no | Cap on opportunities scored per run, 1–1,000 (default 100) |
| `samApiKey` | string (secret) | for SAM.gov | Free personal key from sam.gov; never logged |
| `samFilters` | object | no | `{"naics": "541330", "setAside": "SBA", "state": "WA", "ptype": "o,k"}` |
| `scorer` | string | no | `hosted`, `byok` or `rules`. Empty = `hosted` when the Actor's model key is configured, else `rules` |
| `anthropicApiKey` | string (secret) | for `byok` | Your Anthropic API key; used only in `byok` mode |
| `modelTier` | string | no | `fast` (default, Claude Haiku 5.5), `balanced` (Claude Sonnet 5.5) or `best` (Claude Opus 5.5); applies to `hosted` and `byok` |
| `minRulesScoreForModel` | integer | no | Model modes: opportunities whose rules score is below this get the rules score at the rules price and skip the model (default 0 = model for all) |
| `maxInputTokens` | integer | no | Per-opportunity input cap for model modes, 2,000–8,000 (default 8,000) |
| `maxConcurrency` | integer | no | Opportunities processed in parallel, 1–5 (default 3) |
| `webhookUrl` | string | no | POST target for the run summary (counts, verdicts, top 10, token totals) |

### Output example

A record the buyer supplied, scored in `rules` mode for a small civil engineering firm in the Pacific Northwest:

```json
{
  "source": "provided",
  "recordType": "opportunity",
  "opportunityId": "RFP-22",
  "title": "Wastewater Treatment Plant Engineering Services",
  "agency": "City of Yakima",
  "closeDate": "2026-12-15",
  "url": "https://example.org/rfp/22",
  "description": "Design of rural drinking water and wastewater infrastructure in Yakima, WA. The contractor must provide a licensed PE. Total small business set-aside.",
  "naicsCode": "541330",
  "setAside": "SBA",
  "setAsideCode": "SBA",
  "placeOfPerformanceState": "WA",
  "fitScore": 100,
  "fitVerdict": "strong",
  "recommendedAction": "pursue",
  "fitReasons": [
    "Matches your keywords \"water\", \"wastewater\", \"infrastructure\", \"rural\", \"wastewater treatment\" and partially \"water infrastructure design\" (\"wastewater\", \"wastewater treatment\" in the title).",
    "NAICS 541330 matches your code 541330.",
    "Set aside for SBA, which your profile qualifies for.",
    "Place of performance WA is in your service area.",
    "Closes on 2026-12-15, in 59 days."
  ],
  "disqualifiers": [],
  "keyRequirements": ["The contractor must provide a licensed PE."],
  "fitSummary": "Wastewater Treatment Plant Engineering Services (City of Yakima) scores 100/100 for Cascadia Civil Engineering, a strong fit. Matches your keywords \"water\", \"wastewater\", \"infrastructure\", \"rural\", \"wastewater treatment\" and partially \"water infrastructure design\" (\"wastewater\", \"wastewater treatment\" in the title). NAICS 541330 matches your code 541330. Set aside for SBA, which your profile qualifies for. Place of performance WA is in your service area. Scored by deterministic rules (keywords, NAICS, set-aside, eligibility, geography, deadline, budget, status); no language model was used.",
  "daysToClose": 59,
  "rulesFitScore": 100,
  "rulesBreakdown": {
    "keywords": { "score": 1, "weight": 30, "note": "Matches your keywords \"water\", \"wastewater\", … in the title)." },
    "naics": { "score": 1, "weight": 20, "note": "NAICS 541330 matches your code 541330." },
    "setAside": { "score": 1, "weight": 15, "note": "Set aside for SBA, which your profile qualifies for." },
    "eligibility": { "score": null, "weight": 15, "note": "No applicant-type list on the record." },
    "geography": { "score": 1, "weight": 10, "note": "Place of performance WA is in your service area." },
    "deadline": { "score": 1, "weight": 10, "note": "Closes on 2026-12-15, in 59 days." },
    "budget": { "score": null, "weight": 10, "note": "No award amount on the record." },
    "status": { "score": 1, "weight": 5, "note": "Status not stated; treated as open." }
  },
  "scoreMethod": "rules",
  "profileName": "Cascadia Civil Engineering",
  "fetchedAt": "2026-10-17T10:26:20.692Z"
}
```

Opportunities fetched from Grants.gov carry the feed fields too (`number`, `agencyCode`, `status`, `postedDate`, `assistanceListings`, `awardCeiling`, `awardFloor`, `estimatedTotalFunding`, `expectedAwards`, `costSharing`, `fundingCategories`, `eligibilities`, `agencyContactName`, `agencyContactEmail`, the synopsis as `description`); SAM.gov notices carry `noticeId`, `solicitationNumber`, `department`, `subTier`, `office`, `type`, `responseDeadLine`, `naicsCode`, `setAside`, `placeOfPerformance`, `pointOfContact` and the notice text; USAspending awards carry `recipientName`, `awardAmount`, `awardingSubAgency` and always a disqualifier saying the award is already made. Items written in `byok` or `hosted` mode have the same fit fields (the model writes the reasons and summary in prose, with `recommendedAction` as its bid/no-bid call) and additionally carry `modelTier`, `model`, `tokensIn`, `tokensOut`, `modelCostUsdEstimate` (USD at Anthropic list prices) and `inputTruncated`. If a model call fails (rate limit, API error, timeout, refusal, invalid JSON) or the hosted model is not configured, the item falls back to the rules score with `scoreMethod: "rules-fallback"`, records the reason in `scoreWarning`, and is charged the rules price.

Error items look like `{ "recordType": "error", "source": "grantsGov", "input": "…", "error": "NOT_FOUND", "errorMessage": "…" }` (`INVALID_OPPORTUNITY` for an entry that is neither an ID, a URL nor a record; `NOT_FOUND`; `SAM_KEY_REQUIRED`; `FETCH_FAILED`, `SAM_AUTH_FAILED`, `SAM_RATE_LIMITED` for a source or search term that could not be read) and are charged at the fetched price, because the work to discover the problem was done. A `byok` run without a key, or with a key Anthropic rejects, fails with a clear message before any further opportunity is charged; an empty profile fails before any charge at all.

The `SUMMARY` record in the key-value store holds `scored`, `errors`, `byVerdict`, `byMethod`, `topOpportunities` (the 10 best by `fitScore`, with title, agency, close date and URL), model token totals and `charges`.

If the output saves you time, a review on the Store page helps other buyers find this Actor; the Store invites you to leave one after you have run it a few times.

### 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 fetched | $0.001 | Per opportunity fetched from Grants.gov, SAM.gov or USAspending (search result or pasted ID), including its synopsis or notice text; not charged for records you supply. Error items are charged at this price |
| Opportunity scored (rules, no model) | $0.003 | Per opportunity in `rules` mode, below the `minRulesScoreForModel` pre-screen, or on a `rules-fallback` |
| Opportunity scored (your Anthropic key) | $0.005 | Per opportunity scored by Claude in `byok` mode, any tier; your tokens are billed by Anthropic, not here |
| Opportunity scored (hosted, fast tier) | $0.025 | Claude Haiku 5.5 on the Actor's key, tokens included, up to 8,000 input tokens per opportunity |
| Opportunity scored (hosted, balanced tier) | $0.30 | Claude Sonnet 5.5, same caps |
| Opportunity scored (hosted, best tier) | $0.60 | Claude Opus 5.5, same caps |
| Actor start | $0.00005 | Apify platform event |

**Worked examples.** A weekly run that fetches 200 Grants.gov matches and scores them on hosted `fast` (the default) ≈ $0.20 + $5.00 = **$5.20**; the same on `balanced` ≈ **$60.20**, on `best` ≈ **$120.20**; rules only ≈ **$0.80**. Bring your own key: 200 opportunities ≈ **$1.20** here plus roughly $0.13 of Claude Haiku 5.5 tokens on your Anthropic bill (about 3,800 input and 500 output tokens per opportunity at $0.10 and $0.50 per million); about **$2.50** on Sonnet 5.5 ($2 and $10 per million) or **$5.00** on Opus 5.5 ($4 and $20 per million). With `minRulesScoreForModel: 35`, a typical broad search sends a third of the matches to the model and the rest stay at $0.003. Scoring 50 records you already have (no fetch) on `fast` ≈ **$1.25**.

**Spending limit and model tiers.** The hosted model is called before its per-opportunity event is charged, so before every model call the Actor checks that your run's maximum total charge still covers that event. When it does not, the model is not called: the opportunity gets the rules score instead, charged at the rules price, with a `scoreWarning` that names the tier and the limit to set (the run's `SUMMARY` counts these as `modelSkippedForSpendingLimit`). When not even a rules-scored item fits, the run stops cleanly with a log line and nothing more is charged. A run with no limit set behaves as before. In practice: set the maximum total charge to at least the tier price times the number of opportunities you want scored by the model, plus $0.001 each for fetched ones.

**How the hosted prices are set.** Against the worst case, not the average. The opportunity text is capped at 8,000 input tokens (about 28,000 characters, more than any Grants.gov synopsis or SAM.gov notice text, which is cut at 20,000 characters), every call carries about 1,700 fixed tokens of profile, instructions and schema on top, and output is capped at 1,000 tokens, so the most one opportunity can cost in tokens is: `fast` 9,700 × $0.10/M + 1,000 × $0.50/M ≈ **$0.0015**; `balanced` 9,700 × $2/M + 1,000 × $10/M ≈ **$0.029**; `best` 9,700 × $4/M + 1,000 × $20/M ≈ **$0.059**. Apify pays developers 80% of the event price ($0.02, $0.24 and $0.48 respectively), so every tier clears its worst-case token cost even at the cap, by about 13×, 8× and 8× (at least 85% margin at the cap, the bar every model Actor in this account meets); a typical 2,000-character synopsis costs a fraction of that. Reasoning tokens are kept out of the output cap (thinking is turned off on Haiku and Sonnet; Opus runs at low effort). A model failure is never charged at the hosted price: the item falls back to the rules score at $0.003.

### Limits and fair use

- Sources are read with plain HTTP from Apify datacenter IPs through their official public APIs only: Grants.gov `search2` and `fetchOpportunity` (no key, about 2 requests per second), SAM.gov Opportunities v2 and its notice-description endpoint (your personal key; SAM.gov allows about 1,000 requests per day per non-federal key, and a search is one request per 1,000 notices plus one per notice for its text), USAspending `spending_by_award` (no key). No scraping of the websites, no browsers, no proxies.
- SAM.gov notice IDs are looked up within the last 365 days of postings (the API's maximum window). Grants.gov IDs are fetched directly, any age; closed opportunities are scored and disqualified.
- The rules scorer matches your keywords and capabilities against the title and description with light stemming, maps NAICS codes by shared prefix (6, 5, 4, 3, 2 digits), expands set-aside implications (an 8(a) firm also qualifies for total small business set-asides), reads Grants.gov applicant types against your `organizationType`, and treats missing data as "not applicable" rather than as a penalty. It is deterministic and documented; it does not read the fine print. Model modes do, and get the rules findings as facts in the prompt.
- Model modes cut the description to `maxInputTokens` (at most 8,000 tokens) before the call and say so on the item; output is capped at 1,000 tokens per opportunity. The model is set by `modelTier` in both hosted and byok modes.
- At most 1,000 opportunities per run (`maxOpportunities`); search results are capped by that number across all terms and sources and de-duplicated by source and ID within the run.
- **Time budget.** Each search term on each source gets 10 minutes (paging included), and a search that delivers no new candidates for 60 seconds is stopped. Candidates fetched before the stop are kept, scored and charged as usual and the unit is listed under `truncated` in the `SUMMARY` record (`TIME_BUDGET`); a search that produced nothing returns a charged error item (`TIME_BUDGET` or the source's error code). Source requests time out after 20 seconds (30 for SAM.gov, 45 for USAspending) including retries, and `Retry-After` pauses are capped at 10 seconds. After two search terms in a row fail on the same source, the remaining terms for that source are not requested and each gets one charged `SOURCE_UNAVAILABLE` error item (pasted IDs behave the same after three failed lookups). If three enrichment requests in a row fail, the remaining candidates are scored from their search data (`detailError`/`descriptionError` says so). A model call that takes longer than 60 seconds is cancelled without a retry and the opportunity gets the rules score at the rules price.
- Memory: default run memory is 512 MB (256 to 1024 MB allowed); the Actor holds one opportunity per worker at a time.
- Only public data is accessed: the three government APIs and, in model modes, the Anthropic API. Your `anthropicApiKey` and `samApiKey` are stored encrypted by Apify and are never logged or written to the dataset.

### Related Actors

- *US Government Grants & Contract Opportunities Feed (same account): the raw Grants.gov, SAM.gov and USAspending records with new/updated flags for scheduled monitoring, from $0.001 per record. Its dataset items can be pasted straight into this Actor's `opportunities` input.*
- *Link 1–2 more sibling Actors from this account once published.*

### Use from AI agents (MCP)

Pin this Actor in the Apify MCP server with one link: `https://mcp.apify.com?tools=owlabee/gov-opportunity-fit-scorer`, or let the agent find it with the `search-actors` tool. It is pay-per-event with limited permissions, so agents can discover it, inspect the input schema with `fetch-actor-details` and pay per run through agentic payments. Prompts that map directly onto the input:

1. "Score the Grants.gov opportunities posted in the last 30 days for 'water infrastructure' against our company profile and give me the top five with reasons."
2. "Rank these three SAM.gov notices for a HUBZone IT firm in Virginia and tell me which to bid on."
3. "Check whether we are eligible for Grants.gov opportunity 364099 and summarize its requirements."

The agent receives the same dataset records shown in the output example above.

### FAQ

**Which scorer should I use?** The default, `hosted` on the `fast` tier, reads the synopsis and writes reasons, disqualifiers, requirements and a paragraph for a flat $0.025 per opportunity with nothing to set up; move to `balanced` or `best` when eligibility fine print and subtle capability fit matter more than price. Use `byok` when you already have an Anthropic key and want to control the model and the token spend yourself. Use `rules` for bulk triage, monitoring and schedules: it is free of model costs, needs no key, and its reasons are verbatim facts (keyword hits, NAICS and set-aside matches, days to close). A good pattern: hosted `fast` with `minRulesScoreForModel: 35`, so the model only reads what the rules could not rule out.

**Does this need proxies or an API key?** No proxies. Grants.gov and USAspending are public APIs read over plain HTTP from Apify's standard infrastructure. A free SAM.gov personal key is needed only for SAM.gov notices; an Anthropic API key only for `byok` mode. `hosted` and `rules` on Grants.gov need nothing.

**How is cost controlled?** Cost = opportunities × (the fetched price when the Actor fetches them + the price of the scorer mode and tier you chose). Limit opportunities with `maxOpportunities`, narrow `searchTerms`, `postedWithinDays` and `samFilters`, pre-screen with `minRulesScoreForModel`, lower `maxInputTokens` in `byok` mode to cut your own token bill, and set the run's maximum total charge; the Actor checks it before every model call and stops cleanly when it is reached. Every model-mode item records `tokensIn`, `tokensOut` and `modelCostUsdEstimate`, and the run's `SUMMARY` record totals them.

**How is the fit score computed?** In `rules` mode: eight weighted components (keywords 30, NAICS 20, set-aside 15, eligibility 15, geography 10, deadline 10, budget 10, status 5), each 0–1 or not applicable, averaged over the applicable ones and scaled to 0–100; the result is then multiplied by `0.45 + 0.55 × relevance`, where relevance is the keyword and NAICS part, so an open, eligible grant about something else cannot score high. Hard disqualifiers (closed, already awarded, an applicant-type list or set-aside that excludes you) cap the score at 20. Verdicts: strong ≥ 70, possible ≥ 45, weak below, disqualified when a disqualifier applies. In model modes the model scores 0–100 against the same definitions, with the rules findings in its prompt; the deterministic score is kept on the item as `rulesFitScore`.

**Can I score opportunities from another feed or my own list?** Yes. Paste JSON records into `opportunities`: a `gov-opportunity-feed` dataset item is recognized as is; any other object needs at least a `title` or `description`, and uses `agency`, `naicsCode`, `setAside`/`setAsideCode`, `closeDate` (or `deadline`), `state`, `eligibilities`, `awardCeiling` and `url` when present. Supplied records are not charged the fetched event.

**Can an AI agent use this?** Yes; see "Use from AI agents (MCP)" above for the pin link and example prompts. Schedules and webhooks work the same way from the Console or the API.

**What happens on errors?** A search term or ID that cannot be fetched produces an `error` item with a clear code instead of failing the whole run; other opportunities are unaffected. A Grants.gov detail or SAM.gov notice text that cannot be downloaded leaves `detailError` or `descriptionError` on the item and the opportunity is scored from the fields that were fetched. A model call that fails falls back to the rules score (`scoreMethod: "rules-fallback"`, reason in `scoreWarning`) and is charged the rules price, so a hosted run never fails because of the model. Only a `byok` run with a missing or rejected key stops, with a clear message.

**Can I try it for free?** Yes. The Apify free plan comes with monthly platform credit, and the quick-start input above costs a few cents per run, so you can test it on your own inputs before paying anything.

**How do I find this Actor again?** Bookmark it on the Store page (the bookmark icon next to the title); bookmarked Actors are listed in your Console, and the Store page shows new versions as they ship.

### Legal

Use the data in compliance with each source's terms and applicable law. The Actor reads only the official public APIs of Grants.gov, SAM.gov (with your own key, under SAM.gov's API terms; it never scrapes the SAM.gov website) and USAspending.gov, keeps request rates polite, and in model modes sends the opportunity text and your profile to the Anthropic API solely to produce the score. Scores, reasons and summaries are advisory and machine-generated; they are not legal or eligibility determinations, may contain errors, and must be verified against the notice of funding opportunity or solicitation before a bid or application decision. Agency contact names and emails are reproduced only as the agencies publish them in the notices. The Actor is not a consumer reporting agency and its output must not be used for employment, credit, insurance, or tenant screening decisions.

# Actor input Schema

## `companyProfile` (type: `object`):

Your organization, described once. Fields (all optional, but give at least a description, capabilities or keywords): `name`, `description` (free text), `capabilities` (list), `naicsCodes` (list, e.g. \["541330"]), `setAsides` (list of SAM.gov codes or names: SBA, 8A, HZC, SDVOSBC, WOSB, EDWOSB, VSA…), `organizationType` (small business, nonprofit, university, state government, local government, tribal, individual, for-profit), `businessSize` (small/large or employee count), `locations` (states, regions or "nationwide"), `keywords` (list), `pastAwards` (list), `budgetRange` ({"min": 50000, "max": 2000000}). The prefilled profile is the sample used by Apify's daily automated test.

## `opportunities` (type: `array`):

Specific opportunities, one entry per line: Grants.gov opportunity IDs or URLs (e.g. `351083`, `https://www.grants.gov/search-results-detail/351083`), SAM.gov notice IDs or URLs (needs the SAM.gov API key below), or JSON records (a `gov-opportunity-feed` dataset item, or any object with `title`, `description`, `agency`, `naicsCode`, `setAside`, `closeDate`, `url`…). Supplied records are scored without a fetch. Leave empty to use Search terms instead.

## `searchTerms` (type: `array`):

Keywords to search in the enabled sources, one per line. Every match (up to Max opportunities) is fetched and scored. Used when Opportunities is empty, or in addition to it. The prefilled value is the cheap example used by Apify's daily automated test.

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

Which official APIs to search. Grants.gov and USAspending need no key; SAM.gov needs the free personal API key below and is skipped (with a log line) when the key is missing. USAspending returns recent awards (who won similar work), which are scored as competitive intelligence and always carry an "already awarded" disqualifier.

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

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

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

For search terms: only opportunities posted in the last N days (1–365). For USAspending this is the award start-date window.

## `maxOpportunities` (type: `integer`):

Upper bound on opportunities scored per run across supplied records, IDs and search terms (1–1,000). Each scored opportunity is one charged item; use it together with the run's maximum cost to control spend.

## `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 and SAM.gov notice IDs. Stored encrypted and never logged.

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

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

## `scorer` (type: `string`):

Leave empty for the default (the Console prefills `rules` so Apify's daily automated test never calls a model; clear it or pick a mode): AI scores by the hosted Claude model (`hosted`) when the Actor has its model key configured, otherwise the free rules scorer. Set `rules` for a free, model-free run (deterministic keyword, NAICS, set-aside, eligibility, geography, deadline and budget rules, no key), or `byok` to use your own Anthropic API key (tokens billed by Anthropic to you). In hosted mode a model problem never fails the run: the opportunity gets the rules score, `scoreMethod: "rules-fallback"` and is charged the rules price.

## `anthropicApiKey` (type: `string`):

Your Anthropic API key, used only when Scorer is `byok`. Stored encrypted as a secret and sent only to api.anthropic.com. Get one at console.anthropic.com.

## `modelTier` (type: `string`):

`fast` (default): Claude Haiku 5.5, cheapest and quick. `balanced`: Claude Sonnet 5.5, better reasoning about eligibility and requirements. `best`: Claude Opus 5.5 for the most nuanced bid/no-bid judgment. In hosted mode the tier sets the per-opportunity price ($0.025 / $0.30 / $0.60); in byok mode the tier only changes what Anthropic bills you.

## `minRulesScoreForModel` (type: `integer`):

Model modes only. Opportunities whose deterministic rules score is below this value (0–100) are written with the rules score at the rules price and never sent to the model. 0 (default) sends every opportunity to the model. 30–40 is a sensible cut for broad search terms.

## `maxInputTokens` (type: `integer`):

Descriptions longer than this (estimated) are cut to the cap before the model call and the item is marked `inputTruncated: true`. 8,000 tokens is about 28,000 characters, more than any Grants.gov synopsis or SAM.gov notice text (cut at 20,000 characters). Lower it to cut your own token cost in byok mode.

## `maxConcurrency` (type: `integer`):

Opportunities processed in parallel (detail fetches and model calls). Requests to the same government host are spaced at least 500 ms apart.

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

If set, the Actor POSTs a JSON summary of the run (counts, verdicts, the top 10 opportunities, model token totals) to this URL — 10 s timeout, 2 retries. Failures are logged, never fatal.

## Actor input object example

```json
{
  "companyProfile": {
    "name": "Cascadia Civil Engineering",
    "description": "Civil engineering firm with 40 staff, NAICS 541330, SBA small business, serving the Pacific Northwest; past EPA and USDA rural water and wastewater grants.",
    "capabilities": [
      "water infrastructure design",
      "wastewater treatment",
      "stormwater management",
      "rural water systems",
      "environmental engineering"
    ],
    "naicsCodes": [
      "541330",
      "237110"
    ],
    "setAsides": [
      "SBA"
    ],
    "organizationType": "small business",
    "businessSize": "small",
    "locations": [
      "WA",
      "OR",
      "ID"
    ],
    "keywords": [
      "water",
      "wastewater",
      "infrastructure",
      "drinking water",
      "rural"
    ],
    "pastAwards": [
      "EPA Water Infrastructure Improvements for the Nation",
      "USDA Rural Development Water and Waste Disposal"
    ],
    "budgetRange": {
      "min": 50000,
      "max": 5000000
    }
  },
  "opportunities": [],
  "searchTerms": [
    "water infrastructure"
  ],
  "sources": [
    "grantsGov"
  ],
  "oppStatuses": [
    "posted"
  ],
  "postedWithinDays": 30,
  "maxOpportunities": 25,
  "samFilters": {},
  "scorer": "rules",
  "modelTier": "fast",
  "minRulesScoreForModel": 0,
  "maxInputTokens": 8000,
  "maxConcurrency": 3
}
```

# Actor output Schema

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

One item per opportunity in the default dataset: the normalized record (source, opportunityId, title, agency, status, postedDate, closeDate, url, description, NAICS, set-aside, eligibilities, award amounts) plus fitScore (0–100), fitVerdict, recommendedAction, fitReasons, disqualifiers, keyRequirements, fitSummary, daysToClose, rulesFitScore, rulesBreakdown, scoreMethod and, in model modes, modelTier, model, tokensIn, tokensOut, modelCostUsdEstimate; 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: opportunities fetched and scored, counts per verdict and score method, the top 10 opportunities by fit score, model token totals and cost estimate, 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 = {
    "companyProfile": {
        "name": "Cascadia Civil Engineering",
        "description": "Civil engineering firm with 40 staff, NAICS 541330, SBA small business, serving the Pacific Northwest; past EPA and USDA rural water and wastewater grants.",
        "capabilities": [
            "water infrastructure design",
            "wastewater treatment",
            "stormwater management",
            "rural water systems",
            "environmental engineering"
        ],
        "naicsCodes": [
            "541330",
            "237110"
        ],
        "setAsides": [
            "SBA"
        ],
        "organizationType": "small business",
        "businessSize": "small",
        "locations": [
            "WA",
            "OR",
            "ID"
        ],
        "keywords": [
            "water",
            "wastewater",
            "infrastructure",
            "drinking water",
            "rural"
        ],
        "pastAwards": [
            "EPA Water Infrastructure Improvements for the Nation",
            "USDA Rural Development Water and Waste Disposal"
        ],
        "budgetRange": {
            "min": 50000,
            "max": 5000000
        }
    },
    "searchTerms": [
        "water infrastructure"
    ],
    "sources": [
        "grantsGov"
    ],
    "oppStatuses": [
        "posted"
    ],
    "postedWithinDays": 30,
    "maxOpportunities": 25,
    "scorer": "rules"
};

// Run the Actor and wait for it to finish
const run = await client.actor("owlabee/gov-opportunity-fit-scorer").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 = {
    "companyProfile": {
        "name": "Cascadia Civil Engineering",
        "description": "Civil engineering firm with 40 staff, NAICS 541330, SBA small business, serving the Pacific Northwest; past EPA and USDA rural water and wastewater grants.",
        "capabilities": [
            "water infrastructure design",
            "wastewater treatment",
            "stormwater management",
            "rural water systems",
            "environmental engineering",
        ],
        "naicsCodes": [
            "541330",
            "237110",
        ],
        "setAsides": ["SBA"],
        "organizationType": "small business",
        "businessSize": "small",
        "locations": [
            "WA",
            "OR",
            "ID",
        ],
        "keywords": [
            "water",
            "wastewater",
            "infrastructure",
            "drinking water",
            "rural",
        ],
        "pastAwards": [
            "EPA Water Infrastructure Improvements for the Nation",
            "USDA Rural Development Water and Waste Disposal",
        ],
        "budgetRange": {
            "min": 50000,
            "max": 5000000,
        },
    },
    "searchTerms": ["water infrastructure"],
    "sources": ["grantsGov"],
    "oppStatuses": ["posted"],
    "postedWithinDays": 30,
    "maxOpportunities": 25,
    "scorer": "rules",
}

# Run the Actor and wait for it to finish
run = client.actor("owlabee/gov-opportunity-fit-scorer").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 '{
  "companyProfile": {
    "name": "Cascadia Civil Engineering",
    "description": "Civil engineering firm with 40 staff, NAICS 541330, SBA small business, serving the Pacific Northwest; past EPA and USDA rural water and wastewater grants.",
    "capabilities": [
      "water infrastructure design",
      "wastewater treatment",
      "stormwater management",
      "rural water systems",
      "environmental engineering"
    ],
    "naicsCodes": [
      "541330",
      "237110"
    ],
    "setAsides": [
      "SBA"
    ],
    "organizationType": "small business",
    "businessSize": "small",
    "locations": [
      "WA",
      "OR",
      "ID"
    ],
    "keywords": [
      "water",
      "wastewater",
      "infrastructure",
      "drinking water",
      "rural"
    ],
    "pastAwards": [
      "EPA Water Infrastructure Improvements for the Nation",
      "USDA Rural Development Water and Waste Disposal"
    ],
    "budgetRange": {
      "min": 50000,
      "max": 5000000
    }
  },
  "searchTerms": [
    "water infrastructure"
  ],
  "sources": [
    "grantsGov"
  ],
  "oppStatuses": [
    "posted"
  ],
  "postedWithinDays": 30,
  "maxOpportunities": 25,
  "scorer": "rules"
}' |
apify call owlabee/gov-opportunity-fit-scorer --silent --output-dataset

```

## MCP server setup

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

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/vsS5FXh5TphcP1IeZ/builds/JdrJS7QlUvoyOwF68/openapi.json
