Search opportunities
Search federal contract notices with filters. Returns paginated results.
Every filter is optional. To keep a full local copy in sync, use /api/v1/opportunities/delta: pass a timestamp, get only what changed since.
Combining filters
Every filter on this endpoint combines with AND. A request with naics=541512, state=VA, and content=CMMC returns only opportunities that match all three at once, and any filter can be mixed with any other (content with set_aside, active_only with date_from, and so on). The server echoes the filters it actually applied in filters_applied on every response, so you can confirm nothing was dropped. Two pairs are mutually exclusive, and both are covered below: naics vs naics_multiple (if both are sent, naics_multiple wins), and the value_*/amount_* money bounds (sending both forms of the same bound returns 400).
Basic filters (all plans)
naics: Single NAICS code (e.g.541330)psc: Product Service Code (e.g.D302). Accepts a comma-separated list (OR'd) and a trailing-*prefix:psc=7510,R408,psc=70*(all 70-series), orpsc=R*(all R-series service codes). A bare code stays an exact match.psc_type: coarse class of the notice's primary PSC,supply(numeric codes, products) orservices(letter-prefixed codes). A first-pass supply-vs-services split without listing individual codes.state: State code (e.g.CA,TX) or full name (e.g.California)keywords: Full-text search across title, agency name, and contract description. Also matches solicitation number. Hyphenated terms (e.g.fluorine-free) are handled automatically. Results are ranked by relevance to title and agency by default (descriptions still participate in matching but not in ranking, since they include standard regulatory clause text that would otherwise dilute results).notice_type: Filter by notice type (e.g.Solicitation,Award Notice,Presolicitation). Accepts multiple comma-separated values, matched as OR:notice_type=Solicitation,Presolicitation,Combined Synopsis/Solicitationsolicitation_number: Exact solicitation number (e.g.W52P1J-25-R-0001)limit: Results per page. Default 100; maximum 100 on the free trial and 1,000 on paid plans. Over the maximum returns402.offset: Pagination offset (default 0)fields: Comma-separated response fields to return.notice_idalways included. Example:fields=notice_id,title,naics,posted_date. See Response Shaping, and Response fields below for the full list of the 59 field names.active_only:true/false. Whentrue, returns only currently-open notices (those whose archive date is in the future). Echoed infilters_appliedwhen set so a client can confirm the filter applied. Computed asarchive_date_detailed > today; the storedactivefield is always"Yes"by design and is not what this filter reads.
Concepts to know before you integrate. These are assumptions the data makes that are not obvious from the field names; they cause the most integration mistakes.
notice_idvssolicitation_number. One procurement (onesolicitation_number) moves through a lifecycle: Sources Sought → Presolicitation → Solicitation → amendments → Award Notice. Every stage and every amendment is a separatenotice_id(a new 32-char hex). One solicitation maps to manynotice_ids. To count unique procurements, dedupe onsolicitation_number(falling back tonotice_idwhen null). To walk a procurement's lifecycle, filter bysolicitation_number=....- Three different date fields.
posted_dateis a calendar date (no time).response_deadlineis a timezone-aware timestamp in UTC (+00:00), the bid deadline (precision varies by source).archive_date_detailedis a future calendar date (when the notice will be archived). All timestamp fields on this endpoint (includinglast_seen) are returned in UTC. Filters map to these:date_from/date_to/posted_afterfilterposted_date;due_after/due_beforefilterresponse_deadline;active_onlyreadsarchive_date_detailed. activeis always "Yes". The storedactivefield is a constant by design ("never archived" is a product feature). Do not filter on it. Useactive_only=trueto get currently-open notices.description_textis tri-state. Real description text, the literal sentinel"NO_DESCRIPTION_AVAILABLE"(the notice has no description in the source), ornull(description not yet retrieved). Handle all three.set_aside_type"none" is tri-state too:"","NONE", ornullall mean unrestricted. A real code (SDVOSBC,8A,WOSB,HZC,SBA, etc.) means restricted. Majority of set-asides areSBA.- Combined notices are common. "Combined Synopsis/Solicitation" is roughly a third of opportunities. When pulling bidding opportunities, pass the types together in one call (comma-separated values are OR'd):
notice_type=Solicitation,Presolicitation,Combined Synopsis/Solicitation. - Award amounts only on Award Notices.
award_amount,awardee_name,award_date, and related fields are populated only whennotice_type=Award Notice. Solicitations and Sources Sought do not have these fields.
notice_type valid values (exact case-insensitive match, enforced — anything else returns 400
with the valid list): Solicitation, Combined Synopsis/Solicitation, Presolicitation,
Sources Sought, Award Notice, Justification, Justification and Approval (J&A),
Special Notice, Sale of Surplus Property, Modification/Amendment/Cancel,
Consolidate/(Substantially) Bundle. Solicitation does NOT match Presolicitation. Pass several at
once, comma-separated, to match any of them (notice_type=Solicitation,Presolicitation) — every value
in the list must be valid, or the whole request 400s.
set_aside valid values (case-insensitive; a plain-English term OR the exact SAM code both
work, anything else returns 400 with the valid list): a generic family term matches every variant
of that set-aside (competitive and sole-source together).
| Term you can type | Also matches (exact codes) |
|---|---|
8(a) |
8A, 8AN (competed + sole source) |
SDVOSB |
SDVOSBC, SDVOSBS (competed + sole source) |
WOSB / Women Owned |
WOSB, WOSBSS (competed + sole source) |
EDWOSB |
EDWOSB, EDWOSBSS (competed + sole source) |
HUBZone |
HZC, HZS (competed + sole source) |
VOSB / Veteran |
VSA, VSS (competed + sole source) |
Small Business |
SBA (the majority of set-asides) |
Indian Small Business |
ISBEE |
Indian Economic Enterprise |
IEE |
Buy Indian |
BICiv (Dept. of Health & Human Services / Indian Health Service specific) |
Local Area |
LAS |
Emerging Small Business |
ESB (rare) |
To get only the sole-source half of a family, pass the exact code directly (set_aside=SDVOSBS,
set_aside=HZS, set_aside=8AN, ...) instead of the family term. set_aside=SBP (Partial Small
Business Set-Aside) has no shorter alias — it's distinct from SBA (Total Small Business
Set-Aside) and uncommon enough that most callers want SBA.
Undocumented aliases. Four parameter aliases are accepted for forgiveness: q and keyword map to keywords; posted_from maps to date_from; posted_to maps to date_to. Canonical names win if both forms are sent.
naics + naics_multiple: if both are sent, naics_multiple wins and the single naics is silently dropped. Pick one. A 1-digit or 7+ digit value returns 400; a 2-5 digit prefix returns 400 with the "must be 6-digit" message; a well-formed but nonexistent 6-digit code returns 200 with total=0 (an empty result can mean "bad code," not "no opportunities").
Date, location, amount, and agency filters
agency: Agency name (partial match, e.g.defense)set_aside: Set-aside type. Accepts either a plain-English term or the exact SAM code (both case-insensitive) — anything else returns400with the valid list. Seeset_asidevalid values below.posted_after: Posted after date (YYYY-MM-DD format)due_before: Response deadline before date (YYYY-MM-DD format)due_after: Response deadline after date (YYYY-MM-DD format). Usedue_after=<today>to return only open opportunities.value_min: Minimum award value in dollars (only for Award Notice records)value_max: Maximum award value in dollars (only for Award Notice records)location: Performance location (city or state)date_from: Posted date from (YYYY-MM-DD format)date_to: Posted date to (YYYY-MM-DD format)sort_by: Sort field. Valid:posted_date(default when no keywords),due_date(maps toresponse_deadline),award_amount,title,agency,relevance. Invalid value returns 400 with the valid list (no silent fallback). Whenkeywordsis set andsort_byis omitted, results default torelevance(best title and agency matches first); passsort_by=posted_dateto override.sort_order:ascordesc(defaultdesc). Ignored whensort_by=relevance. NULLs sort first onasc, last ondesc. Results are tie-broken bynotice_idso pagination is stable.naics_multiple: Comma-separated NAICS codes for multiple searchhas_attachments: Filter by attachment availability:trueorfalse
Search inside attachment documents (Pro)
content: Full-text search across the documents attached to an opportunity (PDF, Word, Excel, PowerPoint, and scanned pages read by OCR), not just the notice title and description thatkeywordscovers. Over 209,000 opportunities carry at least one searchable attachment.
content and keywords search different layers. keywords matches the notice's title, agency, and description. content matches the text inside the attached solicitation, statement of work, and price schedule, where the requirements that decide whether you bid usually live: the award method (lowest-price-technically-acceptable vs best-value), the clearance level, the period of performance and option years, the incumbent, wage determinations, subcontracting plans, and compliance bars such as FedRAMP or CMMC. None of those are notice-level fields.
Query syntax matches keywords: multiple words match documents containing all of them (content=CMMC Level 2), and a quoted string matches that exact phrase (content="active secret clearance").
content is not tied to any one filter, it combines with all of them (every filter on this endpoint ANDs together, as described under Combining filters above). Scope a document search to an industry (content=CMMC&naics=541512), to open opportunities only (content=FedRAMP&active_only=true), to a place of performance (content=incumbent&state=VA), to a set-aside (content="Berry Amendment"&set_aside=SDVOSB), or to a date range (content=SBOM&date_from=2025-06-01).
content requires the Pro plan. On other plans the request returns 402 and reports how many opportunities match, so you can see the coverage before upgrading:
{
"detail": "5,312 opportunities have solicitation documents matching 'CMMC'. Full-text search inside attachment documents requires the Pro plan ($39/mo), no per-query limits, 1,000 requests/hour. Upgrade at https://govconapi.com/#pricing"
}
Response
{
"data": [
{
"notice_id": "abc123def456",
"title": "IT Services Contract",
"agency": "Department of Defense",
"posted_date": "2025-11-01",
"response_deadline": "2025-12-15T17:00:00+00:00",
"naics": ["541330"],
"set_aside_type": "Small Business",
"solicitation_number": "W52P1J-25-R-0001",
"description_text": "Full contract requirements...",
"award_amount": 1500000.00,
"awardee_name": "Tech Solutions Inc",
"contact_name": "John Smith",
"contact_email": "[email protected]",
"sam_url": "https://sam.gov/opp/...",
"resource_links_array": ["https://sam.gov/api/prod/opps/v3/opportunities/resources/files/<id>/download"],
"performance_city_name": "Washington",
"performance_state_code": "DC",
"_additional": "59 fields total per record"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 343,
"total_is_estimate": false,
"has_next": true
},
"filters_applied": {
"naics": "541330",
"state": "CA"
},
"window": {
"plan_window_days": null,
"earliest_searchable": null,
"clamped": false
}
}
Total count is capped at 10,000
On broad searches, pagination.total stops at 10000 and the response sets "total_is_estimate": true (read it as "10,000+"). Exact counts on very broad full-text matches are expensive, so the count short-circuits past 10,001. Page forward with has_next; do not compute the last page from total when total_is_estimate is true. The other list endpoints (awards, companies, entities, exclusions) return an exact total.
The window block is always present on this endpoint. On paid plans it reads as in the example above: no window. On the free trial it shows the 90-day window and whether your query was clamped; see Search Window above for the field-by-field explanation.
Response fields (59 per record)
Every record carries the same 59 fields, and the single-notice endpoint /opportunities/{notice_id} returns this identical shape. Pass fields= to request only the ones you need. The response is a flat object; the groups below are for reading only.
Identity and lifecycle
notice_id(string): 32-char hex, unique per notice stage and amendment. Always returned, your row-to-query join key.solicitation_number(string, nullable): human contract identifier, shared across a procurement's stages. Dedupe on this for unique procurements.title(string): notice title.notice_type(string): stage or type (Solicitation,Award Notice, and the rest listed above).notice_base_type(string): base type before amendment suffixing, usually equal tonotice_type.active(string): always"Yes"by design (notices are never archived). Do not filter on it, useactive_only.source_version(string): record vintage (livefor pipeline-ingested,historicalfor backfilled).
Dates
posted_date(string,YYYY-MM-DD): calendar date the notice posted (no time component).response_deadline(string, timestamp, nullable): bid deadline, timezone-aware, precision varies by source. Filtered bydue_after/due_before.archive_date_detailed(string,YYYY-MM-DD, nullable): future date the notice will be archived, and whatactive_onlyreads.archive_type_detailed(string, nullable): how the archive date is derived (e.g.auto15).last_seen(string, timestamp): when the ingest pipeline last observed this notice.
Classification
naics(array of string): NAICS code(s), an array even when there is one.psc(array of string): Product and Service Code(s), an array.set_aside_type(string, nullable): set-aside code (SDVOSBC,8A,WOSB, and so on)."","NONE", andnullall mean unrestricted (tri-state).set_aside_description(string, nullable): human-readable set-aside label.
Agency
agency(string): full org hierarchy as a dotted name path (department.sub-tier.office).agency_path_code(string, nullable): the same hierarchy as numeric codes (e.g.097.97F2.HE1254).office_country_code(string, nullable): issuing office country.organization_type(string, nullable): issuing org level (e.g.OFFICE).
Award (populated on notice_type=Award Notice only)
award_number(string, nullable): award or contract number.award_amount(number, nullable): award value in dollars.award_date(string,YYYY-MM-DD, nullable): date awarded.award_uei_sam(string, nullable): awardee SAM UEI, often null on older or thin award notices.award_cage_code(string, nullable): awardee CAGE code.
Awardee (on Award Notices)
awardee_name(string, nullable).awardee_street_address,awardee_street_address2(string, nullable).awardee_city_name,awardee_city_code(string, nullable).awardee_state_name,awardee_state_code(string, nullable).awardee_zip(string, nullable).awardee_country_name,awardee_country_code(string, nullable).
Place of performance
performance_city_name,performance_city_code(string, nullable).performance_state_name,performance_state_code(string, nullable).performance_zip(string, nullable).performance_country_name,performance_country_code(string, nullable).performance_street_address,performance_street_address2(string, nullable).
Contacts
contact_name,contact_title,contact_email,contact_phone,contact_fax(string, nullable): primary point of contact.primary_contact_type(string, nullable): label for the primary contact.secondary_contact_name,secondary_contact_email(string, nullable): secondary point of contact.
Links and description
sam_url(string): human sam.gov workspace URL for the notice.ui_link(string): sam.gov UI link (frequently identical tosam_url).api_self_link(string): SAM API self link for the notice.additional_info_link(string, nullable): extra information URL when the notice provides one.description_url(string, nullable): SAM API URL for the raw description.description_text(string, nullable): full description. Tri-state: real text, the literal"NO_DESCRIPTION_AVAILABLE"(no description in the source), ornull(not yet retrieved, newly posted notices populate within a short window).resource_links_array(array of string): canonical sam.gov download URLs for the notice's attached documents,[]when the notice has none. To search inside those documents, use the Procontentfilter above.
Examples
NAICS + state:
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://govconapi.com/api/v1/opportunities/search?naics=541330&state=CA&limit=10"
NAICS 541330 (engineering services) in California returns the matching opportunities (343 on a paid plan on 2026-09-14; the free trial sees the last 90 days)
Agency filter:
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://govconapi.com/api/v1/opportunities/search?agency=defense&limit=20"
A broad filter like this matches far more than the count cap, so pagination.total reads 10000 with total_is_estimate: true (page forward with has_next)
Paid plans - High-value awards:
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://govconapi.com/api/v1/opportunities/search?notice_type=Award%20Notice&value_min=1000000&sort_by=award_amount"
Note: value_min only works with Award Notice records (completed contracts)
Paid plans - Multiple NAICS codes:
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://govconapi.com/api/v1/opportunities/search?naics_multiple=541330,541511&limit=10"
Paid plans - Only opportunities with attachments:
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://govconapi.com/api/v1/opportunities/search?has_attachments=true&limit=20"
Not all opportunities have attachments, check resource_links_array in the response.
Pro - Search inside the documents, scoped to a NAICS:
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://govconapi.com/api/v1/opportunities/search?content=CMMC&naics=541512&sort_by=posted_date&limit=2"
{
"data": [
{
"notice_id": "9844dc9a31814234bbfef9359f22676f",
"title": "CIO Microsoft Sentinel Implementation FY26",
"agency": "DEPT OF DEFENSE.DEPT OF THE ARMY.AMC.ACC.MISSION INSTALLATION CONTRACTING COMMAND.FDO EUSTIS.W6QM MICC-CARLISLE BARRACKS",
"posted_date": "2026-07-01"
}
],
"pagination": { "limit": 2, "offset": 0, "total": 108, "total_is_estimate": false, "has_next": true },
"filters_applied": { "naics": "541512", "content": "CMMC", "date_from": "2024-07-15", "sort_by": "posted_date", "sort_order": "desc" }
}
Both filters apply together: every row is NAICS 541512 and its attached documents mention CMMC. content=CMMC alone matches 5,312 opportunities; adding the NAICS narrows it to 108. The matching text lives in the solicitation or statement of work, not the notice title.