Monitor CPPP Tender Deadlines for Compliance Automation
Automate deadline monitoring for India CPPP eProcure tenders. Get Slack and email alerts before bid submission windows close. Tiered urgency classification.

Thirdwatch's India Government Tenders Scraper extracts live tender deadlines from India's Central Public Procurement Portal with pay-per-result pricing. Build automated alerts for approaching bid submission windows, track deadline extensions, and ensure your compliance team never misses a filing. Built for operations teams at companies that bid on government contracts and need systematic deadline management rather than manual calendar entries.
Why automate CPPP deadline monitoring
Missing a government tender deadline is not just a lost opportunity -- it is a compliance risk. According to India's GFR 2017 procurement rules, late bid submissions are summarily rejected with no exceptions. The Central Public Procurement Portal processes over 60,000 active tenders at any given time, each with its own submission deadline, opening date, and amendment history. For operations teams managing bids across multiple departments, tracking these deadlines manually through the eProcure portal is error-prone and does not scale.
The compliance job is specific. A bid management team at an IT services company tracks 30-50 active tenders simultaneously across MeitY, NIC, CERT-In, and UIDAI. A construction firm monitors NHAI and CPWD tenders with staggered deadlines across regional offices. A medical device supplier tracks AIIMS, PGIMER, and JIPMER procurement cycles. Each team needs the same thing: a structured feed of approaching deadlines, tiered alerts (14-day awareness, 7-day escalation, 3-day urgent), and automatic detection of deadline amendments. Without automation, the failure mode is a missed deadline discovered after the fact -- when the only remediation is waiting for the next procurement cycle, often months away.
How does this compare to the alternatives?
| Approach | Alert timeliness | Amendment detection | Multi-department coverage | Setup effort |
|---|---|---|---|---|
| Manual portal checks + calendar entries | Hours to days behind | None unless manually rechecked | Scales linearly with human effort | Per-tender manual entry |
| Email notifications from eProcure portal | Same-day, limited filters | Not supported | Single organization only | Registration per department |
| Tender management SaaS (TenderTiger, BidAssist) | Near real-time | Vendor-dependent | Bundled coverage | Subscription + onboarding |
| Thirdwatch Scraper + alerting pipeline | Real-time per scheduled run | Automatic via field comparison | Unlimited queries and organizations | Half-day initial setup |
eProcure's built-in email notifications are limited to a single organization filter and do not track deadline amendments. Paid SaaS platforms offer broader coverage but lock you into their alerting logic and notification channels. The India Government Tenders Scraper gives you raw structured data to build alerting logic that matches your exact compliance workflow.
How to build deadline monitoring in 5 steps
Step 1: How do I set up daily tender extraction?
Install the Apify client and configure your API token.
pip install apify-client requests
export APIFY_TOKEN="apify_api_xxxxxxxxxxxxxxxx"Step 2: How do I extract tenders with deadline data?
Run the scraper with fetchDetails enabled to get precise submission deadlines and opening dates.
from apify_client import ApifyClient
import os
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("thirdwatch/india-government-tenders-scraper").call(
run_input={
"queries": ["IT services", "software development", "network equipment",
"cloud hosting", "cybersecurity"],
"maxResults": 200,
"fetchDetails": True,
}
)
tenders = list(client.dataset(run["defaultDatasetId"]).iterate_items())
print(f"Extracted {len(tenders)} tenders with deadline data")Step 3: How do I classify tenders by deadline urgency?
Parse the bid_submission_deadline field and bucket tenders into urgency tiers.
from datetime import datetime, timedelta
now = datetime.now()
def classify_urgency(deadline_str):
try:
deadline = datetime.strptime(deadline_str, "%d-%b-%Y %I:%M %p")
except (ValueError, TypeError):
return "unknown"
days_remaining = (deadline - now).days
if days_remaining < 0:
return "expired"
elif days_remaining <= 3:
return "urgent"
elif days_remaining <= 7:
return "escalation"
elif days_remaining <= 14:
return "awareness"
else:
return "open"
urgent_tenders = []
escalation_tenders = []
awareness_tenders = []
for t in tenders:
urgency = classify_urgency(t.get("bid_submission_deadline", ""))
t["_urgency"] = urgency
if urgency == "urgent":
urgent_tenders.append(t)
elif urgency == "escalation":
escalation_tenders.append(t)
elif urgency == "awareness":
awareness_tenders.append(t)
print(f"Urgent (<=3 days): {len(urgent_tenders)}")
print(f"Escalation (<=7 days): {len(escalation_tenders)}")
print(f"Awareness (<=14 days): {len(awareness_tenders)}")Step 4: How do I send tiered Slack alerts?
Route urgency-classified tenders to different Slack channels or mention groups.
import requests
import json
SLACK_WEBHOOK_URGENT = "https://hooks.slack.com/services/T.../B.../xxx"
SLACK_WEBHOOK_TEAM = "https://hooks.slack.com/services/T.../B.../yyy"
def send_slack_alert(webhook_url, tenders, tier_label):
if not tenders:
return
blocks = [{"type": "header", "text": {
"type": "plain_text",
"text": f"eProcure Deadlines - {tier_label} ({len(tenders)} tenders)"
}}]
for t in tenders[:10]: # Slack message limit
blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": (
f"*{t['tender_title'][:80]}*\n"
f"Org: {t.get('organization', 'N/A')}\n"
f"Ref: {t.get('tender_reference_number', 'N/A')}\n"
f"Deadline: *{t.get('bid_submission_deadline', 'N/A')}*\n"
f"<{t.get('detail_href', '')}|View on eProcure>"
)}})
requests.post(webhook_url, json={"blocks": blocks})
# Urgent: goes to #bids-urgent with @channel
send_slack_alert(SLACK_WEBHOOK_URGENT, urgent_tenders, "URGENT - 3 Days or Less")
# Escalation + awareness: goes to #bids-pipeline
send_slack_alert(SLACK_WEBHOOK_TEAM, escalation_tenders, "Escalation - 7 Days")
send_slack_alert(SLACK_WEBHOOK_TEAM, awareness_tenders, "Awareness - 14 Days")Step 5: How do I detect deadline amendments?
Compare current run data against stored deadlines to catch extensions or date changes.
import json
from pathlib import Path
STATE_FILE = Path("tender_deadlines_state.json")
# Load previous state
prev_state = {}
if STATE_FILE.exists():
prev_state = json.loads(STATE_FILE.read_text())
amendments = []
for t in tenders:
tid = t["tender_id"]
new_deadline = t.get("bid_submission_deadline", "")
if tid in prev_state and prev_state[tid] != new_deadline:
amendments.append({
"tender_id": tid,
"tender_title": t["tender_title"],
"old_deadline": prev_state[tid],
"new_deadline": new_deadline,
"organization": t.get("organization"),
})
if amendments:
print(f"\n{len(amendments)} deadline amendments detected:")
for a in amendments:
print(f" {a['tender_title'][:60]}")
print(f" {a['old_deadline']} -> {a['new_deadline']}")
# Send amendment alerts to Slack or email
send_slack_alert(SLACK_WEBHOOK_TEAM, amendments, "AMENDED DEADLINES")
# Save current state
current_state = {t["tender_id"]: t.get("bid_submission_deadline", "")
for t in tenders}
STATE_FILE.write_text(json.dumps(current_state))Sample output
Three records showing tenders at different urgency levels. Each record weighs approximately 1.3 KB.
[
{
"tender_title": "Procurement of Desktop Computers and Peripherals for Census Operations",
"tender_id": "2026_ORGI_334455_1",
"tender_reference_number": "RGI/PROC/2026/IT-023",
"organization": "Office of the Registrar General of India",
"department": "Ministry of Home Affairs",
"published_date": "2026-05-10",
"bid_submission_deadline": "28-May-2026 05:00 PM",
"tender_opening_date": "29-May-2026 11:00 AM",
"detail_href": "https://eprocure.gov.in/eprocure/app?page=FrontEndTendersByOrganisation&service=page"
},
{
"tender_title": "Annual Rate Contract for Server Hardware Maintenance at NIC",
"tender_id": "2026_NIC_667788_1",
"tender_reference_number": "NIC/AMC/2026/HW-056",
"organization": "National Informatics Centre",
"department": "Ministry of Electronics and Information Technology",
"published_date": "2026-05-15",
"bid_submission_deadline": "05-Jun-2026 03:00 PM",
"tender_opening_date": "06-Jun-2026 10:30 AM",
"detail_href": "https://eprocure.gov.in/eprocure/app?page=FrontEndTendersByOrganisation&service=page"
},
{
"tender_title": "Empanelment of Agencies for Cybersecurity Audit of Government Websites",
"tender_id": "2026_CERTIN_112233_1",
"tender_reference_number": "CERT-In/EMP/2026/AUDIT-011",
"organization": "Indian Computer Emergency Response Team",
"department": "Ministry of Electronics and Information Technology",
"published_date": "2026-05-18",
"bid_submission_deadline": "15-Jun-2026 05:00 PM",
"tender_opening_date": "16-Jun-2026 11:00 AM",
"detail_href": "https://eprocure.gov.in/eprocure/app?page=FrontEndTendersByOrganisation&service=page"
}
]bid_submission_deadline is the critical compliance field. tender_opening_date tells you when results will be available. tender_reference_number is what you quote in all correspondence and bid documents. detail_href points to the full tender document for specification review.
Common pitfalls
Three compliance risks surface when monitoring CPPP deadlines at scale. Timezone assumptions -- all eProcure deadlines are in IST (UTC+5:30) but the portal does not always include timezone markers. If your ops team operates across time zones, always convert to IST before calculating days remaining. A deadline that looks like "3 days away" in UTC is actually 2.75 days in IST. Build the IST conversion into your parser, not into downstream logic. Amendment blind spots -- organizations can amend a tender multiple times, each time potentially changing the deadline. If you only compare against the original deadline, you miss intermediate amendments. Store the full amendment history per tender_id, not just the latest value. Alert fatigue from broad queries -- casting a wide net with generic keywords like "services" or "equipment" generates hundreds of alerts daily, causing your team to ignore them. Start narrow with specific department-keyword combinations and widen only after confirming your team processes the existing volume. Better to miss a marginal tender than to miss an urgent one buried under noise.
A well-tuned monitoring pipeline processes 100-300 tenders daily and surfaces 10-20 actionable alerts across the three urgency tiers. If your alert count exceeds 50 daily, your queries are too broad or your organization filters are too loose.
Related use cases
- India Government Tenders Scraper on Apify Store
- Scrape India government tenders for bid tracking
- Build an eProcure tender database for sales pipeline
- Find India government contracts by department
- Lookup insolvency professionals on IBBI registry
- The complete guide to scraping compliance data
- All Thirdwatch use-case guides
Frequently asked questions
What happens if a tender deadline gets extended after I have already flagged it?
Deadline extensions are common on eProcure -- organizations amend tenders to give bidders more preparation time. Run the scraper daily and compare the bid_submission_deadline for each tender_id against your stored value. If the deadline has moved forward, update your alert schedule and notify the bid team. Extensions are a positive signal: more time to prepare a stronger submission.
How far in advance should I set deadline alerts?
Three alert tiers work best: 14 days out (initial awareness, begin document review), 7 days out (escalation, finalize bid/no-bid decision), and 3 days out (urgent, final submission preparation). The 14-day tier catches most CPPP tenders at publication since many have 15-30 day submission windows. The 3-day tier is your safety net for tenders discovered late or those with unusually short windows.
Related
100 free credits, no credit card.
About 30 real searches. Add the MCP to Claude or Cursor in two minutes.