Scrape India Government Tenders for Bid Tracking (2026)
Track live government tenders from India's eProcure CPPP portal with structured data. Automate bid discovery, filter by department, and never miss a deadline.
Thirdwatch's India Government Tenders Scraper extracts live tender listings from India's Central Public Procurement Portal (eProcure CPPP) with pay-per-result pricing. Pull tender titles, reference numbers, organizations, deadlines, and detail links into structured JSON. Built for founders and operations leads at companies that bid on government contracts and need systematic discovery instead of manual portal browsing.
Why track India government tenders programmatically
India's Central Public Procurement Portal (CPPP) at eprocure.gov.in is the mandatory listing platform for central government tenders. According to the Ministry of Finance's 2024-25 procurement report, over 60,000 active tenders are listed at any given time across hundreds of departments, ministries, and public sector undertakings. According to the World Bank's India procurement assessment, India's public procurement spending exceeds 20% of GDP, making it one of the largest government procurement markets globally. For any company that bids on government work, manually checking the eProcure portal is a daily time sink that scales poorly.
The job-to-be-done is straightforward. A construction firm tracks CPWD and NHAI tenders for road and infrastructure projects. An IT services company monitors MeitY and NIC tenders for software development contracts. A medical supplies distributor watches AIIMS and state hospital tenders for equipment procurement. A defence contractor filters Ministry of Defence tenders by category. All of these reduce to the same workflow: query the portal daily, extract structured tender data, filter by relevance, and flag approaching deadlines before the submission window closes.
Manual checking fails at scale. With hundreds of new tenders published daily across dozens of departments, a human reviewer misses opportunities, discovers them too late, or wastes hours scrolling through irrelevant listings. Programmatic extraction solves this by delivering filtered, structured data to your inbox or dashboard every morning.
How does this compare to the alternatives?
| Approach | Reliability | Setup time | Maintenance | Structured output |
|---|---|---|---|---|
| Manual eProcure portal browsing | Human-dependent, inconsistent | None | Daily time investment | No, copy-paste |
| Paid tender aggregation platforms (TenderTiger, BidAssist) | High | Days | Subscription lock-in | Partial, proprietary format |
| Custom scraper built in-house | Variable | Weeks | Ongoing dev maintenance | Yes, full control |
| Thirdwatch eProcure Scraper | Production-tested | 30 minutes | Thirdwatch maintains extraction | Yes, clean JSON |
Paid aggregation platforms bundle tenders from multiple sources but charge monthly subscriptions regardless of volume and lock you into their interface. An in-house scraper gives full control but requires ongoing maintenance as the eProcure portal changes. The India Government Tenders Scraper gives you structured output with pay-per-result pricing and zero maintenance burden.
How to track government tenders in 5 steps
Step 1: How do I get my Apify API token?
Sign up at apify.com (free tier, no credit card required). Navigate to Settings, then Integrations, and copy your personal API token. All examples below assume the token is set as an environment variable:
export APIFY_TOKEN="apify_api_xxxxxxxxxxxxxxxx"Step 2: How do I install the Apify Python client?
pip install apify-clientStep 3: How do I run a basic tender search?
Use the queries field to search for tenders by keyword and maxResults to control volume. The organization field narrows results to a specific department.
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": ["road construction", "highway maintenance"],
"maxResults": 50,
"organization": "National Highways Authority of India",
"fetchDetails": True,
}
)
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())
print(f"Found {len(items)} tenders")
for item in items[:3]:
print(f"{item['tender_title']} | Deadline: {item['bid_submission_deadline']}")Step 4: How do I filter tenders by approaching deadlines?
Post-process the results to flag tenders with submission deadlines within the next 7 days.
from datetime import datetime, timedelta
today = datetime.now()
cutoff = today + timedelta(days=7)
urgent = []
for item in items:
deadline_str = item.get("bid_submission_deadline", "")
if deadline_str:
try:
deadline = datetime.strptime(deadline_str, "%d-%b-%Y %I:%M %p")
if today <= deadline <= cutoff:
urgent.append(item)
except ValueError:
continue
print(f"\n{len(urgent)} tenders with deadlines in the next 7 days:")
for t in urgent:
print(f" {t['tender_title']}")
print(f" Org: {t['organization']} | Deadline: {t['bid_submission_deadline']}")
print(f" Ref: {t['tender_reference_number']}\n")Step 5: How do I schedule daily automated runs?
Use the Apify scheduling API to trigger the scraper every morning.
schedule = client.schedules().create(
name="daily-tender-check",
cron_expression="0 7 * * *", # 7:00 AM daily
actions=[{
"type": "RUN_ACTOR",
"actorId": "thirdwatch/india-government-tenders-scraper",
"runInput": {
"queries": ["IT services", "software development", "cloud infrastructure"],
"maxResults": 100,
"fetchDetails": True,
},
}],
)
print(f"Schedule created: {schedule['id']}")Sample output
Each record from the dataset contains the full tender metadata. Three rows of this shape weigh approximately 4 KB.
[
{
"tender_title": "Supply and Installation of Network Equipment for NIC Data Centre",
"tender_id": "2026_NIC_812345_1",
"tender_reference_number": "NIC/PROC/2026-27/NET-045",
"organization": "National Informatics Centre",
"department": "Ministry of Electronics and Information Technology",
"published_date": "2026-05-20",
"bid_submission_deadline": "15-Jun-2026 03:00 PM",
"tender_opening_date": "16-Jun-2026 03:30 PM",
"detail_href": "https://eprocure.gov.in/eprocure/app?page=FrontEndTendersByOrganisation&service=page"
},
{
"tender_title": "Construction of 4-Lane Highway Bypass on NH-48 Karnataka Section",
"tender_id": "2026_NHAI_887654_1",
"tender_reference_number": "NHAI/PIU-BLR/2026/HW-112",
"organization": "National Highways Authority of India",
"department": "Ministry of Road Transport and Highways",
"published_date": "2026-05-18",
"bid_submission_deadline": "10-Jun-2026 05:00 PM",
"tender_opening_date": "11-Jun-2026 11:00 AM",
"detail_href": "https://eprocure.gov.in/eprocure/app?page=FrontEndTendersByOrganisation&service=page"
},
{
"tender_title": "Annual Maintenance Contract for Medical Imaging Equipment at AIIMS Delhi",
"tender_id": "2026_AIIMS_445566_1",
"tender_reference_number": "AIIMS/STORES/2026/MED-089",
"organization": "All India Institute of Medical Sciences",
"department": "Ministry of Health and Family Welfare",
"published_date": "2026-05-22",
"bid_submission_deadline": "20-Jun-2026 02:00 PM",
"tender_opening_date": "21-Jun-2026 10:00 AM",
"detail_href": "https://eprocure.gov.in/eprocure/app?page=FrontEndTendersByOrganisation&service=page"
}
]tender_reference_number is the canonical identifier for cross-referencing with your internal bid management system. bid_submission_deadline is the critical field for deadline tracking. organization and department enable filtering and routing to the right internal team. detail_href links to the full tender document on eProcure for downloading specifications and bid documents.
Common pitfalls
Three issues surface repeatedly when building tender tracking workflows. Deadline timezone ambiguity -- eProcure displays deadlines in IST but does not include timezone markers in all formats. Always assume IST (UTC+5:30) when parsing bid_submission_deadline and convert to your local timezone before setting alerts. Stale results from infrequent scraping -- tenders with short submission windows (7-10 days) can expire between weekly runs. Daily scraping is the minimum viable cadence; twice-daily for time-sensitive categories like emergency procurement. Duplicate tenders across queries -- overlapping search terms (e.g., "road construction" and "highway construction") return the same tenders. Deduplicate on tender_id before inserting into your database to avoid double-counting and duplicate alerts.
The eProcure portal occasionally goes through maintenance windows during off-hours. Schedule runs during IST business hours (10 AM - 6 PM) for the most reliable extraction. Pair the tender scraper with a notification system (Slack webhook, email, or SMS) to ensure your bid team sees urgent opportunities within hours of publication rather than days.
Related use cases
- Build an eProcure tender database for sales pipeline
- Monitor CPPP tender deadlines for compliance
- Find India government contracts by department
- Scrape IBBI insolvency cases for credit risk — Cross-reference tender-winning companies against insolvency filings.
- Scrape MCA India company data for due diligence — Verify bidder company status and directors via MCA registry.
- The complete guide to scraping compliance data
- All Thirdwatch use-case guides
Frequently asked questions
How often should I scrape eProcure for new tenders?
Daily scraping catches most new publications. CPPP publishes tenders throughout the day, but the bulk appear during IST business hours (10 AM - 6 PM). A daily morning run at 7 AM IST captures the previous day's publications and gives your team a full business day to evaluate and prepare bids before submission windows close.
Can I filter tenders by a specific government department?
Yes. The organization input field lets you filter by any publishing organization on eProcure, such as Indian Railways, CPWD, or Defence Ministry. This narrows results to only tenders from your target department, reducing noise and letting your team focus on relevant opportunities without manual sorting.
Related
100 free credits, no credit card.
About 30 real searches. Add the MCP to Claude or Cursor in two minutes.