Skip to content

Awards by NAICS — stream new contracts in your industry codes

Get a webhook whenever new contracts land in one or more NAICS codes you care about. Optionally narrow by agency, set-aside, or dollar floor.

The 1-line answer

Create a single filter alert on query_type=contract with the NAICS codes you want — use | to OR multiple codes into one 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": "IT consulting NAICS — 541511 / 541512 / 541519",
    "query_type": "contract",
    "filters": { "naics": "541511|541512|541519" },
    "frequency": "realtime"
  }'

That's it. One alert covers any number of NAICS codes.

Step 1 — Verify the filter against the contracts API

curl -H "X-API-KEY: $TANGO_API_KEY" \
  "https://tango.makegov.com/api/contracts/?naics=541511|541512|541519&ordering=-award_date&limit=5"

If this returns recent contracts in your codes, you're good.

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": "IT consulting NAICS",
    "query_type": "contract",
    "filters": {
      "naics": "541511|541512|541519",
      "obligated_gte": "100000"
    },
    "frequency": "realtime"
  }'
import os
from tango import TangoClient

client = TangoClient(api_key=os.environ["TANGO_API_KEY"])

alert = client.create_webhook_alert(
    name="IT consulting NAICS",
    query_type="contract",
    filters={
        "naics": "541511|541512|541519",
        "obligated_gte": "100000",
    },
    frequency="realtime",
)
print(alert.alert_id, alert.status)
import { TangoClient } from "@makegov/tango-node";

const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY! });

const alert = await client.createWebhookAlert({
  name: "IT consulting NAICS",
  query_type: "contract",
  filters: {
    naics: "541511|541512|541519",
    obligated_gte: "100000",
  },
  frequency: "realtime",
});
console.log(alert.alert_id, alert.status);

Step 3 — Refine with optional filters

Stack any of these on top of naics to narrow the alert. All filters are AND-combined.

Filter Notes
funding_agency / awarding_agency Vector-backed agency match — "DOD", "Navy", "NAVSEA" all work. Pick the role you actually care about.
psc Six-digit PSC code; same \| multi-value syntax as naics.
set_aside SBA, 8A, WOSB, SDVOSB, etc. Multi-value via \|.
obligated_gte / obligated_lte Dollar thresholds.
recipient_state / place_of_performance Geographic filters.
ordering Doesn't apply to alerts (the evaluator processes new matches as they arrive).

Example with all the dials turned:

{
  "name": "DOD IT services > $1M, small business",
  "query_type": "contract",
  "filters": {
    "funding_agency": "DOD",
    "naics": "541511|541512|541519",
    "obligated_gte": "1000000",
    "set_aside": "SBA|8A|WOSB|SDVOSB"
  },
  "frequency": "realtime"
}

Step 4 — Receive alerts.contract.match events

{
  "timestamp": "2026-05-12T18:20:14Z",
  "delivery_id": "8c5e3f6a-...-9b21",
  "events": [
    {
      "event_type": "alerts.contract.match",
      "alert_id": "e4c4...-...-...",
      "query_type": "contract",
      "filters": {
        "naics": "541511|541512|541519",
        "obligated_gte": "100000"
      },
      "matches": {
        "new_count": 3,
        "modified_count": 0,
        "new": [
          {"id": "CONT_AWD_...", "piid": "W15QKN24C1234", "obligated": 1450000, "recipient_uei": "ABC123..."},
          {"id": "CONT_AWD_...", "piid": "FA8773-24-C-...", "obligated": 230000, "recipient_uei": "DEF456..."},
          {"id": "CONT_AWD_...", "piid": "N00033-24-D-...", "obligated": 5400000, "recipient_uei": "GHI789..."}
        ],
        "modified": []
      },
      "checked_at": "2026-05-12T18:20:12.000Z"
    }
  ]
}

For the full receiver implementation (signature verification, idempotency, fast 2xx), see Stream contract awards in real time.

Limitations

  • One alert per saved query. If you want different downstream routing per NAICS, create separate alerts and dispatch on alert_id in your receiver.
  • Tier caps apply. Free 1 / Micro 3 / Small 5 / Medium 10 / Large 25 simultaneous alerts.
  • Realtime is "after each ingestion cycle" — FPDS ingest runs roughly twice daily. Realtime delivers within minutes of each refresh, not within minutes of upstream FPDS publication.
  • naics_starts_with is not a contracts filter. Contracts only support exact naics. If you need a NAICS prefix, list the codes explicitly with |.