Skip to content

Latest commit

 

History

History
1646 lines (1270 loc) · 42.3 KB

File metadata and controls

1646 lines (1270 loc) · 42.3 KB

API Reference

Complete reference for all Tango Python SDK methods and functionality.

Table of Contents

Client Initialization

TangoClient

Initialize the Tango API client.

from tango import TangoClient

# With API key
client = TangoClient(api_key="your-api-key")

# From environment variable (TANGO_API_KEY)
client = TangoClient()

# Custom base URL (for testing or different environments)
client = TangoClient(api_key="your-api-key", base_url="https://custom.api.url")

Parameters:

  • api_key (str, optional): Your Tango API key. If not provided, will load from TANGO_API_KEY environment variable.
  • base_url (str, optional): Base URL for the API. Defaults to https://tango.makegov.com.

Agencies

Government agencies that award contracts and manage programs.

list_agencies()

List all federal agencies.

agencies = client.list_agencies(page=1, limit=25)

Parameters:

  • page (int): Page number (default: 1)
  • limit (int): Results per page (default: 25, max: 100)

Returns: PaginatedResponse with agency dictionaries

Example:

agencies = client.list_agencies(limit=10)
print(f"Found {agencies.count} total agencies")

for agency in agencies.results:
    print(f"{agency['code']}: {agency['name']}")

get_agency()

Get a specific agency by code.

agency = client.get_agency(code="GSA")

Parameters:

  • code (str): Agency code (e.g., "GSA", "DOD", "HHS")

Returns: Dictionary with agency details

Example:

gsa = client.get_agency("GSA")
print(f"Name: {gsa['name']}")
print(f"Abbreviation: {gsa.get('abbreviation', 'N/A')}")
if gsa.get('department'):
    print(f"Department: {gsa['department']['name']}")

Agency Fields:

  • code - Agency code
  • name - Full agency name
  • abbreviation - Short name
  • department - Parent department (if applicable)

Offices

Federal agency offices.

list_offices()

List offices with optional search.

offices = client.list_offices(page=1, limit=25, search="acquisitions")

Parameters:

  • page (int): Page number (default: 1)
  • limit (int): Results per page (default: 25, max: 100)
  • search (str, optional): Search term

Returns: PaginatedResponse with office dictionaries

get_office()

Get a specific office by code.

office = client.get_office(code="4732XX")

Parameters:

  • code (str): Office code

Returns: Dictionary with office details


Organizations

Federal organizations (hierarchical agency structure).

list_organizations()

List organizations with filtering and shaping.

organizations = client.list_organizations(
    page=1,
    limit=25,
    shape=ShapeConfig.ORGANIZATIONS_MINIMAL,
    # Filter parameters
    cgac=None,
    include_inactive=None,
    level=None,
    parent=None,
    search=None,
    type=None,
)

Parameters:

  • page (int): Page number (default: 1)
  • limit (int): Results per page (default: 25, max: 100)
  • shape (str, optional): Response shape string
  • flat (bool): Flatten nested objects (default: False)
  • flat_lists (bool): Flatten arrays with indexed keys (default: False)

Filter Parameters:

  • cgac - Filter by CGAC code
  • include_inactive - Include inactive organizations
  • level - Filter by organization level
  • parent - Filter by parent organization
  • search - Search term
  • type - Filter by organization type

Returns: PaginatedResponse with organization dictionaries

get_organization()

Get a specific organization by fh_key.

org = client.get_organization(fh_key="ORG_KEY", shape=ShapeConfig.ORGANIZATIONS_MINIMAL)

Parameters:

  • fh_key (str): Organization key
  • shape (str, optional): Response shape string
  • flat (bool): Flatten nested objects (default: False)
  • flat_lists (bool): Flatten arrays with indexed keys (default: False)

Returns: Dictionary with organization details


Contracts

Federal contract awards and procurement data.

list_contracts()

Search and filter contracts with extensive options.

contracts = client.list_contracts(
    page=1,
    limit=25,
    shape=None,
    flat=False,
    flat_lists=False,
    # Filter parameters (all optional)
    # Text search
    keyword=None,  # Mapped to 'search' API param
    # Date filters
    award_date_gte=None,
    award_date_lte=None,
    pop_start_date_gte=None,
    pop_start_date_lte=None,
    pop_end_date_gte=None,
    pop_end_date_lte=None,
    expiring_gte=None,
    expiring_lte=None,
    # Party filters
    awarding_agency=None,
    funding_agency=None,
    recipient_name=None,  # Mapped to 'recipient' API param
    recipient_uei=None,  # Mapped to 'uei' API param
    # Classification
    naics_code=None,  # Mapped to 'naics' API param
    psc_code=None,  # Mapped to 'psc' API param
    set_aside_type=None,  # Mapped to 'set_aside' API param
    # Type filters
    fiscal_year=None,
    fiscal_year_gte=None,
    fiscal_year_lte=None,
    award_type=None,
    # Identifiers
    piid=None,
    solicitation_identifier=None,
    # Sorting
    sort=None,  # Combined with 'order' into 'ordering' API param
    order=None,  # 'asc' or 'desc'
)

