Skip to main content
Business plan

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.

Ask about this page:Open in ClaudeOpen in ChatGPT

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:

http
Authorization: Bearer tn_live_xxxxxxxxxxxxxxxxxxxxxxxx
Keep keys secret. Never expose them in browser code or public repos. If a key leaks, revoke it in the dashboard and create a new one — revocation takes effect immediately.

Rate 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" }.

StatusMeaning
200Success.
401Missing or invalid API key.
403Key valid but the plan is not Business.
429Rate limit exceeded (60/min).
500Server error — retry with backoff.

List tenders

Returns tenders in reverse-chronological order (newest posted first). Use source to filter and limit/offset to paginate.

GET/api/public/v1/tenders

Query parameters

ParamTypeDescription
sourcestringFilter by source (see Sources). Optional.
limitintegerResults per page. Default 100, max 500.
offsetintegerNumber of results to skip. Default 0.

Request

curl
curl "https://www.tendernotify.online/api/public/v1/tenders?source=RGUKT%20Basar&limit=50" \
  -H "Authorization: Bearer tn_live_..."
javascript
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();
python
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

json
{
  "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

FieldTypeDescription
idstring (uuid)Unique tender identifier.
namestringTender title / description.
sourcestringOriginating portal (see Sources).
posted_datestringPublication date, normalized to YYYY-MM-DD.
closing_datestring | nullSubmission deadline as published by the source (format varies; may be null).
pdf_s3_urlstring | nullMirrored PDF on our CDN (fast, stable). Null until mirrored.
download_linksarrayOriginal source documents: [{ text, url }].
created_atstring (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).

RGUKT BasarRGUKT NuzvidRGUKT OngoleRGUKT RK ValleyRGUKT SrikakulamRGUKT Main

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 POST with a JSON body.
  • Each request carries an X-TenderNotify-Signature header (HMAC-SHA256 of the raw body).
  • Respond 2xx within 8 seconds — the last delivery status is shown in your dashboard.

Payload

json
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.

javascript
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)
  );
}
python
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 →