Skip to main content
Technology

Tenders-SA Developer API v2: Architecture, Usage, and Integration Guide

The v2 Developer API serves 80+ endpoints across 17 resource groups from a dedicated Cloudflare Worker at api.tenders-sa.org. This guide covers the infrastructure design, authentication model, endpoint catalogue, pagination mechanics, rate limiting, and integration patterns for developers and technical decision-makers.

Developer API v2: What Shipped and How to Use It

The Tenders-SA Developer API v2 runs on a dedicated Cloudflare Worker at api.tenders-sa.org. It exposes 80+ endpoints across 17 resource groups — tenders, awards, organisations, suppliers, directors, categories, provinces, SEO content, industry benchmarks, services, OCDS parties, procurement intelligence, restricted supplier forensics, CIPC company data, newsletter editions, and document metadata. Every endpoint returns structured JSON with a consistent envelope, cursor-based pagination, and per-key rate limiting.

This is not a thin proxy over the main application database. The API runs against its own dedicated D1 database, synced from the main platform every five minutes via shared-secret authenticated cursor-based export endpoints. This separation means API traffic never competes with the user-facing website for database connections, and the Worker's global edge distribution provides sub-50ms cold starts regardless of where the caller is located.

Infrastructure Design

Understanding the infrastructure helps you reason about data freshness, reliability, and failure modes. The pipeline has three stages:

  1. Main application (Next.js on AWS, PostgreSQL) — ingests tenders from National Treasury eTenders, Eskom, Transnet, SANRAL, provincial portals, and municipal systems. AI pipelines enrich each tender with summaries, requirement extraction, value estimation, and category classification.
  2. Sync layer — 33 internal sync-export endpoints expose each entity as a paginated cursor-based stream. The Worker pulls delta changes every five minutes using the since and cursor parameters. Only rows with updatedAt newer than the last cursor are fetched — not full-table scans.
  3. API Worker (Cloudflare, D1) — serves all public and authenticated endpoints from its own database. Validates API keys against SHA-256 hashes, enforces per-key daily and monthly rate limits, logs usage, and caches responses at the edge with TTLs ranging from 5 minutes (tender lists) to 6 hours (analytics).

The Worker exposes two health-check endpoints without authentication — /v2/meta/status and /v2/meta/health — that return per-table record counts and the most recent cron sync status. These are useful for monitoring dashboards and integration health checks.

Base URL and API Versioning

All API requests target the /v2/ prefix:

1https://api.tenders-sa.org/v2/{resource}
TEXT

The /v1/ prefix is not active on this Worker. If you are migrating from the previous API surface, all endpoint paths have changed. The v2 API uses cursor-based pagination instead of page-based, and resource paths follow a RESTful-by-resource convention.

Authentication

All data endpoints require a Bearer token in the Authorization header. Keys use the tsa_prod_ prefix:

1Authorization: Bearer tsa_prod_your_api_key
TEXT

Keys are SHA-256 hashed at rest in D1. The authentication flow validates the hash, checks the key status (must be ACTIVE), confirms the key has not expired, then evaluates per-key daily and monthly usage counters against the key's configured limits. Failed authentications return structured error responses with the specific failure reason (MISSING_API_KEY, INVALID_API_KEY, KEY_NOT_ACTIVE, or KEY_EXPIRED).

Rate Limiting

Every authenticated response returns rate limit information in both HTTP headers and the response body:

1X-RateLimit-Limit: 10000
2X-RateLimit-Remaining: 9847
3X-RateLimit-Reset: 2026-06-16T00:00:00.000Z
4X-RateLimit-Policy: daily
TEXT

The same data appears in the JSON response body under meta.rateLimit, so integrators that do not parse HTTP headers can read limit status from the envelope. When a limit is exceeded, the API returns HTTP 429 with either RATE_LIMIT_DAILY_EXCEEDED or RATE_LIMIT_MONTHLY_EXCEEDED, plus a Retry-After header and a resetsAt ISO timestamp in the response body.

