GovCon API Documentation

Federal contracts API for developers. Clean JSON: opportunities, awards, the SAM entity registry (~873K registered firms), exclusions, contracting-officer contacts, and full notice descriptions.

For interactive API testing, visit /docs.

Quick Start: Get a free API key (emailed instantly):
curl -X POST "https://govconapi.com/api/v1/keys?email=YOUR_EMAIL&plan=free"

All example counts and response values in this documentation are illustrative. Use /api/stats or /meta for current numbers.

Critical: The active field is always "Yes"
Do NOT filter by active - it's meaningless (always "Yes" from SAM.gov).
Use response_deadline to find open opportunities and notice_type to understand lifecycle stage.
Plan restrictions. Advanced filters require the Developer plan. CSV export requires the Developer plan. Free trial has basic filters only.

Plans & Limits

Plan Price Rate Limit Results / Request CSV Exports / Day Endpoints Included
Free Trial (14 days) $0 25 / day 50 0 Opportunities (basic filters), Awards, Exclusions, Entities (UEI / CAGE / name lookup), Utilities
Developer $19 / mo 1,000 / hour 1,000 15 Everything in Free Trial + advanced filters + CSV export. SAM Entity registry lookup by UEI/CAGE/name available on this tier.
Pro Bundle $39 / mo 1,000 / hour 1,000 15 Everything in Developer + Companies (search, profile, awards, peers) + Entities multi-filter search (DSBS replacement) + Entities expiration radar + Vendor Risk (single, bulk) + GovCon Contacts

Endpoints with the Pro Bundle badge below require Pro Bundle. Pricing details and full plan comparison: govconapi.com/#pricing.

Authentication

Base URL: https://govconapi.com/api/v1

Send your API key in the Authorization header using the Bearer format:

Authorization: Bearer YOUR_API_KEY

API keys are available at govconapi.com.

Endpoints

GET

/api/v1/opportunities/search

Requires auth

Search federal contract notices with filters. Returns paginated results.

At least one meaningful filter is required. Provide at least one filter parameter (e.g. naics, keywords, state, date_from, agency). Filter values must be meaningful:
  • keywords: minimum 3 characters
  • naics, psc, state, set_aside, notice_type, agency, location, solicitation_number, naics_multiple: minimum 2 characters
  • date_from, date_to, posted_after, due_before, due_after: full date format YYYY-MM-DD
  • value_min, value_max, has_attachments: any valid value
Parameters like limit, offset, and sort_by do not count as filters. Requests without a valid filter return 400. For full-database sync, use /api/v1/opportunities/delta (keyset-based, no depth penalty).

Basic filters (all plans)

  • naics – Single NAICS code (e.g. 541330)
  • psc – Product Service Code (e.g. D302)
  • 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.
  • notice_type – Filter by notice type (e.g. Solicitation, Award Notice, Presolicitation)
  • solicitation_number – Exact solicitation number (e.g. W52P1J-25-R-0001)
  • limit – Results per page (default 20, max: Free=50, Developer=1000)
  • offset – Pagination offset (default 0)
Important Data Patterns:
  • To find open opportunities - Use due_after=<today> to filter to opportunities where the response deadline hasn't passed. Combine with notice_type for best results.
  • Combined notices are very common - About a third of opportunities are "Combined Synopsis/Solicitation". When searching for bidding opportunities, filter for both notice_type=Solicitation and notice_type=Combined%20Synopsis/Solicitation to catch everything.
  • Award amounts only exist on awards - "Award Notice" records have award_amount, awardee_name, etc. Solicitations don't have these fields yet.
  • Amendments create duplicate solicitation_numbers - Same solicitation can appear multiple times as it's modified. Use posted_date or notice_id to track versions.
  • Most are for small business - Majority of set-asides are type "SBA".

Advanced filters (Developer plan)

  • agency – Agency name (partial match, e.g. defense)
  • set_aside – Set-aside type (e.g. SBA, 8(a), SDVOSB)
  • 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). Use due_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 (posted_date, response_deadline, award_amount, title, agency)
  • sort_order – Sort order: asc or desc (default: desc)
  • naics_multiple – Comma-separated NAICS codes for multiple search
  • has_attachments – Filter by attachment availability: true or false

Response

{
  "data": [
    {
      "notice_id": "abc123def456",
      "title": "IT Services Contract",
      "agency": "Department of Defense",
      "posted_date": "2025-11-01",
      "response_deadline": "2025-12-15",
      "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/...",
      "performance_city_name": "Washington",
      "performance_state_code": "DC",
      "_additional": "59 fields total per record"
    }
  ],
  "pagination": {
    "limit": 20,
    "offset": 0,
    "total": 98,
    "has_next": true
  },
  "filters_applied": {
    "naics": "541330",
    "state": "CA",
    "agency": null,
    "keywords": null
  }
}

Examples

Basic search (all plans):

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 98 results

Paid plans - Agency filter:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://govconapi.com/api/v1/opportunities/search?agency=defense&limit=20"

Department of Defense returns 149,706 results

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.

GET /demo/opportunities No Auth Required

Public demo endpoint that returns 2 sample contract notices. Perfect for testing integration without an API key.