Common Parameters:

  • page (int): Page number
  • limit (int): Results per page (max: 100)
  • shape (str): Fields to return (see Shaping Guide)
  • flat (bool): Flatten nested objects to dot-notation keys
  • flat_lists (bool): Flatten arrays with indexed keys

Filter Parameters:

Text Search:

  • keyword - Search contract descriptions (automatically mapped to API's 'search' parameter)

Date Filters:

  • award_date_gte - Awarded on or after date (YYYY-MM-DD)
  • award_date_lte - Awarded on or before date (YYYY-MM-DD)
  • pop_start_date_gte - Period of performance start date ≥
  • pop_start_date_lte - Period of performance start date ≤
  • pop_end_date_gte - Period of performance end date ≥
  • pop_end_date_lte - Period of performance end date ≤
  • expiring_gte - Expiring on or after date
  • expiring_lte - Expiring on or before date

Party Filters:

  • awarding_agency - Agency code (e.g., "4700" for GSA)
  • funding_agency - Funding agency code
  • recipient_name - Vendor/recipient name (mapped to 'recipient' API param)
  • recipient_uei - Vendor UEI (mapped to 'uei' API param)

Classification:

  • naics_code - NAICS industry code (mapped to 'naics' API param)
  • psc_code - Product/Service code (mapped to 'psc' API param)
  • set_aside_type - Set-aside type (mapped to 'set_aside' API param)

Type Filters:

  • fiscal_year - Federal fiscal year (exact match)
  • fiscal_year_gte - Fiscal year ≥
  • fiscal_year_lte - Fiscal year ≤
  • award_type - Award type code

Identifiers:

  • piid - Procurement Instrument Identifier (exact match)
  • solicitation_identifier - Solicitation ID

Sorting:

  • sort - Field to sort by (e.g., "award_date", "obligated")
  • order - Sort order: "asc" or "desc" (default: "asc")

Returns: PaginatedResponse with contract dictionaries

Examples:

# Basic search
contracts = client.list_contracts(limit=10)

# Filter by agency
contracts = client.list_contracts(
    awarding_agency="4700",  # GSA agency code
    limit=50
)

# Text search
contracts = client.list_contracts(
    keyword="software development",
    limit=50
)

# Date range
contracts = client.list_contracts(
    award_date_gte="2023-01-01",
    award_date_lte="2023-12-31",
    limit=100
)

# Expiring contracts
contracts = client.list_contracts(
    expiring_gte="2025-01-01",
    expiring_lte="2025-12-31",
    limit=50
)

# Multiple filters
contracts = client.list_contracts(
    keyword="IT services",
    awarding_agency="4700",  # GSA
    fiscal_year=2024,
    naics_code="541511",
    limit=100
)

# With shaping for performance
contracts = client.list_contracts(
    shape="key,piid,recipient(display_name),total_contract_value,award_date",
    awarding_agency="4700",
    fiscal_year=2024,
    limit=100
)

# Sorting results
contracts = client.list_contracts(
    sort="award_date",
    order="desc",
    limit=100
)

Common Contract Fields:

  • key - Unique identifier
  • piid - Procurement Instrument Identifier
  • description - Contract description
  • award_date - Date awarded
  • fiscal_year - Fiscal year
  • total_contract_value - Total value
  • total_obligated - Total obligated amount
  • recipient - Vendor information (nested)
  • awarding_agency - Awarding agency (nested)
  • funding_agency - Funding agency (nested)
  • naics - Industry classification (nested)
  • psc - Product/service code (nested)
  • place_of_performance - Location (nested)

OTAs

Other Transaction Agreements — non-FAR-based awards.

list_otas()

List OTAs with keyset pagination, filtering, and shaping.

otas = client.list_otas(
    limit=25,
    cursor=None,
    shape=ShapeConfig.OTAS_MINIMAL,
    # Filter parameters (all optional)
    award_date=None,
    award_date_gte=None,
    award_date_lte=None,
    awarding_agency=None,
    expiring_gte=None,
    expiring_lte=None,
    fiscal_year=None,
    fiscal_year_gte=None,
    fiscal_year_lte=None,
    funding_agency=None,
    ordering=None,
    piid=None,
    pop_end_date_gte=None,
    pop_end_date_lte=None,
    pop_start_date_gte=None,
    pop_start_date_lte=None,
    psc=None,
    recipient=None,
    search=None,
    uei=None,
)

Notes:

  • Uses keyset pagination (cursor + limit) rather than page numbers.
  • Filter parameters mirror those on list_contracts.

Returns: PaginatedResponse with OTA dictionaries

get_ota()

ota = client.get_ota("OTA_KEY", shape=ShapeConfig.OTAS_MINIMAL)

OTIDVs

Other Transaction IDVs — umbrella OT agreements that can have child awards.

list_otidvs()

List OTIDVs with keyset pagination, filtering, and shaping.

otidvs = client.list_otidvs(
    limit=25,
    cursor=None,
    shape=ShapeConfig.OTIDVS_MINIMAL,
    # Same filter parameters as list_otas()
)

Notes:

  • Uses keyset pagination (cursor + limit) rather than page numbers.
  • Filter parameters are identical to list_otas().

Returns: PaginatedResponse with OTIDV dictionaries

get_otidv()

otidv = client.get_otidv("OTIDV_KEY", shape=ShapeConfig.OTIDVS_MINIMAL)

Subawards

Subcontract and subaward data under prime awards.

list_subawards()

List subawards with filtering and shaping.

subawards = client.list_subawards(
    page=1,
    limit=25,
    shape=ShapeConfig.SUBAWARDS_MINIMAL,
    # Filter parameters (all optional)
    award_key=None,
    awarding_agency=None,
    fiscal_year=None,
    fiscal_year_gte=None,
    fiscal_year_lte=None,
    funding_agency=None,
    prime_uei=None,
    recipient=None,
    sub_uei=None,
)

Filter Parameters:

  • award_key - Filter by prime award key
  • awarding_agency - Filter by awarding agency code
  • fiscal_year - Exact fiscal year
  • fiscal_year_gte / fiscal_year_lte - Fiscal year range
  • funding_agency - Filter by funding agency code
  • prime_uei - Filter by prime awardee UEI
  • recipient - Search by subrecipient name
  • sub_uei - Filter by subrecipient UEI

Returns: PaginatedResponse with subaward dictionaries


Vehicles

Vehicles provide a solicitation-centric way to discover groups of related IDVs and (optionally) expand into the underlying awards via shaping.

list_vehicles()

List vehicles with optional vehicle-level full-text search.

vehicles = client.list_vehicles(
    page=1,
    limit=25,
    search="GSA schedule",
    shape=ShapeConfig.VEHICLES_MINIMAL,
    flat=False,
    flat_lists=False,
)

Parameters:

  • page (int): Page number (default: 1)
  • limit (int): Results per page (default: 25, max: 100)
  • search (str, optional): Vehicle-level search term
  • shape (str, optional): Shape string (defaults to ShapeConfig.VEHICLES_MINIMAL)
  • flat (bool): Flatten nested objects in shaped response
  • flat_lists (bool): Flatten arrays using indexed keys
  • joiner (str): Joiner used when flat=True (default: ".")

Returns: PaginatedResponse with vehicle dictionaries

get_vehicle()

Get a single vehicle by UUID.

vehicle = client.get_vehicle(
    uuid="00000000-0000-0000-0000-000000000001",
    shape=ShapeConfig.VEHICLES_COMPREHENSIVE,
)

Notes:

  • On the vehicle detail endpoint, search filters expanded awardees when your shape includes awardees(...) (it does not filter the vehicle itself).

list_vehicle_awardees()

List the IDV awardees for a vehicle.

awardees = client.list_vehicle_awardees(
    uuid="00000000-0000-0000-0000-000000000001",
    shape=ShapeConfig.VEHICLE_AWARDEES_MINIMAL,
)

IDVs

IDVs (indefinite delivery vehicles) are the parent “vehicle award” records that can have child awards/orders under them.

list_idvs()

idvs = client.list_idvs(
    limit=25,
    cursor=None,
    shape=ShapeConfig.IDVS_MINIMAL,
    awarding_agency="4700",
)

Notes:

  • This endpoint uses keyset pagination (cursor + limit) rather than page numbers.

get_idv()

idv = client.get_idv("SOME_IDV_KEY", shape=ShapeConfig.IDVS_COMPREHENSIVE)

list_idv_awards()

Lists child awards (contracts) under an IDV.

awards = client.list_idv_awards("SOME_IDV_KEY", limit=25)

list_idv_child_idvs()

Lists child IDVs under an IDV.

children = client.list_idv_child_idvs("SOME_IDV_KEY", limit=25)

list_idv_transactions()

tx = client.list_idv_transactions("SOME_IDV_KEY", limit=100)

Entities

Vendors, recipients, and organizations doing business with the government.

list_entities()

List and search for entities (vendors/recipients).

entities = client.list_entities(
    page=1,
    limit=25,
    shape=None,
    flat=False,
    flat_lists=False,
    # Filter parameters (all optional)
    search=None,
    cage_code=None,
    naics=None,
    name=None,
    psc=None,
    purpose_of_registration_code=None,
    socioeconomic=None,
    state=None,
    total_awards_obligated_gte=None,
    total_awards_obligated_lte=None,
    uei=None,
    zip_code=None,
)

Parameters:

  • page (int): Page number
  • limit (int): Results per page
  • shape (str): Fields to return
  • flat (bool): Flatten nested objects
  • flat_lists (bool): Flatten arrays with indexed keys

Filter Parameters:

  • search - Full-text search
  • cage_code - Filter by CAGE code
  • naics - Filter by NAICS code
  • name - Filter by entity name
  • psc - Filter by PSC code
  • purpose_of_registration_code - Filter by registration purpose
  • socioeconomic - Filter by socioeconomic status
  • state - Filter by state
  • total_awards_obligated_gte / total_awards_obligated_lte - Obligation amount range
  • uei - Filter by UEI
  • zip_code - Filter by ZIP code

Returns: PaginatedResponse with entity dictionaries

Example:

entities = client.list_entities(search="Booz Allen", limit=20)

for entity in entities.results:
    print(f"{entity['display_name']}")
    print(f"UEI: {entity.get('uei', 'N/A')}")
    if entity.get('business_types'):
        print(f"Types: {', '.join(entity['business_types'])}")

get_entity()

Get a specific entity by UEI or CAGE code.

entity = client.get_entity(key="ZQGGHJH74DW7", shape=None)

Parameters:

  • key (str): UEI or CAGE code
  • shape (str, optional): Fields to return

Returns: Dictionary with entity details

Example:

entity = client.get_entity("ZQGGHJH74DW7")
print(f"Name: {entity['legal_business_name']}")
print(f"UEI: {entity['uei']}")

if entity.get('physical_address'):
    addr = entity['physical_address']
    print(f"Location: {addr.get('city')}, {addr.get('state_code')}")

Common Entity Fields:

  • uei - Unique Entity Identifier
  • cage_code - CAGE code
  • legal_business_name - Official business name
  • display_name - Display name
  • dba_name - Doing Business As name
  • business_types - Array of business type codes
  • primary_naics - Primary NAICS code
  • physical_address - Physical address (nested)
  • mailing_address - Mailing address (nested)
  • email_address - Contact email
  • entity_url - Website

Forecasts

Contract forecast and planning information.

list_forecasts()

List contract forecasts.

forecasts = client.list_forecasts(
    page=1,
    limit=25,
    shape=None,
    flat=False,
    flat_lists=False,
    # Filter parameters (all optional)
    agency=None,
    award_date_after=None,
    award_date_before=None,
    fiscal_year=None,
    fiscal_year_gte=None,
    fiscal_year_lte=None,
    modified_after=None,
    modified_before=None,
    naics_code=None,
    naics_starts_with=None,
    search=None,
    source_system=None,
    status=None,
)

Parameters:

  • page (int): Page number
  • limit (int): Results per page
  • shape (str): Fields to return
  • flat (bool): Flatten nested objects
  • flat_lists (bool): Flatten arrays with indexed keys

Filter Parameters:

  • agency - Filter by agency code
  • award_date_after / award_date_before - Expected award date range
  • fiscal_year - Exact fiscal year
  • fiscal_year_gte / fiscal_year_lte - Fiscal year range
  • modified_after / modified_before - Last-modified date range
  • naics_code - NAICS code (exact match)
  • naics_starts_with - NAICS code prefix
  • search - Full-text search
  • source_system - Filter by source system
  • status - Filter by status

Returns: PaginatedResponse with forecast dictionaries

Example:

forecasts = client.list_forecasts(agency="GSA", fiscal_year=2025, limit=20)

for forecast in forecasts.results:
    print(f"{forecast['title']}")
    print(f"Anticipated: {forecast.get('anticipated_award_date', 'TBD')}")
    print(f"Fiscal Year: {forecast.get('fiscal_year', 'N/A')}")

Common Forecast Fields:

  • id - Forecast identifier
  • title - Forecast title
  • description - Description
  • anticipated_award_date - Expected award date
  • fiscal_year - Fiscal year
  • naics_code - Industry code
  • status - Current status

Opportunities

Active contract opportunities and solicitations.

list_opportunities()

List contract opportunities/solicitations.

opportunities = client.list_opportunities(
    page=1,
    limit=25,
    shape=None,
    flat=False,
    flat_lists=False,
    # Filter parameters (all optional)
    active=None,
    agency=None,
    first_notice_date_after=None,
    first_notice_date_before=None,
    last_notice_date_after=None,
    last_notice_date_before=None,
    naics=None,
    notice_type=None,
    place_of_performance=None,
    psc=None,
    response_deadline_after=None,
    response_deadline_before=None,
    search=None,
    set_aside=None,
    solicitation_number=None,
)

Parameters:

  • page (int): Page number
  • limit (int): Results per page
  • shape (str): Fields to return
  • flat (bool): Flatten nested objects
  • flat_lists (bool): Flatten arrays with indexed keys

Filter Parameters:

  • active - Filter by active status (bool)
  • agency - Filter by agency code
  • first_notice_date_after / first_notice_date_before - First notice date range
  • last_notice_date_after / last_notice_date_before - Last notice date range
  • naics - NAICS code
  • notice_type - Filter by notice type
  • place_of_performance - Filter by place of performance
  • psc - PSC code
  • response_deadline_after / response_deadline_before - Response deadline range
  • search - Full-text search
  • set_aside - Set-aside type
  • solicitation_number - Solicitation number (exact match)

Returns: PaginatedResponse with opportunity dictionaries

Example:

opportunities = client.list_opportunities(agency="DOD", active=True, limit=20)

for opp in opportunities.results:
    print(f"{opp['title']}")
    print(f"Solicitation: {opp.get('solicitation_number', 'N/A')}")
    print(f"Deadline: {opp.get('response_deadline', 'Not specified')}")
    print(f"Active: {opp.get('active', False)}")

Common Opportunity Fields:

  • opportunity_id - Unique identifier
  • title - Opportunity title
  • solicitation_number - Solicitation number
  • description - Description
  • response_deadline - Response deadline
  • active - Is currently active
  • naics_code - Industry code
  • psc_code - Product/service code

Notices

Contract award notices and modifications.

list_notices()

List contract notices.

notices = client.list_notices(
    page=1,
    limit=25,
    shape=None,
    flat=False,
    flat_lists=False,
    # Filter parameters (all optional)
    active=None,
    agency=None,
    naics=None,
    notice_type=None,
    posted_date_after=None,
    posted_date_before=None,
    psc=None,
    response_deadline_after=None,
    response_deadline_before=None,
    search=None,
    set_aside=None,
    solicitation_number=None,
)

Parameters:

  • page (int): Page number
  • limit (int): Results per page
  • shape (str): Fields to return
  • flat (bool): Flatten nested objects
  • flat_lists (bool): Flatten arrays with indexed keys

Filter Parameters:

  • active - Filter by active status (bool)
  • agency - Filter by agency code
  • naics - NAICS code
  • notice_type - Filter by notice type
  • posted_date_after / posted_date_before - Posted date range
  • psc - PSC code
  • response_deadline_after / response_deadline_before - Response deadline range
  • search - Full-text search
  • set_aside - Set-aside type
  • solicitation_number - Solicitation number (exact match)

Returns: PaginatedResponse with notice dictionaries

Example:

notices = client.list_notices(agency="GSA", notice_type="award", limit=20)

for notice in notices.results:
    print(f"{notice['title']}")
    print(f"Solicitation: {notice.get('solicitation_number', 'N/A')}")
    print(f"Posted: {notice.get('posted_date', 'N/A')}")

Common Notice Fields:

  • notice_id - Notice identifier
  • title - Notice title
  • solicitation_number - Solicitation number
  • description - Description
  • posted_date - Date posted
  • naics_code - Industry code

Grants

Federal grant opportunities and assistance listings.

list_grants()

List grant opportunities.

grants = client.list_grants(
    page=1,
    limit=25,
    shape=None,
    flat=False,
    flat_lists=False,
    # Filter parameters (all optional)
    agency=None,
    applicant_types=None,
    cfda_number=None,
    funding_categories=None,
    funding_instruments=None,
    opportunity_number=None,
    posted_date_after=None,
    posted_date_before=None,
    response_date_after=None,
    response_date_before=None,
    search=None,
    status=None,
)

Parameters:

  • page (int): Page number
  • limit (int): Results per page (max 100)
  • shape (str): Response shape string
  • flat (bool): Flatten nested objects in shaped response
  • flat_lists (bool): Flatten arrays using indexed keys

Filter Parameters:

  • agency - Filter by agency code
  • applicant_types - Filter by applicant type
  • cfda_number - Filter by CFDA number
  • funding_categories - Filter by funding category
  • funding_instruments - Filter by funding instrument
  • opportunity_number - Filter by opportunity number (exact match)
  • posted_date_after / posted_date_before - Posted date range
  • response_date_after / response_date_before - Response date range
  • search - Full-text search
  • status - Filter by status

Returns: PaginatedResponse with grant dictionaries

Example:

grants = client.list_grants(agency="HHS", status="forecasted", limit=20)

for grant in grants.results:
    print(f"{grant['title']}")
    print(f"Opportunity: {grant.get('opportunity_number', 'N/A')}")
    print(f"Status: {grant.get('status', {}).get('description', 'N/A')}")

Common Grant Fields:

  • grant_id - Grant identifier
  • opportunity_number - Opportunity number
  • title - Grant title
  • status - Status information (nested object with code and description)
  • agency_code - Agency code
  • description - Description
  • last_updated - Last updated timestamp
  • cfda_numbers - CFDA numbers (list of objects with number and title)
  • applicant_types - Applicant types (list of objects with code and description)
  • funding_categories - Funding categories (list of objects with code and description)
  • funding_instruments - Funding instruments (list of objects with code and description)
  • category - Category (object with code and description)
  • important_dates - Important dates (list)
  • attachments - Attachments (list of objects)

Example with Expanded Fields:

# Get grants with expanded status and CFDA numbers
grants = client.list_grants(
    shape="grant_id,title,opportunity_number,status(*),cfda_numbers(number,title)",
    limit=10
)

for grant in grants.results:
    print(f"Grant: {grant['title']}")
    if grant.get('status'):
        print(f"Status: {grant['status'].get('description')}")
    if grant.get('cfda_numbers'):
        for cfda in grant['cfda_numbers']:
            print(f"CFDA: {cfda.get('number')} - {cfda.get('title')}")

GSA eLibrary Contracts

GSA Schedule contracts from the GSA eLibrary.

list_gsa_elibrary_contracts()

List GSA eLibrary contracts with filtering and shaping.

contracts = client.list_gsa_elibrary_contracts(
    page=1,
    limit=25,
    shape=ShapeConfig.GSA_ELIBRARY_CONTRACTS_MINIMAL,
    # Filter parameters (all optional)
    contract_number=None,
    key=None,
    piid=None,
    schedule=None,
    search=None,
    sin=None,
    uei=None,
)

Filter Parameters:

  • contract_number - Filter by contract number
  • key - Filter by key
  • piid - Filter by PIID
  • schedule - Filter by GSA schedule
  • search - Full-text search
  • sin - Filter by SIN (Special Item Number)
  • uei - Filter by UEI

Returns: PaginatedResponse with GSA eLibrary contract dictionaries

get_gsa_elibrary_contract()

Get a single GSA eLibrary contract by UUID.

contract = client.get_gsa_elibrary_contract("UUID_HERE")

Protests

Bid protest records (GAO, COFC, etc.).

list_protests()

List bid protests with filtering and shaping.

protests = client.list_protests(
    page=1,
    limit=25,
    shape=ShapeConfig.PROTESTS_MINIMAL,
    # Filter parameters (all optional)
    source_system=None,
    outcome=None,
    case_type=None,
    agency=None,
    case_number=None,
    solicitation_number=None,
    protester=None,
    filed_date_after=None,
    filed_date_before=None,
    decision_date_after=None,
    decision_date_before=None,
    search=None,
)

Filter Parameters:

  • source_system - Filter by source system (e.g., "gao")
  • outcome - Filter by outcome (e.g., "Denied", "Dismissed", "Withdrawn", "Sustained")
  • case_type - Filter by case type
  • agency - Filter by protested agency
  • case_number - Filter by case number (e.g., "b-423274")
  • solicitation_number - Filter by solicitation number
  • protester - Search by protester name
  • filed_date_after / filed_date_before - Filed date range
  • decision_date_after / decision_date_before - Decision date range
  • search - Full-text search

Returns: PaginatedResponse with protest dictionaries

Example:

protests = client.list_protests(
    source_system="gao",
    outcome="Sustained",
    filed_date_after="2024-01-01",
    shape="case_id,case_number,title,outcome,filed_date,dockets(docket_number,outcome)",
    limit=25,
)

for protest in protests.results:
    print(f"{protest['case_number']}: {protest['title']}{protest['outcome']}")

get_protest()

Get a single protest by case_id (UUID).

protest = client.get_protest(
    "CASE_UUID",
    shape="case_id,case_number,title,source_system,outcome,filed_date,dockets(*)",
)

Notes:

  • Use shape=...,dockets(...) to include nested docket records.

Business Types

Business type classifications.

list_business_types()

List available business type codes.

business_types = client.list_business_types(page=1, limit=25)

Parameters:

  • page (int): Page number
  • limit (int): Results per page

Returns: PaginatedResponse with business type dictionaries

Example:

business_types = client.list_business_types(limit=50)

for biz_type in business_types.results:
    print(f"{biz_type['code']}: {biz_type['name']}")

Business Type Fields:

  • code - Business type code
  • name - Business type name
  • description - Description

NAICS

NAICS (North American Industry Classification System) codes.

list_naics()

List NAICS codes with optional filtering.

naics = client.list_naics(
    page=1,
    limit=25,
    # Filter parameters (all optional)
    employee_limit=None,
    employee_limit_gte=None,
    employee_limit_lte=None,
    revenue_limit=None,
    revenue_limit_gte=None,
    revenue_limit_lte=None,
    search=None,
)

Filter Parameters:

  • employee_limit - Exact employee size standard
  • employee_limit_gte / employee_limit_lte - Employee limit range
  • revenue_limit - Exact revenue size standard
  • revenue_limit_gte / revenue_limit_lte - Revenue limit range
  • search - Full-text search (code or description)

Returns: PaginatedResponse with NAICS dictionaries

Example:

naics = client.list_naics(search="software", limit=10)

for code in naics.results:
    print(f"{code['code']}: {code['title']}")

Webhooks

Webhook APIs let Large / Enterprise users manage subscription filters for outbound Tango webhooks.

list_webhook_event_types()

Discover supported event_type values and subject types.

info = client.list_webhook_event_types()
print(info.event_types[0].event_type)

list_webhook_subscriptions()

subs = client.list_webhook_subscriptions(page=1, page_size=25)

Notes:

  • This endpoint uses page + page_size (tier-capped) rather than limit.

create_webhook_subscription()

sub = client.create_webhook_subscription(
    "Track specific vendors",
    {
        "records": [
            {"event_type": "awards.new_award", "subject_type": "entity", "subject_ids": ["UEI123ABC"]},
            {"event_type": "awards.new_transaction", "subject_type": "entity", "subject_ids": ["UEI123ABC"]},
        ]
    },
)

Notes:

  • Prefer v2 fields: subject_type + subject_ids.
  • Legacy compatibility: resource_ids is accepted as an alias for subject_ids (don’t send both).
  • Catch-all: subject_ids: [] means “all subjects” for that record and is Enterprise-only. Large tier users must list specific IDs.

update_webhook_subscription()

sub = client.update_webhook_subscription("SUBSCRIPTION_UUID", subscription_name="Updated name")

delete_webhook_subscription()

client.delete_webhook_subscription("SUBSCRIPTION_UUID")

list_webhook_endpoints()

List your webhook endpoint(s).

endpoints = client.list_webhook_endpoints(page=1, limit=25)

get_webhook_endpoint()

endpoint = client.get_webhook_endpoint("ENDPOINT_UUID")

create_webhook_endpoint() / update_webhook_endpoint() / delete_webhook_endpoint()

In production, MakeGov provisions the initial endpoint for you. These are most useful for dev/self-service.

endpoint = client.create_webhook_endpoint("https://example.com/tango/webhooks")
endpoint = client.update_webhook_endpoint(endpoint.id, is_active=False)
client.delete_webhook_endpoint(endpoint.id)

test_webhook_delivery()

Send an immediate test webhook to your configured endpoint.

result = client.test_webhook_delivery()
print(result.success, result.status_code)

get_webhook_sample_payload()

Fetch Tango-shaped sample deliveries (and sample subscription request bodies).

sample = client.get_webhook_sample_payload(event_type="awards.new_award")
print(sample["event_type"])

Deliveries / redelivery

The API does not currently expose a public /api/webhooks/deliveries/ or redelivery endpoint. Use:

  • test_webhook_delivery() for connectivity checks
  • get_webhook_sample_payload() for building handlers + subscription payloads

Receiving webhooks (signature verification)

Every delivery includes an HMAC signature header:

  • X-Tango-Signature: sha256=<hex digest>

Compute the digest over the raw request body bytes using your shared secret.

import hashlib
import hmac


def verify_tango_webhook_signature(secret: str, raw_body: bytes, signature_header: str | None) -> bool:
    if not signature_header:
        return False
    sig = signature_header.strip()
    if sig.startswith("sha256="):
        sig = sig[len("sha256=") :]
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

Response Objects

PaginatedResponse

All list methods return a PaginatedResponse object with the following attributes:

response = client.list_contracts(limit=25)

# Attributes
response.count      # Total number of results
response.next       # URL to next page (or None)
response.previous   # URL to previous page (or None)
response.results    # List of result dictionaries

Example:

contracts = client.list_contracts(limit=25)

print(f"Total contracts: {contracts.count:,}")
print(f"Results on this page: {len(contracts.results)}")

# Iterate through results
for contract in contracts.results:
    print(contract['piid'])

# Check for more pages
if contracts.next:
    next_page = client.list_contracts(page=2, limit=25)

Pagination Example:

page = 1
all_results = []

while True:
    response = client.list_contracts(page=page, limit=100)
    all_results.extend(response.results)

    print(f"Page {page}: {len(response.results)} results")

    if not response.next:
        break

    page += 1

print(f"Total collected: {len(all_results)} results")

ShapeConfig (predefined shapes)

The SDK provides predefined shape strings as constants on ShapeConfig. Use them as the shape argument for list/get methods when you want a consistent, validated set of fields without building a custom shape string.

from tango import TangoClient, ShapeConfig

client = TangoClient()

# List methods default to the minimal shape when shape is omitted
contracts = client.list_contracts(limit=10)  # uses CONTRACTS_MINIMAL

# Or pass the constant explicitly
contracts = client.list_contracts(shape=ShapeConfig.CONTRACTS_MINIMAL, limit=10)
entity = client.get_entity("UEI_KEY", shape=ShapeConfig.ENTITIES_COMPREHENSIVE)

Available constants (by resource):

Constant Used by Description
CONTRACTS_MINIMAL list_contracts, search_contracts key, piid, award_date, recipient(display_name), description, total_contract_value
ENTITIES_MINIMAL list_entities uei, legal_business_name, cage_code, business_types
ENTITIES_COMPREHENSIVE get_entity Full entity profile (addresses, naics, psc, obligations, etc.)
FORECASTS_MINIMAL list_forecasts id, title, anticipated_award_date, fiscal_year, naics_code, status
OPPORTUNITIES_MINIMAL list_opportunities opportunity_id, title, solicitation_number, response_deadline, active
NOTICES_MINIMAL list_notices notice_id, title, solicitation_number, posted_date
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, organization_id, awardee_count, order_count, vehicle_obligations, vehicle_contracts_value, 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, list_organization_offices key, fh_key, name, level, type, short_name
OTAS_MINIMAL list_otas key, piid, award_date, recipient(display_name,uei), description, total_contract_value, obligated
OTIDVS_MINIMAL list_otidvs key, piid, award_date, recipient(display_name,uei), description, total_contract_value, obligated, idv_type
SUBAWARDS_MINIMAL list_subawards award_key, prime_recipient(uei,display_name), subaward_recipient(uei,display_name)
GSA_ELIBRARY_CONTRACTS_MINIMAL list_gsa_elibrary_contracts uuid, contract_number, schedule, recipient(display_name,uei), idv(key,award_date)
PROTESTS_MINIMAL list_protests case_id, case_number, title, source_system, outcome, filed_date

All predefined shapes are validated at SDK release time (see Developer Guide). For custom shapes, see the Shaping Guide.


Error Handling

The SDK provides specific exception types for different error scenarios.

Exception Types

from tango import (
    TangoAPIError,       # Base exception
    TangoAuthError,      # 401 - Authentication failed
    TangoNotFoundError,  # 404 - Resource not found
    TangoValidationError,  # 400 - Invalid parameters
    TangoRateLimitError,  # 429 - Rate limit exceeded
)

TangoAPIError

Base exception for all Tango API errors.

Attributes:

  • message (str): Error message
  • status_code (int, optional): HTTP status code

TangoAuthError

Raised when authentication fails (401).

Common causes:

  • Invalid API key
  • Expired API key
  • Missing API key for protected endpoint

TangoNotFoundError

Raised when a resource is not found (404).

Common causes:

  • Invalid agency code
  • Invalid entity key
  • Resource doesn't exist

TangoValidationError

Raised when request parameters are invalid (400).

Attributes:

  • message (str): Error message
  • status_code (int): HTTP status code (400)
  • details (dict): Validation error details from API

TangoRateLimitError

Raised when rate limit is exceeded (429).

Error Handling Examples

from tango import (
    TangoClient,
    TangoAPIError,
    TangoAuthError,
    TangoNotFoundError,
    TangoValidationError,
    TangoRateLimitError,
)

client = TangoClient(api_key="your-api-key")

# Handle specific errors
try:
    agency = client.get_agency("INVALID")
except TangoNotFoundError:
    print("Agency not found")
except TangoAuthError:
    print("Authentication failed - check your API key")
except TangoAPIError as e:
    print(f"API error: {e.message}")

# Handle validation errors with details
try:
    contracts = client.list_contracts(
        award_date_gte="invalid-date"
    )
except TangoValidationError as e:
    print(f"Validation error: {e.message}")
    if e.details:
        print(f"Details: {e.details}")

# Handle rate limiting
try:
    contracts = client.list_contracts(limit=100)
except TangoRateLimitError:
    print("Rate limit exceeded - please wait before retrying")
    # Implement exponential backoff here

# Catch-all for any API error
try:
    result = client.list_contracts()
except TangoAPIError as e:
    print(f"An error occurred: {e.message}")
    if e.status_code:
        print(f"Status code: {e.status_code}")

Best Practices

1. Use Response Shaping

Always use response shaping for better performance:

# ❌ Without shaping (slow, large response)
contracts = client.list_contracts(limit=100)

# ✅ With shaping (fast, small response)
contracts = client.list_contracts(
    shape="key,piid,recipient(display_name),total_contract_value",
    limit=100
)

See Shaping Guide for details.

2. Handle Pagination Properly

Don't fetch all results at once - paginate responsibly:

# ✅ Good - process page by page
page = 1
while page <= 10:  # Limit to 10 pages
    contracts = client.list_contracts(page=page, limit=100)
    process_contracts(contracts.results)

    if not contracts.next:
        break
    page += 1

3. Use Filters to Narrow Results

Filter on the server side instead of client side:

# ❌ Don't do this
all_contracts = client.list_contracts(limit=1000)
gsa_contracts = [c for c in all_contracts.results if c['awarding_agency']['code'] == 'GSA']

# ✅ Do this instead
gsa_contracts = client.list_contracts(
    awarding_agency="GSA",
    limit=100
)

4. Handle Errors Gracefully

Always wrap API calls in try-except blocks:

try:
    contracts = client.list_contracts(limit=10)
except TangoAPIError as e:
    logger.error(f"Failed to fetch contracts: {e.message}")
    # Handle error appropriately

5. Use Environment Variables for API Keys

Never hardcode API keys:

# ❌ Don't do this
client = TangoClient(api_key="sk_live_abc123...")

# ✅ Do this instead
import os
client = TangoClient(api_key=os.getenv("TANGO_API_KEY"))

# Or just use the default (loads from environment)
client = TangoClient()

Additional Resources