Skip to content

Protests

Protests are bid protest records exposed at /api/protests/. For field definitions, see the Protests Data Dictionary.

Authentication: Protests endpoints require authentication (API key or OAuth2). Unauthenticated requests receive HTTP 401.

Endpoints

  • GET /api/protests/ (list + filtering + search + pagination)
  • GET /api/protests/{case_id}/ (detail by deterministic case UUID)

Both list and detail return case-level objects identified by case_id (a deterministic UUID derived from source_system + base_case_number). Subordinate dockets are available via ?shape=...,dockets(...) expansion. The path segment must be a valid RFC 4122 UUID (version 1–5, variant 8/9/a/b).

Filtering

Core filters:

Param What it does
source_system Filter by source system (for example gao). Supports OR/AND syntax.
outcome Filter by protest outcome (for example Denied, Dismissed, Withdrawn, Sustained). Supports OR/AND syntax.
case_type Filter by case type (for example Bid Protest, Bid Protest: Cost). Supports OR/AND syntax.
agency Filter by protested agency text. Supports OR/AND syntax.
case_number Filter by case number (B-number, for example b-423274). Supports OR/AND syntax.
solicitation_number Filter by exact solicitation number.
protester Filter by protester name text. Supports OR/AND syntax.
filed_date_after, filed_date_before Filed date range filters.
decision_date_after, decision_date_before Decision date range filters.
search Full-text search over protest searchable fields.

Ordering

Protests do not support custom ordering via ordering=.

Results are returned in a fixed timeline order:

  • decision_date when present, otherwise filed_date (newest first)
  • UUID descending as a deterministic tie-breaker

If ordering is provided, the API returns HTTP 400.

Pagination

Protests use standard page-number pagination:

  • page (default 1)
  • limit (max 100)

List response: one result per case

The list endpoint returns one result per case (distinct base_case_number), not one per docket. Each result includes case-level fields from the most recent docket in the case. case_id is a deterministic UUID from (source_system, base_case_number) used for grouping and detail lookup. Pagination and filters apply to dockets first; results are then grouped, so count is the total number of matching cases across all pages.

The default (unshaped) response does not include nested dockets. To get dockets, use the dockets expansion via ?shape=...,dockets(...).

Detail response: case-level

The detail endpoint (GET /api/protests/{case_id}/) returns a single case-level object, the same structure as a list item. Use ?shape=...,dockets(...) to include nested dockets.

Shaping

Protests support shape on both list and detail endpoints:

  • GET /api/protests/?shape=...
  • GET /api/protests/{case_id}/?shape=...

Both list and detail return case-level objects. You can request case-level fields and the dockets expansion (e.g. shape=case_id,case_number,title,dockets(case_number,docket_number,filed_date)).

Allowed shape fields (case-level for both list and detail):

  • case_id (deterministic case UUID; use for detail lookup)
  • source_system, case_number (base B-number), title, protester, agency
  • solicitation_number, case_type, outcome
  • filed_date, posted_date, decision_date, due_date
  • docket_url, decision_url
  • digest (opt-in only; from raw_data.digest, e.g. decision summary text)
  • dockets (expand with docket fields, e.g. dockets(case_number,docket_number,filed_date))
  • resolved_protester (expand: entity resolution for the protester name)
  • resolved_agency (expand: organization resolution for the agency name)

Allowed shape fields inside dockets(...):

  • source_system, case_number (base B-number), docket_number (specific docket, e.g. b-424046.1)
  • title, protester, agency, solicitation_number, case_type, outcome
  • filed_date, posted_date, decision_date, due_date
  • docket_url, decision_url
  • digest

The case_number field (e.g. b-424214) identifies the case. The docket_number field (e.g. b-424214.1, b-424214.2) identifies the specific sub-docket within a case and is only available inside dockets(...).

Entity Resolution: resolved_protester(...)

When a protester name has been resolved to a canonical entity in Tango, you can expand the match via resolved_protester(...). Only high-confidence (confident) and medium-confidence (review) matches are returned; unresolved or low-confidence names return null.

Allowed fields inside resolved_protester(...):

  • uei — Unique Entity Identifier of the matched entity
  • name — Display name of the matched entity
  • match_confidence"confident" (auto-linkable) or "review" (needs human review)
  • rationale — Human-readable explanation of why the match was made

Organization Resolution: resolved_agency(...)

When an agency name has been resolved to a canonical organization in Tango, you can expand the match via resolved_agency(...). Same confidence rules as resolved_protester.

Allowed fields inside resolved_agency(...):

  • key — UUID key of the matched organization
  • name — Display name of the matched organization
  • match_confidence"confident" or "review"
  • rationale — Human-readable explanation

Not exposed in the API (internal only):

  • raw_data, field_provenance, external_id, data_quality, source_last_updated, created, modified
  • internal search/index fields (for example search_vector)

Examples:

# Case-level fields and nested dockets
GET /api/protests/?shape=case_id,case_number,title,dockets(case_number,docket_number,filed_date,outcome)

# Case-level only
GET /api/protests/?shape=case_id,source_system,case_number,title,outcome,filed_date

More examples:

# Timeline-focused list payload
GET /api/protests/?source_system=gao&shape=case_id,title,outcome,filed_date,decision_date

# Link-focused payload
GET /api/protests/?shape=case_id,docket_url,decision_url

# Detail by case UUID with shaped response
GET /api/protests/550e8400-e29b-41d4-a716-446655440000/?shape=case_id,source_system,title,agency,protester,filed_date,decision_date

# Detail with nested dockets
GET /api/protests/550e8400-e29b-41d4-a716-446655440000/?shape=case_id,title,dockets(docket_number,filed_date,outcome)

# Entity resolution for protester
GET /api/protests/?shape=case_id,protester,resolved_protester(*)

# Organization resolution for agency
GET /api/protests/?shape=case_id,agency,resolved_agency(*)

# Selective resolution fields
GET /api/protests/?shape=case_id,resolved_protester(uei,match_confidence)

Notes:

  • Use the dockets expansion to request nested docket fields (e.g. dockets(docket_number,case_number)).
  • * is only valid inside expansions. Use explicit field lists at the root.
  • Invalid shape fields return HTTP 400 with structured validation errors.