Performance: Responses are cached for 6 hours since data updates daily at 2:00 AM UTC.

Response Format

{ "data": [ { "notice_id": "abc123...", "title": "Example Contract", "_additional": "59 fields total per record" } ], "total": 2, "message": "Demo data - sign up for full API access" }

Example

curl "https://govconapi.com/demo/opportunities"

GET /api/agency-crosswalk No Auth Required

Free agency name standardization utility. Returns mappings between agency variants and canonical names, with department hierarchy and USASpending codes.

Data: Generated fresh from current contract database. Includes 2,000+ agency mappings with confidence scores.

Response Format

{ "data": [ { "raw_agency_text": "Dept. of Defense", "canonical_agency": "Department of Defense", "canonical_acronym": "DOD", "department": "Department of Defense", "department_acronym": "9700", "fh_level": "sub_tier", "usaspending_toptier_code": "097", "confidence": 0.95, "source": "federal_register", "frequency": 1250, "first_seen": "2025-08-07", "last_seen": "2025-11-21" } ], "count": 2000, "message": "Free agency name standardization utility - updated daily" }

Example

curl "https://govconapi.com/api/agency-crosswalk"

GET /api/v1/opportunities/{notice_id} Requires Auth

Get complete details for a specific contract notice by its ID.

Response Format

{ "opportunity": { "notice_id": "abc123def456", "title": "IT Services Contract", "agency": "Department of Defense", "_additional": "all 59 opportunity fields returned" }, "has_raw_data": true, "last_updated": "2025-11-22T02:06:35.259767+00:00" }

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/abc123def456"

GET /api/v1/opportunities/{notice_id}/attachments Requires Auth

Get direct download URLs for all attachments associated with a specific contract opportunity. Returns SAM.gov file links ready to download.

Response

{ "notice_id": "abc123def456", "title": "IT Services Contract", "attachment_count": 3, "attachments": [ "https://sam.gov/api/prod/opps/v3/opportunities/resources/files/...", "https://sam.gov/api/prod/opps/v3/opportunities/resources/files/...", "https://sam.gov/api/prod/opps/v3/opportunities/resources/files/..." ] }

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/abc123def456/attachments"

Note: Attachment URLs are direct SAM.gov download links. If an opportunity has no attachments, attachments will be an empty array. Attachments are also available in the main opportunity response via the resource_links_array field.

GET /api/v1/stats Requires Auth

Get comprehensive database statistics including total records, recent activity, unique agencies and NAICS codes.

Response

{ "database_stats": { "total_opportunities": 199016, "recent_opportunities": 7874, "unique_agencies": 2224, "unique_naics_codes": 1095, "total_exclusions": 163314, "active_exclusions": 163306, "last_updated": "2026-04-04T02:01:11.019038+00:00" }, "data_freshness": { "last_collection": "2026-04-04T02:01:11.019038+00:00", "collection_frequency": "Daily at 2:00 AM UTC", "retention_period": "730 days" } }

Note: All values shown are examples. Use the endpoint to get current statistics.

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/stats"

GET /api/stats No Auth Required

Public statistics endpoint for landing page. Returns real-time counts computed from database.

Response

{ "total_notices": 199016, "currently_open": 14701, "award_records": 47103, "full_descriptions": 167770, "with_attachments": 63327, "total_exclusions": 163314, "coverage": { "descriptions": "84.3%", "attachments": "31.8%" } }

Note: All values shown are examples. Use the endpoint to get current statistics.

Field Descriptions

  • total_notices - Total contract notices in database (all types, never archived)
  • currently_open - Notices still accepting bids (deadline > now, not yet awarded)
  • award_records - Notices with award information (winner, amount, date)
  • full_descriptions - Notices with complete extracted requirement text from SAM.gov

Example

curl "https://govconapi.com/api/stats"

GET /meta No Auth Required

Get metadata about the dataset including last collection time and total record count.

Response

{ "last_delta_utc": "2025-11-22T02:06:52.095708+00:00", "records": 62550, "description": "Federal contract opportunities from SAM.gov" }

Example

curl "https://govconapi.com/meta"

Note: Use this endpoint to check data freshness. Collection runs daily at 2:00 AM UTC. The records value shown is an example - use the endpoint to get the current count.

GET /api/v1/me Requires Auth

Get information about your API key including plan type and email address.

Response

{ "plan": "developer", "email": "[email protected]", "docs_url": "https://govconapi.com/api-guide" }

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/me"

GET /api/v1/awards/{award_number} Requires Auth

Get a single award record by contract/award number. Returns the same 16 fields as the search endpoint. If multiple records share the same award number, returns the most recent.

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/awards/SPE4A626PL503"

Note: Returns 404 if the award number is not found.

GET /api/v1/entities/{uei} Requires Auth

Single SAM-registered entity by UEI. Returns the registration block (status, expiration, dates) and the entity block (legal name, address, NAICS, PSC, business-type certifications, CAGE codes). Available on Developer tier; Pro Bundle subscribers can use /api/v1/companies/{uei} for the same data merged with award history.

Parameters

  • uei (path) - 12-character SAM Unique Entity Identifier. Normalized: lowercase, mixed case, and surrounding whitespace are accepted.

