Complete reference for all Tango Python SDK methods and functionality.
- Client Initialization
- Agencies
- Offices
- Organizations
- Contracts
- IDVs
- OTAs
- OTIDVs
- Subawards
- Vehicles
- Entities
- Forecasts
- Opportunities
- Notices
- Grants
- GSA eLibrary Contracts
- Protests
- Business Types
- NAICS
- Webhooks
- Response Objects
- ShapeConfig (predefined shapes)
- Error Handling
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 fromTANGO_API_KEYenvironment variable.base_url(str, optional): Base URL for the API. Defaults tohttps://tango.makegov.com.
Government agencies that award contracts and manage programs.
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 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 codename- Full agency nameabbreviation- Short namedepartment- Parent department (if applicable)
Federal agency 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 a specific office by code.
office = client.get_office(code="4732XX")Parameters:
code(str): Office code
Returns: Dictionary with office details
Federal organizations (hierarchical agency structure).
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 stringflat(bool): Flatten nested objects (default: False)flat_lists(bool): Flatten arrays with indexed keys (default: False)
Filter Parameters:
cgac- Filter by CGAC codeinclude_inactive- Include inactive organizationslevel- Filter by organization levelparent- Filter by parent organizationsearch- Search termtype- Filter by organization type
Returns: PaginatedResponse with organization dictionaries
Get a specific organization by fh_key.
org = client.get_organization(fh_key="ORG_KEY", shape=ShapeConfig.ORGANIZATIONS_MINIMAL)Parameters:
fh_key(str): Organization keyshape(str, optional): Response shape stringflat(bool): Flatten nested objects (default: False)flat_lists(bool): Flatten arrays with indexed keys (default: False)
Returns: Dictionary with organization details
Federal contract awards and procurement data.
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 numberlimit(int): Results per page (max: 100)shape(str): Fields to return (see Shaping Guide)flat(bool): Flatten nested objects to dot-notation keysflat_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 dateexpiring_lte- Expiring on or before date
Party Filters:
awarding_agency- Agency code (e.g., "4700" for GSA)funding_agency- Funding agency coderecipient_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 identifierpiid- Procurement Instrument Identifierdescription- Contract descriptionaward_date- Date awardedfiscal_year- Fiscal yeartotal_contract_value- Total valuetotal_obligated- Total obligated amountrecipient- 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)
Other Transaction Agreements — non-FAR-based awards.
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
ota = client.get_ota("OTA_KEY", shape=ShapeConfig.OTAS_MINIMAL)Other Transaction IDVs — umbrella OT agreements that can have child awards.
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
otidv = client.get_otidv("OTIDV_KEY", shape=ShapeConfig.OTIDVS_MINIMAL)Subcontract and subaward data under prime awards.
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 keyawarding_agency- Filter by awarding agency codefiscal_year- Exact fiscal yearfiscal_year_gte/fiscal_year_lte- Fiscal year rangefunding_agency- Filter by funding agency codeprime_uei- Filter by prime awardee UEIrecipient- Search by subrecipient namesub_uei- Filter by subrecipient UEI
Returns: PaginatedResponse with subaward dictionaries
Vehicles provide a solicitation-centric way to discover groups of related IDVs and (optionally) expand into the underlying awards via shaping.
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 termshape(str, optional): Shape string (defaults toShapeConfig.VEHICLES_MINIMAL)flat(bool): Flatten nested objects in shaped responseflat_lists(bool): Flatten arrays using indexed keysjoiner(str): Joiner used whenflat=True(default:".")
Returns: PaginatedResponse with vehicle dictionaries
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,
searchfilters expanded awardees when yourshapeincludesawardees(...)(it does not filter the vehicle itself).
List the IDV awardees for a vehicle.
awardees = client.list_vehicle_awardees(
uuid="00000000-0000-0000-0000-000000000001",
shape=ShapeConfig.VEHICLE_AWARDEES_MINIMAL,
)IDVs (indefinite delivery vehicles) are the parent “vehicle award” records that can have child awards/orders under them.
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.
idv = client.get_idv("SOME_IDV_KEY", shape=ShapeConfig.IDVS_COMPREHENSIVE)Lists child awards (contracts) under an IDV.
awards = client.list_idv_awards("SOME_IDV_KEY", limit=25)Lists child IDVs under an IDV.
children = client.list_idv_child_idvs("SOME_IDV_KEY", limit=25)tx = client.list_idv_transactions("SOME_IDV_KEY", limit=100)Vendors, recipients, and organizations doing business with the government.
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 numberlimit(int): Results per pageshape(str): Fields to returnflat(bool): Flatten nested objectsflat_lists(bool): Flatten arrays with indexed keys
Filter Parameters:
search- Full-text searchcage_code- Filter by CAGE codenaics- Filter by NAICS codename- Filter by entity namepsc- Filter by PSC codepurpose_of_registration_code- Filter by registration purposesocioeconomic- Filter by socioeconomic statusstate- Filter by statetotal_awards_obligated_gte/total_awards_obligated_lte- Obligation amount rangeuei- Filter by UEIzip_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 a specific entity by UEI or CAGE code.
entity = client.get_entity(key="ZQGGHJH74DW7", shape=None)Parameters:
key(str): UEI or CAGE codeshape(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 Identifiercage_code- CAGE codelegal_business_name- Official business namedisplay_name- Display namedba_name- Doing Business As namebusiness_types- Array of business type codesprimary_naics- Primary NAICS codephysical_address- Physical address (nested)mailing_address- Mailing address (nested)email_address- Contact emailentity_url- Website
Contract forecast and planning information.
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 numberlimit(int): Results per pageshape(str): Fields to returnflat(bool): Flatten nested objectsflat_lists(bool): Flatten arrays with indexed keys
Filter Parameters:
agency- Filter by agency codeaward_date_after/award_date_before- Expected award date rangefiscal_year- Exact fiscal yearfiscal_year_gte/fiscal_year_lte- Fiscal year rangemodified_after/modified_before- Last-modified date rangenaics_code- NAICS code (exact match)naics_starts_with- NAICS code prefixsearch- Full-text searchsource_system- Filter by source systemstatus- 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 identifiertitle- Forecast titledescription- Descriptionanticipated_award_date- Expected award datefiscal_year- Fiscal yearnaics_code- Industry codestatus- Current status
Active contract opportunities and solicitations.
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 numberlimit(int): Results per pageshape(str): Fields to returnflat(bool): Flatten nested objectsflat_lists(bool): Flatten arrays with indexed keys
Filter Parameters:
active- Filter by active status (bool)agency- Filter by agency codefirst_notice_date_after/first_notice_date_before- First notice date rangelast_notice_date_after/last_notice_date_before- Last notice date rangenaics- NAICS codenotice_type- Filter by notice typeplace_of_performance- Filter by place of performancepsc- PSC coderesponse_deadline_after/response_deadline_before- Response deadline rangesearch- Full-text searchset_aside- Set-aside typesolicitation_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 identifiertitle- Opportunity titlesolicitation_number- Solicitation numberdescription- Descriptionresponse_deadline- Response deadlineactive- Is currently activenaics_code- Industry codepsc_code- Product/service code
Contract award notices and modifications.
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 numberlimit(int): Results per pageshape(str): Fields to returnflat(bool): Flatten nested objectsflat_lists(bool): Flatten arrays with indexed keys
Filter Parameters:
active- Filter by active status (bool)agency- Filter by agency codenaics- NAICS codenotice_type- Filter by notice typeposted_date_after/posted_date_before- Posted date rangepsc- PSC coderesponse_deadline_after/response_deadline_before- Response deadline rangesearch- Full-text searchset_aside- Set-aside typesolicitation_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 identifiertitle- Notice titlesolicitation_number- Solicitation numberdescription- Descriptionposted_date- Date postednaics_code- Industry code
Federal grant opportunities and assistance listings.
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 numberlimit(int): Results per page (max 100)shape(str): Response shape stringflat(bool): Flatten nested objects in shaped responseflat_lists(bool): Flatten arrays using indexed keys
Filter Parameters:
agency- Filter by agency codeapplicant_types- Filter by applicant typecfda_number- Filter by CFDA numberfunding_categories- Filter by funding categoryfunding_instruments- Filter by funding instrumentopportunity_number- Filter by opportunity number (exact match)posted_date_after/posted_date_before- Posted date rangeresponse_date_after/response_date_before- Response date rangesearch- Full-text searchstatus- 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 identifieropportunity_number- Opportunity numbertitle- Grant titlestatus- Status information (nested object with code and description)agency_code- Agency codedescription- Descriptionlast_updated- Last updated timestampcfda_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 Schedule contracts from the GSA eLibrary.
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 numberkey- Filter by keypiid- Filter by PIIDschedule- Filter by GSA schedulesearch- Full-text searchsin- Filter by SIN (Special Item Number)uei- Filter by UEI
Returns: PaginatedResponse with GSA eLibrary contract dictionaries
Get a single GSA eLibrary contract by UUID.
contract = client.get_gsa_elibrary_contract("UUID_HERE")Bid protest records (GAO, COFC, etc.).
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 typeagency- Filter by protested agencycase_number- Filter by case number (e.g.,"b-423274")solicitation_number- Filter by solicitation numberprotester- Search by protester namefiled_date_after/filed_date_before- Filed date rangedecision_date_after/decision_date_before- Decision date rangesearch- 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 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 type classifications.
List available business type codes.
business_types = client.list_business_types(page=1, limit=25)Parameters:
page(int): Page numberlimit(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 codename- Business type namedescription- Description
NAICS (North American Industry Classification System) codes.
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 standardemployee_limit_gte/employee_limit_lte- Employee limit rangerevenue_limit- Exact revenue size standardrevenue_limit_gte/revenue_limit_lte- Revenue limit rangesearch- 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']}")Webhook APIs let Large / Enterprise users manage subscription filters for outbound Tango webhooks.
Discover supported event_type values and subject types.
info = client.list_webhook_event_types()
print(info.event_types[0].event_type)subs = client.list_webhook_subscriptions(page=1, page_size=25)Notes:
- This endpoint uses
page+page_size(tier-capped) rather thanlimit.
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_idsis accepted as an alias forsubject_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.
sub = client.update_webhook_subscription("SUBSCRIPTION_UUID", subscription_name="Updated name")client.delete_webhook_subscription("SUBSCRIPTION_UUID")List your webhook endpoint(s).
endpoints = client.list_webhook_endpoints(page=1, limit=25)endpoint = client.get_webhook_endpoint("ENDPOINT_UUID")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)Send an immediate test webhook to your configured endpoint.
result = client.test_webhook_delivery()
print(result.success, result.status_code)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"])The API does not currently expose a public /api/webhooks/deliveries/ or redelivery endpoint. Use:
test_webhook_delivery()for connectivity checksget_webhook_sample_payload()for building handlers + subscription payloads
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)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 dictionariesExample:
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")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.
The SDK provides specific exception types for different error scenarios.
from tango import (
TangoAPIError, # Base exception
TangoAuthError, # 401 - Authentication failed
TangoNotFoundError, # 404 - Resource not found
TangoValidationError, # 400 - Invalid parameters
TangoRateLimitError, # 429 - Rate limit exceeded
)Base exception for all Tango API errors.
Attributes:
message(str): Error messagestatus_code(int, optional): HTTP status code
Raised when authentication fails (401).
Common causes:
- Invalid API key
- Expired API key
- Missing API key for protected endpoint
Raised when a resource is not found (404).
Common causes:
- Invalid agency code
- Invalid entity key
- Resource doesn't exist
Raised when request parameters are invalid (400).
Attributes:
message(str): Error messagestatus_code(int): HTTP status code (400)details(dict): Validation error details from API
Raised when rate limit is exceeded (429).
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}")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.
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 += 1Filter 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
)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 appropriatelyNever 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()- Shaping Guide - Response shaping syntax, examples, and field reference
- Developer Guide - Dynamic models, predefined shapes, and SDK conformance (maintainers)
- Quick Start - Interactive notebook with examples
- GitHub Repository - Source code and examples
- Tango API Documentation - Full API documentation