Response Envelope

Every successful response follows this structure:

1{
2  "success": true,
3  "data": [ ],
4  "meta": {
5    "requestId": "req_67af5c2e",
6    "timestamp": "2026-06-15T10:04:14.318Z",
7    "apiVersion": "v2",
8    "totalCount": 5916,
9    "pageSize": 20,
10    "totalPages": 296,
11    "hasNext": true,
12    "hasPrev": false,
13    "rateLimit": {
14      "limit": 10000,
15      "remaining": 9847,
16// ... (truncated)
JSON

Error responses use the same envelope shape but with success: false, an error message, a machine-readable code, and a docs URL pointing to the relevant error documentation:

1{
2  "success": false,
3  "error": "The provided API key is invalid or has been revoked",
4  "code": "INVALID_API_KEY",
5  "action": "API_KEY_REQUIRED",
6  "requestId": "req_a1b2c3d4",
7  "docs": "https://tenders-sa.org/developers/docs",
8  "timestamp": "2026-06-15T10:04:14.318Z"
9}
JSON

Pagination

List endpoints use cursor-based pagination. Every list response includes a nextCursor value in the meta. To retrieve the next page, pass this value as the cursor query parameter:

1GET /v2/tenders?limit=50&cursor=eyJpZCI6ImNs...
TEXT

Cursor-based pagination is stable under concurrent writes — unlike offset-based pagination, a cursor points to a specific row and will not skip or duplicate records when new data is inserted between requests. The totalCount field in the response meta gives the full dataset size, and hasNext / hasPrev booleans simplify client-side navigation logic.

Endpoint Catalogue

The full API surface is documented in an auto-generated OpenAPI 3.0 specification at /v2/openapi.json (no auth required) with an interactive Redocly viewer at /v2/docs. Below is a functional summary of every resource group.

Tenders — 27 endpoints

EndpointDescription
GET /v2/tendersCursor-paginated tender list with limit and cursor params
GET /v2/tenders/search?q=Full-text search across tenders
GET /v2/tenders/closing-soonTenders approaching closing date
GET /v2/tenders/newMost recently published tenders
GET /v2/tenders/bbbee-requiredTenders with B-BBEE requirements
GET /v2/tenders/value-range?min=&max=Filter by estimated value range
GET /v2/tenders/counts/provinceTender counts grouped by province
GET /v2/tenders/counts/categoryTender counts grouped by category
GET /v2/tenders/counts/organizationTender counts grouped by issuing org
GET /v2/tenders/counts/statusTender counts grouped by status
GET /v2/tenders/by-province/{province}Tenders filtered by province
GET /v2/tenders/by-organization/{orgId}Tenders filtered by issuing organisation
GET /v2/tenders/by-publication-type/{type}Filter by publication type (TENDER_NOTICE, AWARD_NOTICE, etc.)
GET /v2/tenders/by-category/{category}Tenders filtered by procurement category
GET /v2/tenders/{id}Full tender detail with all enriched fields
GET /v2/tenders/{id}/awardsAwards linked to this tender
GET /v2/tenders/{id}/contractsContracts linked to this tender
GET /v2/tenders/{id}/milestonesProcurement milestones
GET /v2/tenders/{id}/documentsAssociated documents with metadata
GET /v2/tenders/{id}/biddersList of bidders who responded
GET /v2/tenders/{id}/submission-requirementsRequired submission documentation
GET /v2/tenders/{id}/timelineFull procurement timeline events
GET /v2/tenders/{id}/analysisAI-generated analysis (summary, requirements, criteria)
GET /v2/tenders/{id}/value-estimateEstimated value range with methodology
GET /v2/tenders/{id}/seoSEO metadata for the tender detail page
GET /v2/tenders/{id}/slugURL slug mapping for the tender
GET /v2/tenders/{id}/relatedRelated tenders by category or organisation

Awards — 12 endpoints

EndpointDescription
GET /v2/awardsCursor-paginated award list
GET /v2/awards/{id}Full award detail with supplier and contract data
GET /v2/awards/{id}/subcontractorsSubcontractors for this award with B-BBEE demographics
GET /v2/awards/analyticsAggregated market analytics
GET /v2/awards/analytics/provinceAnalytics grouped by province
GET /v2/awards/analytics/categoryAnalytics grouped by procurement category
GET /v2/awards/analytics/bee-levelAnalytics grouped by B-BBEE contributor level
GET /v2/awards/analytics/enterprise-typeAnalytics grouped by enterprise type (EME, QSE, Large)
GET /v2/awards/by-tender/{tenderId}Awards for a specific tender
GET /v2/awards/by-supplier/{name}Awards received by a supplier
GET /v2/awards/by-supplier-party/{partyId}Awards by OCDS supplier party identifier
GET /v2/awards/by-date-range?from=&to=Awards within a date range

Organisations (Procurement Bodies) — 8 endpoints

EndpointDescription
GET /v2/organizationsCursor-paginated organisation list
GET /v2/organizations/search?q=Search organisations by name
GET /v2/organizations/counts-by-typeOrganisation counts grouped by type
GET /v2/organizations/{id}Full organisation profile with enrichment data
GET /v2/organizations/{id}/tendersTenders issued by this organisation
GET /v2/organizations/{id}/directorsDirectors of this organisation
GET /v2/organizations/by-registration/{reg}Lookup by CIPC registration number
GET /v2/organizations/by-slug/{slug}Lookup by URL slug

Suppliers (Companies) — 9 endpoints

EndpointDescription
GET /v2/companiesCursor-paginated company list
GET /v2/companies/search?q=Search companies by name
GET /v2/companies/top?limit=Top companies by award value or count
GET /v2/companies/{name}Company intelligence profile with aggregated award history
GET /v2/companies/{name}/awardsAwards received by this company
GET /v2/companies/{name}/contractsContracts held by this company
GET /v2/companies/{name}/tendersTenders this company bid on
GET /v2/companies/{name}/directorsDirectors linked to this company
GET /v2/companies/by-registration/{reg}Lookup by registration number

Directors, Categories, Provinces — 10 endpoints

EndpointDescription
GET /v2/directorsCursor-paginated director list
GET /v2/directors/search?q=Search directors by name
GET /v2/directors/{id}Director detail
GET /v2/directors/by-organization/{orgId}Directors of a specific organisation
GET /v2/categoriesFull category list with tender counts
GET /v2/categories/{id}Category detail
GET /v2/categories/by-slug/{slug}Category lookup by URL slug
GET /v2/provincesProvince list
GET /v2/provinces/{id}Province detail
GET /v2/provinces/{id}/health-scoresProvince procurement health scores

SEO, Content, Industry, Services, OCDS — 14 endpoints

EndpointDescription
GET /v2/seo/category/{slug}SEO metadata for a category page
GET /v2/seo/province/{slug}SEO metadata for a province page
GET /v2/articlesCursor-paginated article list
GET /v2/articles/{id}Full article with content
GET /v2/authors/{id}Author profile
GET /v2/industry/benchmarksIndustry value benchmarks with sample sizes
GET /v2/industry/benchmarks/{id}Single benchmark detail
GET /v2/servicesService type classification list
GET /v2/services/{id}Service type detail
GET /v2/ocds/partiesCursor-paginated OCDS party list
GET /v2/ocds/parties/{id}OCDS party detail

Intelligence, Forensic, CIPC, Newsletters, Documents, Meta — remainder

GroupEndpointsKey Endpoint
Intelligence4GET /v2/intel/items — market alerts and sector insights
Forensic4GET /v2/forensic/restricted-suppliers/check?q= — supplier screening
CIPC4GET /v2/cipc/enrichments — company registry data
Newsletters2GET /v2/newsletters — newsletter edition archive
Documents2GET /v2/documents/{id}/download-url — document download link
Meta (auth)2GET /v2/meta/usage — your current API key consumption

Public Endpoints (No Authentication)

Four endpoints are available without an API key. These serve monitoring, reference data, and documentation needs:

EndpointReturns
GET /v2/meta/statusPer-table record counts (tenders, awards, organisations, etc.), last cron run status and timestamp, worker version. Useful for health-check dashboards.
GET /v2/meta/provincesAll provinces with live tender counts. Useful for populating dropdown filters and geographic selectors in UIs.
GET /v2/meta/categoriesAll procurement categories with live tender counts. Useful for category navigation and filter components.
GET /v2/openapi.jsonComplete OpenAPI 3.0 specification auto-generated from the Worker's D1 schema. Import this into Postman, Insomnia, or any OpenAPI-compatible tool to get a full interactive API client.

Working with the API: Key Patterns

Listing tenders with pagination

1# First page — no cursor
2curl -s "https://api.tenders-sa.org/v2/tenders?limit=10" \
3  -H "Authorization: Bearer tsa_prod_<key>" | jq '{count: .meta.totalCount, next: .meta.nextCursor, first: .data[0].title}'
4
5# Second page — pass the cursor from the first response
6curl -s "https://api.tenders-sa.org/v2/tenders?limit=10&cursor=eyJpZCI6ImNs..." \
7  -H "Authorization: Bearer tsa_prod_<key>" | jq '.data[].title'
BASH

Searching tenders

1curl -s "https://api.tenders-sa.org/v2/tenders/search?q=construction+western+cape" \
2  -H "Authorization: Bearer tsa_prod_<key>" | jq '.data[] | {title, province, closingDate}'
BASH

Supplier due diligence: forensic check

1# Check if a supplier appears on any restricted list
2curl -s "https://api.tenders-sa.org/v2/forensic/restricted-suppliers/check?q=ACME+Construction+Pty+Ltd" \
3  -H "Authorization: Bearer tsa_prod_<key>" | jq .
BASH

Award analytics by B-BBEE level

1curl -s "https://api.tenders-sa.org/v2/awards/analytics/bee-level" \
2  -H "Authorization: Bearer tsa_prod_<key>" | jq '.data'
BASH

Monitoring your API usage

1curl -s "https://api.tenders-sa.org/v2/meta/usage" \
2  -H "Authorization: Bearer tsa_prod_<key>" | jq .
BASH

Integration Use Cases

The endpoint catalogue supports several integration patterns that go beyond simple data retrieval:

  • Compliance dashboards — Combine /v2/awards/analytics/bee-level with /v2/awards/analytics/province to build real-time B-BBEE spend visualisations. Filter by date range to track procurement transformation over time.
  • Supplier risk screening — Before onboarding a new subcontractor or responding to a tender that requires joint venture partners, call /v2/forensic/restricted-suppliers/check?q={name} to verify the entity does not appear on government restricted supplier lists.
  • Market opportunity tracking — Poll /v2/tenders/closing-soon and /v2/tenders/new on a schedule to populate opportunity feeds, email alerts, or internal CRM pipelines.
  • Competitor intelligence — Use /v2/companies/{name}/awards to understand which contracts a competitor has won, at what values, and in which categories. Cross-reference with /v2/companies/{name}/tenders to see what they are bidding on.
  • Content enrichment — Pull tender data into external websites, industry blogs, or newsletters. Use the /v2/tenders/{id} endpoint to get AI-summarised descriptions and structured requirement lists without parsing raw government PDFs.
  • Procurement transparency reporting — Use /v2/organizations/{id}/tenders and /v2/awards/by-tender/{id} to audit procurement activity by department, track award concentration, or verify publication compliance.

OpenAPI Specification and Tooling

The API serves its own OpenAPI 3.0 specification at /v2/openapi.json. This spec is auto-generated from the Worker's D1 schema, which means it always reflects the actual deployed data surface — there is no separate hand-maintained spec file. The spec includes:

  • All 17 tag groups with descriptions
  • Every path, HTTP method, and parameter definition
  • Response schemas derived from the D1 table columns
  • Security scheme definition for Bearer token authentication
  • A Redocly HTML viewer at /v2/docs

To use the spec with external tooling, point Postman, Insomnia, or any OpenAPI-compatible client at https://api.tenders-sa.org/v2/openapi.json

. The spec includes the production server URL, so imported collections are immediately callable after setting an API key.

SDKs

Three official SDKs are available in the monorepo under api-sdk-packages/:

  • JavaScript / TypeScript@tenders-sa-org/sdk-js on npm. Published via semantic-release on push to main. Uses the resource pattern: client.tenders.list(), client.awards.get(id).
  • Pythontendersa-sdk on PyPI (alpha). Built with hatchling. Automatically converts camelCase API fields to snake_case Python attributes.
  • CLI@tenders-sa-org/cli on npm. Wraps the JS SDK. Supports piping output to jq and file redirection for scripting.

The SDKs handle authentication, pagination, error mapping to typed exceptions, rate limit tracking, and retry with exponential backoff for transient failures. The HTTP API is the stable surface — SDKs are convenience layers that reduce boilerplate.

Error Handling

HTTP StatusCodeMeaning
400INVALID_REQUEST_BODYRequest body is not valid JSON or missing required fields
401MISSING_API_KEYNo Authorization header present
401INVALID_AUTH_FORMATAuthorization header does not use Bearer scheme
401INVALID_API_KEYKey not found in database or SHA-256 hash mismatch
403KEY_NOT_ACTIVEKey exists but status is not ACTIVE
403KEY_EXPIREDKey has passed its expiry date
403FORBIDDENValid credentials but insufficient permissions for this resource
404NOT_FOUNDThe requested endpoint or resource does not exist
429RATE_LIMIT_DAILY_EXCEEDEDDaily call quota reached
429RATE_LIMIT_MONTHLY_EXCEEDEDMonthly call quota reached
500INTERNAL_ERRORUnexpected server error — retry with exponential backoff

Getting an API Key

API keys are generated through the main application at https://tenders-sa.org/developers/api-keys

. Each key is configurable with:

  • A descriptive name for identifying the key's purpose (e.g. "CRM integration", "Analytics dashboard")
  • A tier (Professional or Enterprise) that determines default rate limits
  • Custom daily and monthly limits if the tier defaults need adjustment
  • An optional expiry date for time-limited access

Keys can be revoked at any time from the same dashboard. Revocation takes effect immediately on the Worker — the key cache has a 5-minute TTL, after which the revocation propagates to all edge locations.

Data Freshness and Monitoring

Data is synced from the main application to the API Worker's D1 database every 5 minutes via a cron trigger. Each sync cycle processes up to 10 entities in priority order — entities that have never been synced or have unfinished cursors are prioritised. The sync uses the shared-secret-protected internal export endpoints and only fetches rows with updatedAt timestamps newer than the last completed cursor.

You can inspect the current state of the API database at any time through the public health endpoint:

1curl -s "https://api.tenders-sa.org/v2/meta/status" | jq '.data.entities'
2# Returns: { "tenders": 5916, "awards": 1800, "organizations": 764, ... }
BASH

The response includes the most recent cron run timestamp, its status, and rows synced in that run. For production integrations, poll this endpoint on a regular interval and alert if the last successful sync is older than your freshness tolerance.

Tags

APIDeveloper ToolsREST APIv2Cloudflare WorkersOpenAPIProcurement DataGovernment DataOpen DataIntegration
Relevant Tender Opportunities

Based on this article's topics, here are some current tenders that might interest you

Supplies: Computer Equipment

FOR THE APPOINTMENT OF A SERVICE PROVIDER TO SUPPLY AND DELIVER ONCE OFF PROCUREMENT OF INFORMATION TECHNOLOGY (IT) EQUIPMENT FROM SUPPLIERS LISTED ON THE SITA RFB 740 TRANSVERSAL CONTRACT- FOR THE WESTERN CAPE (COASTAL REGION) FOR DEPARTMENT OF FORESTRY, FISHERIES, AND THE ENVIRONMENT (DFFE).

National - Environment, Forestry and Fisheries
Western Cape
03 Jul 2026
10d left
Supplies: General

Request for Supply and Delivery of PAT Materials to appropriate workshop clothing to Technical, Comprehensive and Agricultural Schools: Lot1 - Supply, delivery and installation of Equipment and Tools for Civil, Electrical, Mechanical Technology Specialisations and Engineering Graphics and Design (EGD): Lot2 - Supply and Delivery of Practical Assessment Task (PAT) Materials to Technical, Comprehensive and Agricultural Schools: Lot3 - Supply and Delivery of Personal Protective Clothing for Teachers to Technical and Comprehensive Schools in the Free State for the period of Three Financial Years from the date of approval

Free State - Education
Free State
17 Jul 2026
24d left
Services: Professional

Request for information on commercially available and/or proven technology for re-engineering of the OPC DA (Data Access) communication protocol for control systems that are impacted by the Microsoft security changes for systems using the Windows operating system and have the OPC classic architecture infrastructure based on DCOM based communication (where OEM support is no longer available as they have exited the business).

Eskom
Mpumalanga
14 Jul 2026
21d left
Other Service Activities

THE APPOINTMENT OF ONE SERVICE PROVIDER TO SUPPLY, INSTALL, CONFIGURE AND PROVIDE TECHNICAL TRAINING OF INFORMATION TECHNOLOGY (IT) SERVICE HELPDESK SYSTEM FOR THE DEPARTMENT OF FORESTRY, FISHERIES, AND THE ENVIRONMENT (DFFE) FOR A PERIOD OF TWO (2) YEARS.

National - Environment, Forestry and Fisheries
National
13 Jul 2026
20d left
Other Service Activities

THE APPOINTMENT OF ONE SERVICE PROVIDER TO SUPPLY, INSTALL, CONFIGURE AND PROVIDE TECHNICAL TRAINING OF INFORMATION TECHNOLOGY (IT) SERVICE HELPDESK SYSTEM FOR THE DEPARTMENT OF FORESTRY, FISHERIES, AND THE ENVIRONMENT (DFFE) FOR A PERIOD OF TWO (2) YEARS.

National - Environment, Forestry and Fisheries
National
13 Jul 2026
20d left
Civil Engineering

REPLACEMENT OF EXISTING PIPE SYSTEMS THROUGH TRENCHLESS TECHNOLOGY FOR A PERIOD ENDING 30 JUNE 2029

Breede Valley Municipality
Western Cape
24 Jul 2026
31d left

Want to see all available tenders?

Browse All Tenders →
AI-Powered Matching
Never Miss a Perfect Tender Again
Our AI analyzes thousands of tenders and finds the ones YOUR company can actually win
AI Match Scoring for every tender
Instant alerts for 85%+ matches
B-BBEE level optimization
Document readiness checks

Share this article

Tenders-SA Developer API v2: Architecture, Usage, and Integration Guide

The v2 Developer API serves 80+ endpoints across 17 resource groups from a dedicated Cloudflare Worker at api.tenders-sa.org. This guide covers the infrastructure design, authentication model, endpoint catalogue, pagination mechanics, rate limiting, and integration patterns for developers and technical decision-makers.

https://www.tenders-sa.org/blog/developer-api-v2-architecture-and-usage