Response

{ "uei": "XX2WFHJEFB45", "registration": { "status": "A", "active": true, "registration_date": "2001-08-16", "activation_date": "2026-01-21", "expiration_date": "2027-01-19", "expiring_soon": false, "source_extract_date": "2026-04-05" }, "entity": { "legal_business_name": "KAMPI COMPONENTS CO INC", "dba_name": null, "entity_structure_code": "2L", "entity_url": "www.kampi.com", "physical_address": { "street1": "...", "city": "FAIRLESS HILLS", "state": "PA", "zip": "19030", "country": "USA" }, "primary_naics": "423990", "naics_codes": ["423990Y", "332710Y", "..."], "psc_codes": ["...", "..."], "business_types": ["2X", "XS"], "business_types_labels": [ "For Profit Organization", "Subchapter S Corporation" ], "cage_codes": ["7Z016"] } }

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/entities/XX2WFHJEFB45"

Errors: 401 (no auth), 404 (UEI not in current SAM data), 429 (rate limit).

Use cases: compliance checks before contract signing (registration.active + registration.expiration_date), CRM enrichment, vendor-onboarding form auto-fill, set-aside qualification preview (entity.business_types_labels).

GET /api/v1/entities/by-cage/{cage_code} Requires Auth

Same record as /api/v1/entities/{uei}, keyed by 5-character CAGE code instead of UEI. Adds a queried_cage field at the top of the response so callers can confirm the match. Useful for legacy DoD systems that key on CAGE.

Parameters

  • cage_code (path) - 5-character DLA-issued CAGE code. Normalized: lowercase and surrounding whitespace are accepted and converted to uppercase.

Response

{ "queried_cage": "53YC5", "uei": "C111ATT311C8", "registration": { "status": "A", "active": true, "...": "..." }, "entity": { "legal_business_name": "K & K CONSTRUCTION SUPPLY INC", "...": "..." } }

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/entities/by-cage/53YC5"

Errors: 401, 404 (CAGE not found), 429.

Note: Most entities have one CAGE code. Some large organizations (USPS, large banks) have many - up to 80. The entity.cage_codes array in the response lists all CAGE codes registered to the resolved UEI.

GET /api/v1/entities/expiring Requires Auth Pro Bundle

Active SAM registrations expiring within within_days days. Sorted by urgency (most urgent first). Past-due registrations are excluded (their status is 'E'). The renewal-radar use case: compliance dashboards, renewal-reminder SaaS, BD pipelines flagging at-risk leads.

Parameters

  • within_days - 1-365, default 60. Days from today to look ahead.
  • state - 2-character US state code (optional).
  • naics - 6-digit NAICS code, no suffix (optional). Matches both primary_naics and naics_codes[].
  • limit - 1-500, default 50.
  • offset - Pagination offset (default 0).

Response

{ "query": {"within_days": 30, "state": "DC", "naics": null}, "pagination": {"limit": 50, "offset": 0, "total": 391, "has_next": true}, "results": [ { "uei": "...", "legal_business_name": "ACME CONSULTING LLC", "registration_status": "A", "registration_expiration_date": "2026-05-12", "days_until_expiration": 12, "primary_naics": "541611", "business_types": ["27", "2X", "8W"], "business_types_labels": [ "Self Certified Small Disadvantaged Business", "For Profit Organization", "Women-Owned Small Business (WOSB)" ], "physical_city": "WASHINGTON", "physical_state": "DC", "physical_country": "USA" } ] }

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/entities/expiring?within_days=30&state=DC&limit=20"

Errors: 401, 402 (not on Pro Bundle), 422 (within_days out of 1-365), 429.

Note: days_until_expiration is computed at request time. Cached responses can drift. Sort is fixed at urgency-ascending.

GET /api/v1/companies/{uei} Requires Auth Pro Bundle

Combined SAM registration + award profile for one UEI. Returns 200 if either the SAM entity registry or our award data has the UEI; returns 404 only when both miss. Award fields populate when the UEI has at least one Award Notice; otherwise they zero out and the registration / entity blocks still populate.

Response

{ "uei": "XX2WFHJEFB45", "name": "KAMPI COMPONENTS CO INC", "primary_city": "FAIRLESS HILLS", "primary_state": "PA", "total_awards": 842, "total_value": 223500000.0, "avg_value": 265440.0, "first_award_date": "2024-11-04", "last_award_date": "2026-04-18", "awards_last_90d": 47, "awards_last_12mo": 312, "top_naics": [ {"naics": "332710", "awards": 198, "value": 52400000.0} ], "top_agencies": [ {"agency": "DEPT OF DEFENSE", "awards": 601, "value": 168200000.0} ], "registration": { "status": "A", "active": true, "registration_date": "2001-08-16", "activation_date": "2026-01-21", "expiration_date": "2027-01-19", "expiring_soon": false, "source_extract_date": "2026-04-05" }, "entity": { "legal_business_name": "KAMPI COMPONENTS CO INC", "dba_name": null, "entity_structure_code": "2L", "entity_url": "www.kampi.com", "physical_address": { "street1": "...", "city": "FAIRLESS HILLS", "state": "PA", "zip": "19030", "country": "USA" }, "primary_naics": "423990", "naics_codes": ["423990Y", "332710Y", "..."], "psc_codes": ["...", "..."], "business_types": ["2X", "XS"], "business_types_labels": [ "For Profit Organization", "Subchapter S Corporation" ], "cage_codes": ["7Z016"] } }

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/companies/XX2WFHJEFB45"

