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_datewhen present, otherwisefiled_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,agencysolicitation_number,case_type,outcomefiled_date,posted_date,decision_date,due_datedocket_url,decision_urldigest(opt-in only; fromraw_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,outcomefiled_date,posted_date,decision_date,due_datedocket_url,decision_urldigest
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 entityname— Display name of the matched entitymatch_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 organizationname— Display name of the matched organizationmatch_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
docketsexpansion 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
shapefields return HTTP 400 with structured validation errors.