Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **`holder_count` and `order_winner_count` on vehicles** (Tango API 5.8.0). `holder_count` is the number of distinct companies holding one of the vehicle's IDVs, and `order_winner_count` is the number of distinct companies that have won a task order under it. Both are in the default `VEHICLES_MINIMAL` and `VEHICLES_COMPREHENSIVE` shapes and accepted in custom shapes.
- **`data_through_period` on budget accounts** (Tango API 5.8.0). The File A period (1-12) an account-year's figures run through; below 12 the year is partial, and `None` means there is no File A data. It is in the default `BUDGET_ACCOUNTS_MINIMAL` shape and accepted in custom shapes.
- **`list_budget_accounts(data_through_period=..., data_through_period_gte=..., data_through_period_lte=..., data_through_period_isnull=...)`** filters by that period, exactly, by range, or by whether it is set.

### Changed

- Re-vendored `contracts/filter_shape_contract.json` (Tango API 5.8.0) and regenerated `tango/shapes/generated_overlay.py`.

### Deprecated

- **`awardee_count` on vehicles.** It is the same value as `order_winner_count`; use that instead. The API removes it at its next major version, and the SDK's default vehicle shapes will drop it then.

## [1.12.0] - 2026-10-02

### Added
Expand Down
31 changes: 30 additions & 1 deletion contracts/filter_shape_contract.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"meta": {
"api_version": "5.7.0",
"api_version": "5.8.0",
"description": "Canonical API filter/shape contract. Downstream consumers (SDK, MCP) should validate their conformance against this manifest.",
"generated_from": "scripts/filter_shape_conformance.py",
"schema_version": 2
Expand Down Expand Up @@ -1672,6 +1672,10 @@
"contract_share_of_obligated_capped",
"contract_share_of_obligated_capped__gte",
"contract_share_of_obligated_capped__lte",
"data_through_period",
"data_through_period__gte",
"data_through_period__isnull",
"data_through_period__lte",
"enacted_ba",
"enacted_ba_5yr_cagr",
"enacted_ba_5yr_cagr__gte",
Expand Down Expand Up @@ -1933,6 +1937,25 @@
"lookup": "lte",
"type": "number"
},
"data_through_period": {
"filter_class": "NumberFilter",
"type": "number"
},
"data_through_period__gte": {
"filter_class": "NumberFilter",
"lookup": "gte",
"type": "number"
},
"data_through_period__isnull": {
"filter_class": "BooleanFilter",
"lookup": "isnull",
"type": "boolean"
},
"data_through_period__lte": {
"filter_class": "NumberFilter",
"lookup": "lte",
"type": "number"
},
"enacted_ba": {
"filter_class": "NumberFilter",
"type": "number"
Expand Down Expand Up @@ -2304,6 +2327,7 @@
"contract_share_of_obligated",
"contract_share_of_obligated_capped",
"created",
"data_through_period",
"enacted_ba",
"enacted_ba_5yr_cagr",
"enacted_ba_yoy_pct",
Expand Down Expand Up @@ -2392,6 +2416,7 @@
"contract_share_of_obligated",
"contract_share_of_obligated_capped",
"created",
"data_through_period",
"enacted_ba",
"enacted_ba_5yr_cagr",
"enacted_ba_yoy_pct",
Expand Down Expand Up @@ -16061,6 +16086,7 @@
"description",
"descriptions",
"fiscal_year",
"holder_count",
"idv_count",
"is_synthetic_solicitation",
"last_date_to_order",
Expand All @@ -16069,6 +16095,7 @@
"name",
"opportunity_id",
"order_count",
"order_winner_count",
"organization",
"organization_id",
"program_acronym",
Expand Down Expand Up @@ -16474,6 +16501,7 @@
"description",
"descriptions",
"fiscal_year",
"holder_count",
"idv_count",
"is_synthetic_solicitation",
"last_date_to_order",
Expand Down Expand Up @@ -16600,6 +16628,7 @@
"opportunity.title",
"opportunity_id",
"order_count",
"order_winner_count",
"organization",
"organization.agency_code",
"organization.agency_name",
Expand Down
18 changes: 18 additions & 0 deletions contracts/observed_shape_types.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@
"kind": "scalar",
"type": "int"
},
"data_through_period": {
"is_list": false,
"is_optional": true,
"kind": "scalar",
"type": "int"
},
"narratives": {
"is_list": true,
"is_optional": false,
Expand Down Expand Up @@ -12494,6 +12500,12 @@
"kind": "scalar",
"type": "int"
},
"holder_count": {
"is_list": false,
"is_optional": true,
"kind": "scalar",
"type": "int"
},
"idv_count": {
"is_list": false,
"is_optional": true,
Expand Down Expand Up @@ -12708,6 +12720,12 @@
"kind": "scalar",
"type": "int"
},
"order_winner_count": {
"is_list": false,
"is_optional": true,
"kind": "scalar",
"type": "int"
},
"organization": {
"is_list": false,
"is_optional": false,
Expand Down
16 changes: 13 additions & 3 deletions docs/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,10 @@ The post-cutover (May 2026) vehicle response includes these top-level fields, al
| `vehicle_type`, `who_can_use`, `type_of_idc`, `contract_type` | dict \| None | Returned as `{code, description}`. |
| `description` | str \| None | Common text across IDV descriptions. |
| `descriptions` | list[str] \| None | Distinct IDV descriptions. |
| `idv_count`, `awardee_count`, `order_count` | int \| None | Denormalized rollups. |
| `idv_count`, `order_count` | int \| None | Denormalized rollups. |
| `holder_count` | int \| None | Distinct companies holding one of the vehicle's IDVs. |
| `order_winner_count` | int \| None | Distinct companies that have won a task order under the vehicle. |
| `awardee_count` | int \| None | **Deprecated.** Same value as `order_winner_count`; use that instead. Removed at the next major API version. |
| `total_obligated`, `vehicle_obligations`, `vehicle_contracts_value` | Decimal \| None | Denormalized rollups. |
| `award_date`, `latest_award_date`, `last_date_to_order` | date \| None | |
| `solicitation_title`, `solicitation_description`, `solicitation_date`, `opportunity_id` | str / date / None | From SAM.gov via the linked Opportunity. |
Expand Down Expand Up @@ -1772,6 +1775,10 @@ accounts = client.list_budget_accounts(
subfunction_code=None,
account_category=None,
account_category_in=None,
data_through_period=None,
data_through_period_gte=None,
data_through_period_lte=None,
data_through_period_isnull=None,
search=None,
ordering=None,
)
Expand All @@ -1789,6 +1796,9 @@ accounts = client.list_budget_accounts(
- `subfunction_code` - Subfunction code (exact)
- `account_category` - Account category (exact): `budgetary` or `credit_financing` today; treat it as an open string
- `account_category_in` - Comma-separated account categories to match any of (e.g. `"budgetary,credit_financing"`)
- `data_through_period` - File A period (1-12) the account-year's figures run through (exact); below 12 the year is partial
- `data_through_period_gte` / `data_through_period_lte` - `data_through_period` range
- `data_through_period_isnull` - `True` for accounts with no File A data, `False` for accounts with it
- `search` - Full-text search over `account_title`, `agency_name`, `bureau_name`
- `ordering` - Sort field; prefix with `-` for descending. The default is the latest fiscal year first, then largest `enacted_ba` first, with accounts that have no `enacted_ba` last.

Expand Down Expand Up @@ -2544,7 +2554,7 @@ entity = client.get_entity("UEI_KEY", shape=ShapeConfig.ENTITIES_COMPREHENSIVE)
| `GRANTS_MINIMAL` | `list_grants` | grant_id, opportunity_number, title, status(*), agency_code |
| `IDVS_MINIMAL` | `list_idvs`, `list_vehicle_awardees` | key, piid, award_date, recipient(display_name,uei), description, total_contract_value, obligated, idv_type |
| `IDVS_COMPREHENSIVE` | `get_idv` | Full IDV with offices, place_of_performance, competition, transactions, etc. |
| `VEHICLES_MINIMAL` | `list_vehicles` | uuid, solicitation_identifier, is_synthetic_solicitation, program_acronym, organization_id, organization, vehicle_type, description, idv_count, awardee_count, order_count, total_obligated, vehicle_obligations, vehicle_contracts_value, latest_award_date, solicitation_title, solicitation_date |
| `VEHICLES_MINIMAL` | `list_vehicles` | uuid, solicitation_identifier, is_synthetic_solicitation, program_acronym, organization_id, organization, vehicle_type, description, idv_count, holder_count, order_winner_count, awardee_count, order_count, total_obligated, vehicle_obligations, vehicle_contracts_value, latest_award_date, solicitation_title, solicitation_date |
| `VEHICLES_COMPREHENSIVE` | `get_vehicle` | Full vehicle with competition_details, fiscal_year, set_aside, etc. |
| `VEHICLE_AWARDEES_MINIMAL` | `list_vehicle_awardees` | uuid, key, piid, award_date, title, order_count, idv_obligations, idv_contracts_value, recipient(display_name,uei) |
| `ORGANIZATIONS_MINIMAL` | `list_organizations` | key, fh_key, name, level, type, short_name |
Expand All @@ -2559,7 +2569,7 @@ entity = client.get_entity("UEI_KEY", shape=ShapeConfig.ENTITIES_COMPREHENSIVE)
| `FEDERAL_REGISTER_COMPREHENSIVE` | `get_federal_register_document` | The list fields plus dates, signing_date, start_page, end_page, volume, docket_ids, dockets, regulation_id_numbers, topics, correction_of, corrections, executive_order_number, presidential_document_number, proclamation_number, comment_url, regulations_dot_gov_url, raw_text_url, body_html_url (omits `full_text`) |
| `EBUY_REQUESTS_MINIMAL` | `list_ebuy_requests` | rfq_id, request_type, title, schedule, sin, status, buyer_name, buyer_agency, buyer_agency_code, reference_number, issue_date, close_date, attachment_count, link_count, last_seen |
| `EBUY_REQUESTS_COMPREHENSIVE` | `get_ebuy_request` | Every field, plus the `organization` and `attachments` expands |
| `BUDGET_ACCOUNTS_MINIMAL` | `list_budget_accounts`, `get_budget_account` | id, federal_account_symbol, fiscal_year, agency_code/name, bureau_name, account_title, bea_category, on_off_budget, subfunction_code, account_category, lifecycle (requested/enacted/apportioned/obligated/outlayed/unobligated), contract & assistance rollups, key ratios, next-year growth, source_anomalies |
| `BUDGET_ACCOUNTS_MINIMAL` | `list_budget_accounts`, `get_budget_account` | id, federal_account_symbol, fiscal_year, data_through_period, agency_code/name, bureau_name, account_title, bea_category, on_off_budget, subfunction_code, account_category, lifecycle (requested/enacted/apportioned/obligated/outlayed/unobligated), contract & assistance rollups, key ratios, next-year growth, source_anomalies |
| `VEHICLE_ORDERS_MINIMAL` | `list_vehicle_orders` | key, piid, award_date, recipient(display_name,uei), total_contract_value, obligated |
| `ITDASHBOARD_INVESTMENTS_MINIMAL` | `list_itdashboard_investments` | Minimal IT Dashboard investment fields |
| `ITDASHBOARD_INVESTMENTS_COMPREHENSIVE` | `get_itdashboard_investment` | Full investment fields: uii, agency_code, agency_name, bureau_code, bureau_name, investment_title, type_of_investment, part_of_it_portfolio, updated_time, url |
Expand Down
14 changes: 13 additions & 1 deletion tango/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4450,6 +4450,10 @@ def list_budget_accounts(
subfunction_code: str | None = None,
account_category: str | None = None,
account_category_in: str | None = None,
data_through_period: int | None = None,
data_through_period_gte: int | None = None,
data_through_period_lte: int | None = None,
data_through_period_isnull: bool | None = None,
# Range-numeric filters: each field also accepts ``__gte`` / ``__lte``.
requested_ba: float | None = None,
requested_ba_gte: float | None = None,
Expand Down Expand Up @@ -4561,6 +4565,8 @@ def list_budget_accounts(
categories may be added.
account_category_in: Comma-separated account categories to match
any of, e.g. ``"budgetary,credit_financing"``.
data_through_period: File A period (1-12) the account-year's figures run through (exact). Also ``data_through_period_gte`` / ``data_through_period_lte``; a value below 12 means a partial year.
data_through_period_isnull: ``True`` for accounts with no File A data, ``False`` for accounts with it.
requested_ba: President's-budget requested BA (exact). Also
``requested_ba_gte`` / ``requested_ba_lte`` for range queries.
enacted_ba: Enacted budget authority (exact / gte / lte).
Expand Down Expand Up @@ -4630,11 +4636,17 @@ def list_budget_accounts(
("subfunction_code", subfunction_code),
("account_category", account_category),
("account_category__in", account_category_in),
("data_through_period", data_through_period),
("data_through_period__gte", data_through_period_gte),
("data_through_period__lte", data_through_period_lte),
("data_through_period__isnull", data_through_period_isnull),
("search", search),
("ordering", ordering),
)
for key, val in scalar_filters:
if val is not None:
if isinstance(val, bool):
params[key] = "true" if val else "false"
elif val is not None:
params[key] = val
# Range-numeric filters: each field has exact / __gte / __lte forms.
range_filters: tuple[tuple[str, float | None, float | None, float | None], ...] = (
Expand Down
12 changes: 9 additions & 3 deletions tango/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,8 @@ class Vehicle:
program_acronym: str | None = None
description: str | None = None
idv_count: int | None = None
holder_count: int | None = None
order_winner_count: int | None = None
total_obligated: Decimal | None = None
latest_award_date: date | None = None
solicitation_title: str | None = None
Expand Down Expand Up @@ -847,6 +849,8 @@ class BudgetAccount:
id: int | None = None
federal_account_symbol: str | None = None
fiscal_year: int | None = None
# File A period (1-12) this account-year's figures run through; below 12 the year is partial, and `None` means no File A data.
data_through_period: int | None = None
agency_code: str | None = None
agency_name: str | None = None
bureau_name: str | None = None
Expand Down Expand Up @@ -1752,7 +1756,7 @@ class ShapeConfig:
VEHICLES_MINIMAL: Final = (
"uuid,solicitation_identifier,is_synthetic_solicitation,program_acronym,"
"organization_id,organization,vehicle_type,description,"
"idv_count,awardee_count,order_count,total_obligated,"
"idv_count,holder_count,order_winner_count,awardee_count,order_count,total_obligated,"
"vehicle_obligations,vehicle_contracts_value,latest_award_date,"
"solicitation_title,solicitation_date"
)
Expand All @@ -1764,7 +1768,8 @@ class ShapeConfig:
"solicitation_title,solicitation_description,solicitation_date,opportunity_id,"
"naics_code,psc_code,set_aside,"
"fiscal_year,award_date,latest_award_date,last_date_to_order,"
"description,idv_count,awardee_count,order_count,total_obligated,"
"description,idv_count,holder_count,order_winner_count,awardee_count,order_count,"
"total_obligated,"
"vehicle_obligations,vehicle_contracts_value,"
"type_of_idc,contract_type,metrics(*)"
)
Expand All @@ -1780,7 +1785,8 @@ class ShapeConfig:
# Default for list_budget_accounts() / get_budget_account()
# Mirrors the API's BUDGET_ACCOUNT_DEFAULT_SHAPE.
BUDGET_ACCOUNTS_MINIMAL: Final = (
"id,federal_account_symbol,fiscal_year,agency_code,agency_name,bureau_name,"
"id,federal_account_symbol,fiscal_year,data_through_period,agency_code,agency_name,"
"bureau_name,"
"account_title,bea_category,on_off_budget,subfunction_code,account_category,"
"requested_ba,enacted_ba,apportioned,obligated_total,outlayed_total,"
"unobligated_balance,contract_obligated,contract_share_of_obligated_capped,"
Expand Down
5 changes: 5 additions & 0 deletions tango/shapes/explicit_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,11 @@
),
# Denormalized rollups
"idv_count": FieldSchema(name="idv_count", type=int, is_optional=True, is_list=False),
"holder_count": FieldSchema(name="holder_count", type=int, is_optional=True, is_list=False),
"order_winner_count": FieldSchema(
name="order_winner_count", type=int, is_optional=True, is_list=False
),
# Deprecated: same value as `order_winner_count`; removed at the next major API version.
"awardee_count": FieldSchema(name="awardee_count", type=int, is_optional=True, is_list=False),
"order_count": FieldSchema(name="order_count", type=int, is_optional=True, is_list=False),
"total_obligated": FieldSchema(
Expand Down
3 changes: 3 additions & 0 deletions tango/shapes/generated_overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,9 @@
name="contract_share_of_obligated_capped", type=str, is_optional=True, is_list=False
),
"created": FieldSchema(name="created", type=datetime, is_optional=True, is_list=False),
"data_through_period": FieldSchema(
name="data_through_period", type=int, is_optional=True, is_list=False
),
"enacted_ba": FieldSchema(name="enacted_ba", type=str, is_optional=True, is_list=False),
"enacted_ba_5yr_cagr": FieldSchema(
name="enacted_ba_5yr_cagr", type=str, is_optional=True, is_list=False
Expand Down
Loading
Loading