Use cases: compliance check (registration.active + registration.expiration_date), due diligence (customer concentration via top_agencies), pipeline health (awards_last_90d vs awards_last_12mo), capability mapping (top_naics, entity.business_types_labels). UEI normalization: lowercase, mixed case, and surrounding whitespace are accepted.

For SAM-data-only lookups without the award merge, see /api/v1/entities/{uei} (Developer tier).

GET /api/v1/companies/{uei}/awards Requires Auth Pro Bundle

Paginated award history for one UEI. Same data as /api/v1/awards/search?uei=X, served under a company-centric URL for convenience.

Parameters

  • limit - Results per page (1-1000, default 50)
  • offset - Pagination offset (default 0, no upper bound)
  • sort - date (default, desc) or amount (desc)

Response

{ "uei": "XX2WFHJEFB45", "pagination": {"limit": 50, "offset": 0, "total": 842, "has_next": true}, "sort": "date", "data": [ { "notice_id": "abc123...", "solicitation_number": "SPE7M525R0012", "title": "Replenishment parts for F-16 landing gear", "award_number": "SPE7M525C0042", "award_date": "2026-03-10", "award_amount": 487200.0, "agency": "DEPT OF DEFENSE", "naics": "336413", "psc": "1680", "posted_date": "2026-03-11", "response_deadline": null, "performance_state_code": "OK", "performance_city_name": "OKLAHOMA CITY" } ] }

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/companies/XX2WFHJEFB45/awards?limit=100&sort=amount"

GET /api/v1/companies/{uei}/peers Requires Auth Pro Bundle

Similar companies ranked by NAICS and agency overlap with the target. Use for competitive mapping, teaming-partner discovery, and BD target lists.

Parameters

  • limit - Results (1-50, default 10)

Response

{ "uei": "XX2WFHJEFB45", "target": {"naics_count": 12, "agency_count": 7}, "count": 10, "peers": [ { "uei": "H11HD5VHGHN3", "name": "LOCKHEED MARTIN CORPORATION", "total_awards": 16, "total_value": 26800000.0, "naics_overlap": 6, "agency_overlap": 1, "similarity_score": 19 } ] }

Ranking

similarity_score = naics_overlap * 3 + agency_overlap. NAICS is weighted 3x higher because same-industry is a direct competition signal; shared customers is a weaker signal. Results sorted by similarity_score desc, then total_awards desc.

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/companies/XX2WFHJEFB45/peers?limit=10"

GET /api/v1/vendor-risk/{uei} Requires Auth Pro Bundle

Returns every red-flag signal the public federal record contains about one vendor UEI, plus a mechanical triage verdict so compliance teams can sort without parsing the full tree. Seven signals, all derived from SAM data: exclusion status, address cluster, name-variant cluster, individual exclusions at the same address, coordinated-wave membership, last-award-to-exclusion gap, and dual-CAGE detection.

Parameters

  • uei (path) - 12-character SAM UEI. Case-insensitive; non-alphanumeric input returns 422.

Response

{ "response_version": "1", "uei": "N2EDPB1SMN55", "entity_name": "Bella Mia Donna LLC", "address": {"line1": "111 SW 1 ST", "city": "Pompano Beach", "state": "FL"}, "address_source": "exclusions", "computed_at": "2026-04-24T16:22:10Z", "triage": { "category": "high", "label": "High risk", "reasons": [ "entity is currently on the federal exclusion list: Ineligible (Proceedings Pending) by DLA (create_date 2025-11-21)", "debarred alongside 24 entities by the same agency on 2025-11-21 (coordinated enforcement wave)" ] }, "signals": { "exclusion_status": { "excluded": true, "exclusion_type": "Ineligible (Proceedings Pending)", "excluding_agency_code": "DLA", "create_date": "2025-11-21", "_more_keys": "see API reference for full shape" }, "address_cluster": { "total_at_address": 24, "excluded_at_address": 24, "address_type": "normal", "others": [{"uei": "...", "entity_name": "...", "create_date": "2025-11-21"}] }, "name_variant_cluster": { "match_count": 1, "excluded_match_count": 1, "variants": [] }, "individual_exclusions_at_address": { "count": 5, "individuals": [{"name": "...", "create_date": "2025-11-21"}] }, "wave_membership": { "in_coordinated_wave": true, "wave_date": "2025-11-21", "wave_size": 24, "wave_members": [{"uei": "...", "entity_name": "..."}] }, "timing_gap": { "last_sam_award_date": "2025-11-17", "exclusion_create_date": "2025-11-21", "days_between": 4 }, "dual_cage": { "has_dual_cage": false, "cage_codes": ["8SGL5"] } }, "disclaimers": ["All signals derive from the public federal record."] }

