Grants by agency — track new grant opportunities¶
Get a webhook when new grant opportunities post at agencies you care about, optionally narrowed by CFDA number, applicant type, or response window.
The 1-line answer¶
Create a filter alert on query_type=grant. Filters are the same query parameters you'd pass to /api/grants/.
curl -X POST -H "X-API-KEY: $TANGO_API_KEY" \
-H "Content-Type: application/json" \
"https://tango.makegov.com/api/webhooks/alerts/" \
-d '{
"name": "DHS cybersecurity grants",
"query_type": "grant",
"filters": {
"agency": "DHS",
"cfda_number": "97.067"
},
"frequency": "daily"
}'
That's it. Tango re-evaluates daily and POSTs alerts.grant.match events when new grants match.
Step 1 — Verify the filter against the grants API¶
curl -H "X-API-KEY: $TANGO_API_KEY" \
"https://tango.makegov.com/api/grants/?agency=DHS&cfda_number=97.067&ordering=-posted_date&limit=5"
If this returns the grants you expect, the alert will fire when new ones land.
Step 2 — Create the alert¶
curl -X POST -H "X-API-KEY: $TANGO_API_KEY" \
-H "Content-Type: application/json" \
"https://tango.makegov.com/api/webhooks/alerts/" \
-d '{
"name": "DHS cybersecurity grants — open in next 60d",
"query_type": "grant",
"filters": {
"agency": "DHS",
"cfda_number": "97.067",
"applicant_types": "00",
"response_date_after": "2026-05-12",
"response_date_before": "2026-07-12"
},
"frequency": "daily"
}'
import os
from datetime import date, timedelta
from tango import TangoClient
client = TangoClient(api_key=os.environ["TANGO_API_KEY"])
today = date.today()
sixty_days = today + timedelta(days=60)
alert = client.create_webhook_alert(
name="DHS cybersecurity grants — open in next 60d",
query_type="grant",
filters={
"agency": "DHS",
"cfda_number": "97.067",
"applicant_types": "00", # State governments
"response_date_after": today.isoformat(),
"response_date_before": sixty_days.isoformat(),
},
frequency="daily",
)
print(alert.alert_id, alert.status)
import { TangoClient } from "@makegov/tango-node";
const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY! });
const today = new Date().toISOString().slice(0, 10);
const sixtyDays = new Date(Date.now() + 60 * 24 * 3600 * 1000)
.toISOString()
.slice(0, 10);
const alert = await client.createWebhookAlert({
name: "DHS cybersecurity grants — open in next 60d",
query_type: "grant",
filters: {
agency: "DHS",
cfda_number: "97.067",
applicant_types: "00", // State governments
response_date_after: today,
response_date_before: sixtyDays,
},
frequency: "daily",
});
console.log(alert.alert_id, alert.status);
Step 3 — Available grant filters¶
These are the keys accepted by /api/grants/ and therefore by query_type=grant alerts.
| Filter | Notes |
|---|---|
search | Full-text search across title and description (vector-backed). |
agency | Substring match on agency abbreviation. "DHS" matches DHS-FEMA, DHS-CISA, etc. |
opportunity_number | Exact opportunity number. |
cfda_number | CFDA number, substring match (e.g. "97.067"). |
status | Opportunity status (case-insensitive choice). |
applicant_types | Eligibility / applicant type code (case-insensitive choice). |
funding_categories | Funding category codes. |
funding_instruments | Funding instrument codes. |
posted_date_after / posted_date_before | Posted date range (YYYY-MM-DD). |
response_date_after / response_date_before | Application deadline range (YYYY-MM-DD). |
Filter names matter
The filter is cfda_number (not cfda) and applicant_types (not eligibility). Using the wrong key returns 400.
For full filter docs, see the Grants API reference.
Step 4 — Receive alerts.grant.match events¶
{
"timestamp": "2026-05-12T08:00:14Z",
"delivery_id": "8c5e3f6a-...-9b21",
"events": [
{
"event_type": "alerts.grant.match",
"alert_id": "e4c4...-...-...",
"query_type": "grant",
"filters": {
"agency": "DHS",
"cfda_number": "97.067"
},
"matches": {
"new_count": 2,
"modified_count": 1,
"new": [
{"grant_id": "...", "opportunity_number": "DHS-26-CISA-067-001", "title": "Cybersecurity Grant Program FY26", "agency_code": "DHS-CISA"},
{"grant_id": "...", "opportunity_number": "DHS-26-CISA-067-002", "title": "State and Local Cybersecurity Pilot", "agency_code": "DHS-CISA"}
],
"modified": [
{"grant_id": "...", "opportunity_number": "DHS-25-CISA-067-014", "title": "...", "agency_code": "DHS-CISA"}
]
},
"checked_at": "2026-05-12T08:00:12.000Z"
}
]
}
Pull full grant details from /api/grants/{grant_id}/ (or via the SDK's client.get_grant(grant_id) / client.getGrant(grantId)) when you need more than the summary fields.
Limitations¶
- No multi-value
agencyshortcut.agencyis a substring match; if you need to watch DHS and HHS, create two alerts (or usesearch=with a broader term). - Polling-friendly cadences. Grants don't post every minute —
dailyorweeklyis usually plenty.realtimeworks but most agencies refresh on a daily cycle anyway. - Tier caps apply. Free 1 / Micro 3 / Small 5 / Medium 10 / Large 25 simultaneous alerts.
Related¶
- Webhooks user guide — protocol reference
- Grants API reference — full filter list
- Recipes index