Accessing SA Tender Data with the Python SDK
A practical guide to the tendersa-sdk Python package — async/await client, resource methods, paginated iteration, error handling, and rate limit tracking. For data analysts, researchers, and Python developers.
SA Procurement Data in Python: Async, Typed, and Open Source
The tendersa-sdk Python package provides an idiomatic async client for the Tenders-SA Developer API. It uses httpx for asynchronous HTTP, maps every API response to typed Python objects, and supports async iteration through paginated results. It is designed for data analysts, researchers, and Python backend developers who need to integrate SA procurement data into their workflows.
The SDK is open source under the MIT license and published on PyPI. The source code is available at github.com/Tenders-SA/python.
Installation
1pip install tendersa-sdkBASH
The SDK requires Python 3.9+ and httpx 0.27+ (installed automatically as a dependency).
Quick Start
The SDK uses async/await throughout. Here is a minimal example to verify connectivity and pull live data:
1import asyncio 2from tendersa import TendersaClient 3 4async def main(): 5 client = TendersaClient(api_key="tsa_prod_your_key") 6 7 # List open tenders 8 tenders = await client.tenders.list({ 9 "status": "OPEN", 10 "province": "Western Cape", 11 }) 12 13 for t in tenders.data: 14 print(t.title, t.status) 15 16// ... (truncated)PYTHON
The client also supports async context manager usage, which handles cleanup automatically:
1async with TendersaClient(api_key="tsa_prod_your_key") as client: 2 result = await client.tenders.list({"status": "OPEN"})PYTHON
Client Configuration
1from tendersa import TendersaClient 2 3client = TendersaClient( 4 api_key="tsa_prod_your_key", 5 base_url="https://api.tenders-sa.org", # default 6 timeout=30.0, # 30 seconds (default) 7 max_retries=3, # exponential backoff (default) 8)PYTHON
Resource Methods
The SDK is organised into five resource classes, mirroring the API structure. Each method maps directly to a REST endpoint.
Tenders
1# List with filters 2result = await client.tenders.list({ 3 "status": "OPEN", 4 "category": "Construction", 5 "province": "Gauteng", 6 "sort": "-closingDate", 7}) 8 9# Get detail 10# Access AI-enriched fields: .summary, .requirements, .estimated_value 11detail = await client.tenders.get("tender_001") 12 13# Sub-resources 14docs = await client.tenders.documents("tender_001") 15awards = await client.tenders.awards("tender_001") 16// ... (truncated)PYTHON
Awards
1result = await client.awards.list({ 2 "province": "Western Cape", 3 "beeLevel": "Level 1", 4 "minAmount": 1_000_000, 5}) 6 7award = await client.awards.get("award_001") 8 9analytics = await client.awards.analytics({ 10 "groupBy": "province", 11 "from": "2025-01-01", 12 "to": "2025-12-31", 13})PYTHON
Companies
1# Full company intelligence profile 2company = await client.companies.get("BuildCorp SA") 3 4# Search by name, BEE level, or province 5results = await client.companies.search({ 6 "q": "Construction", 7 "beeLevel": "Level 1", 8 "province": "Gauteng", 9})PYTHON
Organisations (Procurement Bodies)
1org = await client.organizations.get("org_001") 2tenders = await client.organizations.tenders("org_001", {"status": "OPEN"})PYTHON
Meta
1status = await client.meta.status() 2provinces = await client.meta.provinces() 3categories = await client.meta.categories() 4usage = await client.meta.usage()PYTHON
Pagination
The Python SDK supports idiomatic async iteration through paginated results. Use the paginated() method on list resources:
1# Iterate through all pages of open tenders 2async for page in client.tenders.paginated({ 3 "status": "OPEN", 4 "category": "Construction", 5}): 6 for tender in page: 7 print(tender.title, tender.closing_date) 8 9# Control max pages 10async for page in client.awards.paginated( 11 {"province": "Gauteng"}, 12 max_pages=5, 13): 14 print(f"Page {page.page}: {len(page)} items") 15 print(f" Total: {page.total_count}, Has next: {page.has_next}")PYTHON
Each page is a PaginatedResponse object with convenience properties: .page, .page_size, .total_count, .total_pages, .has_next, and .has_prev.
Error Handling
The SDK raises typed exceptions for every HTTP status code the API can return. Catch specific exceptions for targeted handling:
1from tendersa.errors import ( 2 TendersaError, 3 AuthError, 4 NotFoundError, 5 RateLimitError, 6 BadRequestError, 7 ForbiddenError, 8 ConflictError, 9) 10 11try: 12 tender = await client.tenders.get("nonexistent") 13except AuthError: 14 print("Invalid API key. Get one at https://tenders-sa.org/developers/api-keys") 15except NotFoundError: 16// ... (truncated)PYTHON
Rate Limit Tracking
1rl = client.last_rate_limit 2if rl: 3 print(f"{rl.remaining}/{rl.limit} requests remaining ({rl.policy})")PYTHON
Use Cases in Python
The Python SDK is particularly useful for data analysis and automation workflows:
- Market research: Pull tender data into pandas DataFrames for sector analysis. Iterate through all pages of awards and export to CSV for offline analysis.
- Competitive monitoring: Script periodic scans that check for new awards in your sector and send alerts when specific suppliers win contracts.
- Compliance tracking: Monitor procurement opportunities in specific categories or regions. Use the analytics endpoint to track spending trends over time.
- Data enrichment: Combine Tenders-SA data with other datasets (e.g. company registries, geographic data) for enriched analysis.
Links and Resources
The full SDK source code is on GitHub at github.com/Tenders-SA/python. The package is published to PyPI as tendersa-sdk. The complete API reference is available at tenders-sa.org/developers/docs.
The repository includes the full Python source code with type annotations, comprehensive test coverage, async context manager support, and a README with examples for every endpoint.
Tags
Based on this article's topics, here are some current tenders that might interest you
NAMC T03 2026 APPOINTMENT OF A SERVICE PROVIDER TO PROVIDE MANAGED INFORMATION AND COMMUNICATION TECHNOLOGY SERVICES TO THE NATIONAL AGRICULTURAL MARKETING COUNCIL (NAMC) FOR A PERIOD OF THREE (3) YEARS
Request for Proposals (RFP) The Provision of Underwater Measurement Capabilities and Technical Expertise Related to the Development and Testing of Maritime Technology and Underwater Sensors with the CSIR for a Period of 5 Years.
Want to see all available tenders?
Browse All Tenders →Share this article
Accessing SA Tender Data with the Python SDK
A practical guide to the tendersa-sdk Python package — async/await client, resource methods, paginated iteration, error handling, and rate limit tracking. For data analysts, researchers, and Python developers.