Triage categories

  • high - the entity itself is actively excluded.
  • elevated - not excluded, but ≥2 co-tenants at the same address are excluded, or a name-variant entity is excluded, or ≥1 individual exclusion shares the address.
  • needs_review - weaker signals only (e.g. multiple CAGE codes).
  • clean - no signals returned.
  • unknown - UEI not present in SAM exclusions or opportunities data at all.

Triage rules are deliberately simple and visible in triage.reasons so your compliance team can override by reading the signals themselves. Keys in the response are stable: values may be null, empty arrays, or false, but the shape never changes. Safe to parse with response_version branching if you need to stay compatible as we add signals.

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/vendor-risk/N2EDPB1SMN55"

Notes: Returns 404 if the UEI has no identity data in our SAM tables. For non-US addresses where the state field is empty (Singapore, Hong Kong, Ankara, etc.), clustering falls back to line1+city; the address_source field tells you where the address came from (exclusions, opportunities, or unknown). See /vendor-risk-api for signal definitions, the triage rules, and worked examples from real federal enforcement actions.

POST /api/v1/vendor-risk/bulk Requires Auth Pro Bundle

Bulk vendor screening. Upload up to 100 UEIs in one call and get back a risk report for each. Designed for quarterly vendor reviews where you want to screen your whole subcontractor list in one pass, not one-at-a-time.

Request body

{ "ueis": ["N2EDPB1SMN55", "C1BVCA2N9YB4", "SH2ER4W6LH13"] }
  • ueis - array of 1-100 UEIs. Duplicates deduplicated server-side. Max 100 per request (422 if exceeded).

Response

{ "requested_count": 3, "returned_count": 3, "results": [ { "uei": "N2EDPB1SMN55", "status": "ok", "report": {"_shape": "same as GET /api/v1/vendor-risk/{uei}"} }, { "uei": "C1BVCA2N9YB4", "status": "ok", "report": {"_shape": "same as GET /api/v1/vendor-risk/{uei}"} }, { "uei": "SH2ER4W6LH13", "status": "not_found", "report": null } ] }

UEIs not found in our data come back with status: "not_found" rather than failing the whole request; stale UEIs in vendor lists are normal. Each UEI counts against your hourly quota (a 100-UEI bulk = 100 quota hits). Expected latency: ~80ms per UEI on warm paths.

Example

curl -X POST -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ueis":["N2EDPB1SMN55","C1BVCA2N9YB4","SH2ER4W6LH13"]}' \ https://govconapi.com/api/v1/vendor-risk/bulk

Use cases: quarterly subcontractor review ("which of my 200 vendors now need attention?"), pre-contract screening, M&A target due diligence on a portfolio company's vendor list.

GET /api/v1/opportunities/delta Requires Auth

Get all opportunities that were added or updated since a given timestamp. Use this to keep your local database in sync without re-downloading the entire dataset.

How It Works

  • Pass a timestamp, get changes. Returns every record that was added, updated, or backfilled since the timestamp you provide. This includes newly posted notices, updated deadlines, and historical records that were filled in during data reconciliation.
  • Same 59 fields as the search endpoint. Each record is a complete opportunity, not a diff.
  • Data updates daily around 2:00 AM UTC. A typical day has 1,000 to 2,000 changed records. Calling this once a day keeps you fully in sync.
  • Save the server_time from the response and use it as since on your next sync. This guarantees no gaps.

Parameters

  • since (required) - ISO 8601 timestamp (e.g. 2026-04-05T06:00:00Z)
  • limit - Results per page (Free: max 50, Developer: max 1000, default 1000)
  • offset - Pagination offset

Response

{ "data": [ { "notice_id": "abc123", "title": "IT Support Services", "notice_type": "Solicitation", "agency": "DEPT OF VETERANS AFFAIRS", "_additional": "all 59 fields, same as search endpoint" } ], "pagination": { "limit": 1000, "offset": 0, "total": 1547, "has_next": true }, "sync": { "since": "2026-04-05T06:00:00Z", "records_changed": 1547, "server_time": "2026-04-06T06:30:12.345678+00:00" } }

Sync Workflow

# 1. Initial load: pull everything with the search endpoint # (paginate with limit=1000 and offset) # 2. Save the server_time from your last response # 3. Next day: call delta with that timestamp curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/delta?since=2026-04-05T06:00:00Z" # 4. If has_next is true, paginate with offset curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/delta?since=2026-04-05T06:00:00Z&offset=1000" # 5. Save the new server_time, repeat tomorrow
Tip: On a typical day, the delta returns 1,000 to 2,000 records. That's 1 to 2 API calls at limit=1000, compared to 200+ calls to re-download the full database. If you're currently syncing by paginating through all records, switching to delta will reduce your API usage by ~99%.

GET /api/v1/exclusions/{uei_sam} Requires Auth

Get detailed exclusion record by UEI SAM identifier. Returns full 39-field record including dates, location, and exclusion details.

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/exclusions/N2EDPB1SMN55"

Note: Returns 404 if UEI not found. Most individuals don't have UEI identifiers, so name search via /api/v1/exclusions/search?entity_name=... is the better default lookup for people.

GET /health No Auth Required

Health check endpoint showing system status and database connection.

Response

