TenderNotify API
Programmatic access to aggregated government tender data. A simple REST API returns JSON over HTTPS, and webhooks push new tenders to your systems in real time.
Base URL
https://www.tendernotify.online
Format
JSON over HTTPS
Availability
Business plan
Authentication
All requests authenticate with a personal API key. Generate one in Settings → Developer. Keys are prefixed tn_live_ and are shown only once at creation — store them securely. Pass the key as a bearer token on every request:
Authorization: Bearer tn_live_xxxxxxxxxxxxxxxxxxxxxxxxRate limits
Requests are limited to 60 per minute per API-key owner. Exceeding the limit returns 429 Too Many Requests. Space out bulk jobs and cache responses where possible — tender data updates hourly, so polling more often than that adds no value.
Errors
Errors return the appropriate HTTP status with a JSON body { "error": "message" }.
| Status | Meaning |
|---|---|
200 | Success. |
401 | Missing or invalid API key. |
403 | Key valid but the plan is not Business. |
429 | Rate limit exceeded (60/min). |
500 | Server error — retry with backoff. |
List tenders
Returns tenders in reverse-chronological order (newest posted first). Use source to filter and limit/offset to paginate.
Query parameters
| Param | Type | Description |
|---|---|---|
source | string | Filter by source (see Sources). Optional. |
limit | integer | Results per page. Default 100, max 500. |
offset | integer | Number of results to skip. Default 0. |
Request
curl "https://www.tendernotify.online/api/public/v1/tenders?source=RGUKT%20Basar&limit=50" \
-H "Authorization: Bearer tn_live_..."const res = await fetch(
"https://www.tendernotify.online/api/public/v1/tenders?limit=50",
{ headers: { Authorization: "Bearer tn_live_..." } }
);
const { data, pagination } = await res.json();import requests
r = requests.get(
"https://www.tendernotify.online/api/public/v1/tenders",
headers={"Authorization": "Bearer tn_live_..."},
params={"source": "RGUKT Basar", "limit": 50},
)
r.raise_for_status()
tenders = r.json()["data"]Response
{
"data": [
{
"id": "7b5ced52-c8d2-4cc6-89b5-1757d102daad",
"name": "Supply and Installation of Air Conditioners for RGUKT Basar",
"source": "RGUKT Basar",
"posted_date": "2026-07-22",
"closing_date": "23-07-2026 05:00 PM",
"pdf_s3_url": "https://cdn.tendernotify.online/pdfs/....pdf",
"download_links": [
{ "text": "Download", "url": "https://drive.google.com/file/d/..." }
],
"created_at": "2026-07-22T18:33:07.452Z"
}
],
"pagination": { "limit": 50, "offset": 0, "count": 1 }
}The Tender object
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique tender identifier. |
name | string | Tender title / description. |
source | string | Originating portal (see Sources). |
posted_date | string | Publication date, normalized to YYYY-MM-DD. |
closing_date | string | null | Submission deadline as published by the source (format varies; may be null). |
pdf_s3_url | string | null | Mirrored PDF on our CDN (fast, stable). Null until mirrored. |
download_links | array | Original source documents: [{ text, url }]. |
created_at | string (ISO 8601) | When we first ingested the tender. |
Note: closing_date is passed through from the source and is not guaranteed to be a fixed format — always parse defensively.
Sources
Valid values for the source filter. Pass the exact string (URL-encoded in the query).
Webhooks
Instead of polling, register a webhook to receive new tenders the moment they're detected. Add an HTTPS endpoint in Settings → Developer. On creation you receive a signing secret (shown once) — use it to verify every delivery.
- Deliveries are HTTP
POSTwith a JSON body. - Each request carries an
X-TenderNotify-Signatureheader (HMAC-SHA256 of the raw body). - Respond
2xxwithin 8 seconds — the last delivery status is shown in your dashboard.
Payload
POST https://your-endpoint.example.com
X-TenderNotify-Signature: 3f9a...c1
{
"event": "tenders.new",
"count": 2,
"tenders": [
{
"id": "7b5ced52-...",
"name": "Supply of Medicines for RGUKT Basar",
"source": "RGUKT Basar",
"closingIso": "2026-08-01"
}
],
"sent_at": "2026-07-22T18:33:07.452Z"
}Verifying signatures
Compute an HMAC-SHA256 of the raw request body using your signing secret, then compare (constant-time) against the X-TenderNotify-Signatureheader. Reject the request if they don't match.
import crypto from "crypto";
function verify(rawBody, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}import hmac, hashlib
def verify(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)Ready to build?
Generate an API key and make your first request in minutes.
Get your API key →