Skip to content

Opportunities

Opportunities are SAM.gov opportunities/solicitations exposed at /api/opportunities/ (with a latest-notice pointer and history behind the scenes). For field definitions, see the Opportunities Data Dictionary.

Endpoints

  • GET /api/opportunities/ (list + filtering + search + ordering)
  • GET /api/opportunities/{opportunity_id}/ (detail)

Notice UUID Resolution

If you pass a notice UUID (instead of an opportunity UUID) to the detail endpoint, the API automatically redirects (HTTP 302) to the parent opportunity. This is useful when you have a subsequent notice UUID (e.g., from an amendment) and want to reach the parent opportunity directly.

# Using a notice UUID redirects to the parent opportunity
GET /api/opportunities/{notice_id}/    302 /api/opportunities/{opportunity_id}/

If the notice UUID has no parent opportunity, the endpoint returns 404.

Filtering

Swagger is the canonical list of query parameters. These are the core filters most people use day-to-day. All date filters require YYYY-MM-DD format; invalid dates or inverted ranges return 400. See Date filters.

Param What it does
search Full-text search over opportunities (vector-backed).
agency Filter by agency or department (vector-backed).
naics Filter by NAICS.
psc Filter by PSC.
set_aside Filter by set-aside.
notice_type Filter by notice type (valid values are validated).
solicitation_number Filter by solicitation number.
first_notice_date First notice date (exact, YYYY-MM-DD).
first_notice_date_after, first_notice_date_before First notice date range.
last_notice_date Last notice date (exact, YYYY-MM-DD).
last_notice_date_after, last_notice_date_before Last notice date range.
response_deadline Response deadline (exact, YYYY-MM-DD).
response_deadline_after, response_deadline_before Response deadline range.
place_of_performance Full-text-ish filter over place-of-performance text.
active Filter active/inactive (default true).

Ordering

Opportunities support ordering= with a strict allowlist:

  • first_notice_date (alias: posted_date)
  • last_notice_date
  • response_deadline

Examples:

  • Newest posted first: GET /api/opportunities/?ordering=-posted_date
  • Soonest deadline first: GET /api/opportunities/?ordering=response_deadline

Note: if you use search without an explicit ordering=..., results default to relevance ordering (rank).

Pagination

Opportunities use page-number pagination (page, limit) and return next/previous URLs.

Response Shaping

Opportunities support the ?shape= query parameter. When no ?shape= is provided, the endpoint returns a default shape.

Default shape (list): active,award_number,first_notice_date,last_notice_date,meta(*),naics_code,office(*),opportunity_id,place_of_performance(*),psc_code,response_deadline,sam_url,set_aside,solicitation_number,title

Default shape (detail): adds attachments(*), description, notice_history(*), primary_contact(*)

set_aside is available as a leaf (returns the code string) or as an expansion set_aside(code,description) for the full label.

See Response Shaping for syntax and examples.

Opportunities shaping notes

  • New leaves: latest_notice_id, archive_date, plus relation id leaves agency_id, department_id, office_id.
  • New expansions: agency(*), department(*), latest_notice(notice_id,link).
  • Bare expand shorthand: common expansions can be requested without parentheses when the runtime must expand them for safety (e.g., office behaves like office(*), and attachments behaves like attachments(*)).

Examples:

# Include latest notice pointer + org expansions
/api/opportunities/?shape=opportunity_id,title,latest_notice_id,latest_notice(*),agency(*),department(*),office&limit=1

# Attachments without specifying subfields (shorthand)
/api/opportunities/{opportunity_id}/?shape=opportunity_id,title,attachments

SDK examples

Search opportunities (with shaping)

import os

from tango import ShapeConfig, TangoClient

client = TangoClient(api_key=os.environ["TANGO_API_KEY"])
resp = client.list_opportunities(
    search="cybersecurity",
    agency="DOD",
    limit=10,
    shape=ShapeConfig.OPPORTUNITIES_MINIMAL,
)

for o in resp.results:
    print(o.opportunity_id, o.title)
import { ShapeConfig, TangoClient } from "@makegov/tango-node";

const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY });
const resp = await client.listOpportunities({
  search: "cybersecurity",
  agency: "DOD",
  limit: 10,
  shape: ShapeConfig.OPPORTUNITIES_MINIMAL,
});

for (const o of resp.results) {
  console.log(o.opportunity_id, o.title);
}