{ "status": "healthy", "database": "connected", "last_sync": "2025-11-07T06:15:23Z" }

Example

curl "https://govconapi.com/health"

GET /export.csv Requires Auth Developer

Export contract notices as CSV file. Free plan does not have CSV export access.

Query Parameters

Supports the same filters as /opportunities/search, plus:

  • limit - Max records to export (default: 100, max: 1000)

Plan Restrictions:

  • Developer: All filters available. 15 exports/day.
  • Free: No CSV export access.

CSV Format

CSV exports a 15-column analyst-friendly subset (the columns below in this exact order). Empty values show as blank cells. If you need fields not in this set, pull them from /api/v1/opportunities/search instead, which returns all 59 fields per record.

notice_id,title,notice_type,agency,naics,psc, posted_date,response_deadline,solicitation_number, set_aside_type,award_amount,awardee_name, performance_state_code,contact_email,sam_url

Examples

Export high-value awards:

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/export.csv?notice_type=Award%20Notice&agency=defense&value_min=1000000&limit=1000" \ -o defense_awards.csv

Export by NAICS and state:

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/export.csv?naics=541330&state=CA&limit=500" \ -o ca_it_contracts.csv

Free plan - Not available:

{"detail":"CSV export requires a paid plan. Upgrade at https://govconapi.com/#pricing"}

POST /api/v1/keys No Auth Required

Create a new API key. The key will be emailed to you instantly. Generating a new key deactivates the previous one for that email.

Query Parameters

  • email - Your email address (required)
  • plan - Plan type: "free" (default) or "developer"

Example

curl -X POST \ "https://govconapi.com/api/v1/keys?email=YOUR_EMAIL&plan=free"

Note: For the Developer plan, use the /api/v1/checkout endpoint to create a Stripe checkout session.

POST /api/v1/checkout No Auth Required

Create a Stripe checkout session for Developer plan subscription. Redirects to Stripe payment page.

Query Parameters

  • email - Your email address (required)

Response

{ "status": "success", "checkout_url": "https://checkout.stripe.com/c/pay/cs_...", "plan": "Developer Plan - $19/month", "session_id": "cs_..." }

Example

curl -X POST \ "https://govconapi.com/api/v1/checkout?email=YOUR_EMAIL"

Performance & Reliability

Response Times (US West Coast)

  • Search Queries: ~1.1s (measured average)
  • Single Record: ~800ms
  • Public Endpoints: ~600ms (cached)
  • CSV Export: 1-2s (small datasets)

Technical Details

  • Database: PostgreSQL 16 with connection pooling
  • Platform: Railway (managed hosting)
  • Concurrent Requests: 5-15 recommended for best performance
  • Data Updates: Daily at 2 AM UTC (99.9% uptime)

Best Practices for High-Volume Usage

Expect geographic variation in response times:

curl -w "Total: %{time_total}s (Network: ~%{time_connect}s + Server: ~%{time_starttransfer}s)\n" \ -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/search?naics=541330&limit=100"

Use pagination within a filtered query:

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/search?naics=541330&limit=100&offset=0"

For full-database sync, use /api/v1/opportunities/delta instead. It's keyset-based and scales to any volume.

Monitor your usage to stay within rate limits:

Use specific filters to reduce server processing time:

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/search?naics=541330&posted_after=2025-11-01"

Note: NAICS filtering is well-optimized and faster than generic searches

Error Handling Best Practices

import requests import time def make_api_request(url, headers): response = requests.get(url, headers=headers) if response.status_code == 429: # Rate limit exceeded retry_after = int(response.headers.get('Retry-After', 3600)) print(f"Rate limited. Waiting {retry_after} seconds...") time.sleep(retry_after) return make_api_request(url, headers) # Retry elif response.status_code == 403: # Plan upgrade needed error = response.json() print(f"Upgrade required: {error['detail']}") return None return response.json()
Technical Architecture (Click to expand)

Rate Limiting

  • Free: 25 requests/day
  • Developer: 1,000 requests/hour
  • Plan Restrictions: Advanced filters and CSV export enforced by plan level

Database Performance

  • Database: PostgreSQL 16 on Railway (managed)
  • Connection Pool: 2-20 connections via psycopg2 ThreadedConnectionPool
  • Query Optimization: Indexed on common search fields (agency, naics, dates)
  • Performance Notes: NAICS filtering is well-optimized and 2x faster than generic searches
  • Data Size: Loading... opportunities updated daily

Infrastructure

  • Platform: Railway (Docker-based deployment)
  • Server: FastAPI with uvicorn ASGI server
  • Monitoring: Built-in health checks and error logging
  • SSL/TLS: Automatic HTTPS with Railway managed certificates

Data Pipeline

  • Source: SAM.gov API (daily collection at 2:00 AM UTC)
  • Processing: Dual storage (raw JSON + normalized fields)
  • Deduplication: SHA256 hash comparison to prevent duplicates
  • Historical Tracking: Full version history for opportunities

Production Usage Guide

Python client with pagination & error handling (Click to expand)
# Production-ready integration pattern import requests import time from datetime import datetime, timedelta class GovConAPIClient: def __init__(self, api_key, plan='developer'): self.api_key = api_key self.base_url = 'https://govconapi.com/api/v1' self.headers = {'Authorization': f'Bearer {api_key}'} # Configure based on your plan if plan == 'developer': self.rate_limit = 1000 # hourly self.burst_limit = 60 # per minute else: self.rate_limit = 25 # daily (free) self.burst_limit = 10 # per minute def search_opportunities(self, filters=None, limit=100): """Search with automatic pagination and rate limiting. Pass at least one meaningful filter; /search rejects unfiltered requests with 400 for keys created after the filter-required cutoff.""" all_results = [] offset = 0 while True: params = {'limit': limit, 'offset': offset} if filters: params.update(filters) response = self._make_request('/opportunities/search', params) if not response or 'data' not in response: break all_results.extend(response['data']) # Use the explicit pagination.has_next signal returned by the API. if not response.get('pagination', {}).get('has_next'): break offset += limit time.sleep(0.1) # gentle on the rate limit return all_results def _make_request(self, endpoint, params=None): """Make request with proper error handling""" url = f"{self.base_url}{endpoint}" try: response = requests.get(url, headers=self.headers, params=params) if response.status_code == 429: retry_after = int(response.headers.get('Retry-After', 3600)) print(f"Rate limited. Waiting {retry_after} seconds...") time.sleep(retry_after) return self._make_request(endpoint, params) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Request failed: {e}") return None # Example usage client = GovConAPIClient('your_api_key_here', 'developer') # Search for DoD opportunities in the last 30 days filters = { 'agency': 'Department of Defense', 'posted_after': (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d') } opportunities = client.search_opportunities(filters) print(f"Found {len(opportunities)} opportunities")

Load Testing Recommendations

Enterprise Integration Patterns (Click to expand)

Pattern 1: Daily Sync with the delta endpoint

Use /api/v1/opportunities/delta, not /search, for keeping a local copy in sync. Delta returns every record changed since your since timestamp (including backfilled records that posted_after would miss) and pages cleanly with has_next.

def daily_sync(client, last_sync_time):
    """Pull every opportunity that's been added or updated since last_sync_time."""
    offset = 0
    new_server_time = None
    while True:
        resp = client._make_request('/opportunities/delta', {
            'since': last_sync_time,
            'limit': 1000,
            'offset': offset,
        })
        if not resp:
            break
        for op in resp['data']:
            store_opportunity(op)   # upsert by notice_id
        # Capture the server's clock from the FIRST page so we use a single
        # consistent cutoff across all paginated calls in this run.
        if new_server_time is None:
            new_server_time = resp['sync']['server_time']
        if not resp['pagination']['has_next']:
            break
        offset += 1000
    return new_server_time   # save this; pass as last_sync_time tomorrow

Pattern 2: Agency Monitoring

Watching N agencies? Make one delta call (or one search call), filter agencies client-side. Looping the API once per agency burns the rate limit and adds latency for no benefit.

def monitor_agencies(client, agencies_of_interest, last_sync_time):
    """Pull all changes since last sync, route by agency client-side."""
    of_interest = {a.lower() for a in agencies_of_interest}
    resp = client._make_request('/opportunities/delta', {
        'since': last_sync_time,
        'limit': 1000,
    })
    if not resp:
        return
    for op in resp['data']:
        agency = (op.get('agency') or '').lower()
        if any(target in agency for target in of_interest):
            if is_new_opportunity(op):
                send_notification(op)
    return resp['sync']['server_time']

Pattern 3: Bulk CSV Export for Analytics

For analytics warehouses, the CSV export endpoint is the lowest-overhead path. Stream the response straight to disk; it's not JSON.

def export_csv_to_file(api_key, filename, **filters):
    """Download /export.csv to a local file. Filters: naics, agency, state,
    set_aside, notice_type, solicitation_number, keywords, naics_multiple, limit."""
    response = requests.get(
        'https://govconapi.com/export.csv',
        headers={'Authorization': f'Bearer {api_key}'},
        params=filters,
        stream=True,
        timeout=120,
    )
    response.raise_for_status()
    with open(filename, 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)
    return filename

# Example: pull DLA award notices to a CSV
export_csv_to_file('your_api_key', 'dla_awards.csv',
                   notice_type='Award Notice', agency='defense', limit=1000)

The CSV is the 15-column analyst subset documented in the CSV Export endpoint section. For the full 59-field record set, use the JSON search endpoint instead.

Error Codes

Code Description Common Causes
400 Bad Request Invalid query parameters or malformed request
401 Unauthorized Missing or invalid API key
403 Forbidden Attempting to use advanced filters on Free plan
404 Not Found Resource (notice_id) doesn't exist
429 Too Many Requests Rate limit exceeded for your plan
500 Internal Server Error Server-side error (contact support)

Data Freshness & Transparency

Contract notices are collected daily at 2:00 AM UTC from SAM.gov using their official API. The collection includes:

Check the /meta endpoint for the exact timestamp of the last collection run.

Important: We Keep ALL Records

We intentionally never archive or delete records. This means:

  • Use response_deadline to determine if opportunities are still open for bidding
  • Use notice_type to understand lifecycle (Solicitation → Award Notice)
  • Don't use active field - it's always "Yes" (meaningless SAM.gov value)
  • Past-deadline opportunities remain queryable for competitive intelligence

Check /api/stats for current counts.

Tracking amendments: Same solicitation_number appears multiple times (original + amendments). Use notice_id for unique records, or group by solicitation_number to track changes.

Tips for Getting the Most Out of the API

  • Use limit=1000 instead of paginating at 100. If you need 5,000 records, that's 5 requests instead of 50. Saves API calls and time.
  • Use posted_after for daily syncs. Instead of scanning all 200K+ records, fetch only what's new since your last sync. posted_after=2026-04-03 returns just today's notices.
  • Know your notice_type values:
    • Combined Synopsis/Solicitation (71K) - most common, open for bids
    • Award Notice (43K) - contracts already awarded, has winner data
    • Solicitation (40K) - open for bids
    • Sources Sought (15K) - market research, not yet a solicitation
    • Presolicitation (13K) - upcoming, not yet open
    • Special Notice (9K) - informational only
  • Find who won a contract: Search by solicitation_number and filter notice_type=Award Notice. The award record has awardee_name, award_amount, and award_date.
  • Find open opportunities only: Use due_after=2026-04-04 (today's date) to get only notices with a future deadline. Combine with notice_type=Solicitation or notice_type=Combined%20Synopsis/Solicitation.
  • Cache responses locally. Data updates once daily at 2 AM UTC. If you're calling the same query multiple times per day, cache the first response.

Tested Integration Examples

1. Plan Verification Workflow

Step 1: Check your plan level

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/me"

Response: {"plan":"developer","email":"[email protected]"}

Step 2: Use features appropriate to your plan

Paid plans: All features available

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/search?agency=defense¬ice_type=Award%20Notice&value_min=1000000"

Free plan: Basic filters only

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/search?naics=541330&state=CA"

2. Error-Aware Search Implementation

Try advanced filter and handle graceful fallback:

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/search?agency=defense" 2>/dev/null

If 403 error (Free plan), fallback to basic search:

if [ $? -ne 0 ]; then echo "Free plan detected, using basic search..." curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/search?keywords=defense" fi

3. Pagination Best Practices

Efficient pagination within a filtered query:

For full-database sync or backfills use /opportunities/delta (keyset-based, no depth penalty). /search pagination is intended for paging through filtered result sets.

offset=0 limit=100 total_collected=0 filter="naics=541330" # required: at least one filter on /search while true; do response=$(curl -s -H "Authorization: Bearer YOUR_API_KEY" \ "https://govconapi.com/api/v1/opportunities/search?${filter}&limit=$limit&offset=$offset") has_next=$(echo "$response" | jq -r '.pagination.has_next') data_length=$(echo "$response" | jq '.data | length') total_collected=$((total_collected + data_length)) echo "Collected $total_collected records..." if [ "$has_next" != "true" ] || [ "$data_length" -eq 0 ]; then break fi offset=$((offset + limit)) sleep 0.1 done

Note: Be respectful of the API with small delays between requests

4. Working with Attachments

Option 1: Filter for opportunities with attachments (Developer plan):

import requests API_KEY = "your_api_key" headers = {"Authorization": f"Bearer {API_KEY}"} response = requests.get( "https://govconapi.com/api/v1/opportunities/search?has_attachments=true&limit=20", headers=headers ) results = response.json()

Option 2: Get attachments for specific opportunity:

import requests # From search results notice_id = results['data'][0]['notice_id'] # Get attachments via dedicated endpoint attachments = requests.get( f"https://govconapi.com/api/v1/opportunities/{notice_id}/attachments", headers=headers ).json() # Download files (SAM.gov URLs, no auth needed) for url in attachments['attachments']: file_response = requests.get(url) # Save file to disk with open(f"attachment_{attachments['attachments'].index(url)}.pdf", "wb") as f: f.write(file_response.content)

Option 3: Use existing field in search response:

import requests # Attachments already included in search response for opp in results['data']: if opp.get('resource_links_array'): print(f"Opportunity {opp['notice_id']} has {len(opp['resource_links_array'])} attachments") for url in opp['resource_links_array']: file_response = requests.get(url) # Process attachment directly

Which approach to use? Use resource_links_array from search results when processing multiple opportunities. Use the /attachments endpoint only if you need attachments for one specific opportunity without fetching the full record.

Feature Reference

Plan Restrictions Summary

Basic filters (NAICS, PSC, state, keywords, notice_type, solicitation_number) available on all plans. Advanced filters (agency, value_min/max, date range, has_attachments) require a paid plan. CSV export is paid-only.

Current Database Stats (Live)

Stats loaded live from /api/stats endpoint. Updates daily at 2:00 AM UTC.

Performance Benchmarks (Tested)

Federal Contracting Officers Directory

Need direct contact with government buyers? Our sister site contacts.govconapi.com provides verified contact information for federal contracting officers with phone numbers, emails, agency details, and recent procurement activity.

Need Help?

Questions or issues? Contact [email protected]

API Status: https://govconapi.com/health

Interactive Docs: https://govconapi.com/docs (auto-generated from OpenAPI schema)