7 days of Pro free, no cardStart free

Developers

Bidkoo API

Read-only access to the tenders Bidkoo collects: search and filter them, fetch one in full, and get counts. Requests use an API key and return JSON.

Base URL

https://api.bidkoo.com/api/v1

Plan

API access is included in the Business plan.

Authentication

Send your API key as a bearer token in the Authorization header of every request:

Authorization: Bearer tb_live_YOUR_API_KEY
  • Create a key in Settings → API. Keys start with tb_live_ followed by 64 characters.
  • The full key is shown once, when you create it. We store only a hash of it, so it cannot be shown again: if you lose it, regenerate it.
  • An account has one key. Regenerating replaces it, and revoking removes it; either way the old key stops working at once.
  • Keep the key on your server. Do not put it in a web page or mobile app: anyone who has it can use your quota. Browser requests from other websites are refused by the API's CORS policy.
  • While the key's account is not on a plan with API access (for example after the subscription ends or is downgraded), requests are refused with 403 PLAN_REQUIRED. The key works again once the account is back on such a plan.

List tenders

GET/ext/tenders

Search and filter tenders. All parameters are optional; an unknown parameter is an error rather than being ignored. Lists are comma-separated.

ParameterTypeDescription
qstring, up to 200 charactersText search over the title, description, organisation and other text fields. A value that is exactly a state name (for example Gujarat) filters by that state instead.
categorycomma-separated listOne or more of WORKS, GOODS, SERVICES, CONSULTANCY.
statecomma-separated list, up to 40State names as they appear in the state field, for example Gujarat,Tamil Nadu.
statuscomma-separated listOne or more of ACTIVE, CORRIGENDUM, CLOSED, AWARDED, CANCELLED. ACTIVE and CORRIGENDUM are open for bidding.
sourcePortalcomma-separated list, up to 20Portal names as they appear in the sourcePortal field, for example GeM.
minValuenumber (rupees)Estimated value at least this. Tenders with no stated value are left out when minValue or maxValue is set.
maxValuenumber (rupees)Estimated value at most this.
publishedFromISO 8601 date or date-timePublished on or after. A bare date (2026-11-01) starts at 00:00 UTC.
publishedToISO 8601 date or date-timePublished on or before. A bare date runs to the end of that day, 23:59:59.999 UTC.
deadlineFromISO 8601 date or date-timeBid submission deadline on or after.
deadlineToISO 8601 date or date-timeBid submission deadline on or before.
pageinteger, from 1Page number. Default 1. page × limit may not exceed 10,000; narrow the filters to reach further.
limitinteger, 1 to 50Tenders per page. Default 20.
sortnewest | deadline | value_desc | value_asc | relevanceOrder of results; see the table below.

Sort orders

newestMost recently published first. The default.
deadlineSoonest bid submission deadline first.
value_descHighest estimated value first.
value_ascLowest estimated value first.
relevanceBest match for q first. Without q, the same as newest.
Example
curl -H "Authorization: Bearer tb_live_YOUR_API_KEY" \
  "https://api.bidkoo.com/api/v1/ext/tenders?category=GOODS&state=Gujarat&status=ACTIVE%2CCORRIGENDUM&deadlineFrom=2026-11-01&sort=deadline&limit=50"
Response (illustrative values)
{
  "success": true,
  "data": [
    {
      "id": "cmexample00000000000000001",
      "title": "Example: Supply of laboratory equipment",
      "description": "Example description, as published on the source portal.",
      "referenceNumber": "EXAMPLE/2026/001",
      "organization": "Example Organisation",
      "department": null,
      "tenderType": "OPEN",
      "category": "GOODS",
      "status": "ACTIVE",
      "state": "Gujarat",
      "district": null,
      "city": null,
      "estimatedValue": 1250000,
      "emdAmount": 25000,
      "publishedDate": "2026-10-28T05:30:00.000Z",
      "bidSubmissionDeadline": "2026-11-12T09:30:00.000Z",
      "bidOpeningDate": null,
      "reservedForMSME": true,
      "reservedForStartups": false,
      "sourcePortal": "GeM",
      "sourceUrl": "https://portal.example/tenders/EXAMPLE-2026-001"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 1
  }
}

Get a tender

GET/ext/tenders/{id}

One tender in full, by the id from the list. It adds eligibility criteria, documents and award results to the list fields. An unknown id is 404 TENDER_NOT_FOUND.

Example
curl -H "Authorization: Bearer tb_live_YOUR_API_KEY" \
  "https://api.bidkoo.com/api/v1/ext/tenders/cmexample00000000000000001"
Response (illustrative values)
{
  "success": true,
  "data": {
    "id": "cmexample00000000000000001",
    "title": "Example: Supply of laboratory equipment",
    "description": "Example description, as published on the source portal.",
    "referenceNumber": "EXAMPLE/2026/001",
    "organization": "Example Organisation",
    "department": null,
    "tenderType": "OPEN",
    "category": "GOODS",
    "status": "ACTIVE",
    "state": "Gujarat",
    "district": null,
    "city": null,
    "estimatedValue": 1250000,
    "emdAmount": 25000,
    "publishedDate": "2026-10-28T05:30:00.000Z",
    "bidSubmissionDeadline": "2026-11-12T09:30:00.000Z",
    "bidOpeningDate": "2026-11-13T05:30:00.000Z",
    "reservedForMSME": true,
    "reservedForStartups": false,
    "sourcePortal": "GeM",
    "sourceUrl": "https://portal.example/tenders/EXAMPLE-2026-001",
    "preBidMeetingDate": null,
    "emdExemptMSME": true,
    "minTurnover": 5000000,
    "minNetWorth": null,
    "minExperience": 3,
    "requiredCertifications": [
      "Example certification"
    ],
    "documents": [
      {
        "name": "Example bid document",
        "fileUrl": "https://portal.example/tenders/EXAMPLE-2026-001/document.pdf",
        "fileType": "pdf",
        "fileSize": null
      }
    ],
    "results": []
  }
}

Statistics

GET/ext/stats

Tender counts. active counts tenders open for bidding (ACTIVE and CORRIGENDUM); closed counts CLOSED, AWARDED and CANCELLED; byCategory and byState count open tenders.

ParameterTypeDescription
statestringLimit total, active, closed and byCategory to one state. byState always covers every state.
Example
curl -H "Authorization: Bearer tb_live_YOUR_API_KEY" \
  "https://api.bidkoo.com/api/v1/ext/stats?state=Gujarat"
Response (illustrative values)
{
  "success": true,
  "data": {
    "total": 1200,
    "active": 800,
    "closed": 400,
    "byCategory": {
      "GOODS": 450,
      "WORKS": 200,
      "SERVICES": 120,
      "CONSULTANCY": 30
    },
    "byState": {
      "Gujarat": 300,
      "Maharashtra": 250
    }
  }
}

Tender fields

Every tender has the fields below; those marked detail are only in /ext/tenders/{id}. A field the tender does not state is null.

FieldTypeDescription
idstringBidkoo’s id for the tender. Use it with GET /ext/tenders/{id}.
titlestringTitle as published.
descriptionstring | nullDescription as published.
referenceNumberstringThe issuing organisation’s reference number.
organizationstringIssuing organisation.
departmentstring | nullDepartment, when stated.
tenderTypestringOne of OPEN, LIMITED, SINGLE, TWO_PACKET, EOI, AUCTION, RFP, RFQ.
categorystringOne of WORKS, GOODS, SERVICES, CONSULTANCY.
statusstringOne of ACTIVE, CORRIGENDUM, CLOSED, AWARDED, CANCELLED.
statestringState or union territory.
districtstring | nullDistrict, when the tender states it.
citystring | nullCity, when the tender states it.
estimatedValuenumber | nullEstimated value in rupees (not paise). null when not stated.
emdAmountnumber | nullEarnest money deposit in rupees, when stated.
publishedDatestring (ISO 8601, UTC)When the tender was published.
bidSubmissionDeadlinestring (ISO 8601, UTC)Last date and time to submit a bid.
bidOpeningDatestring | nullBid opening date and time, when stated.
reservedForMSMEbooleanReserved for micro, small and medium enterprises.
reservedForStartupsbooleanReserved for startups.
sourcePortalstringThe portal the tender was collected from, for example GeM.
sourceUrlstringLink to the tender on that portal.
preBidMeetingDatedetailstring | nullPre-bid meeting date and time, when stated.
emdExemptMSMEdetailbooleanMSMEs are exempt from the earnest money deposit.
minTurnoverdetailnumber | nullMinimum turnover required, in rupees, when stated.
minNetWorthdetailnumber | nullMinimum net worth required, in rupees, when stated.
minExperiencedetailnumber | nullMinimum years of experience required, when stated.
requiredCertificationsdetailstring[]Certifications the tender asks for, when stated.
documentsdetailobject[]Tender documents: name, fileUrl (a link on the source portal), fileType and fileSize, each null when not known.
resultsdetailobject[]Award results, when published: winnerName, winnerCompany, awardedValue (rupees) and awardedDate.

Responses and errors

Every response is JSON with a success flag. Successful responses carry data (and meta for lists); errors carry error.code, a readable error.message and sometimes error.details. Match on the code, not the message.

A validation error
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input data",
    "details": [
      {
        "field": "limit",
        "message": "Number must be less than or equal to 50"
      }
    ]
  }
}
StatusCodeMeaning
400VALIDATION_ERRORA parameter is invalid or unknown. error.details names each field and the problem.
401NO_API_KEYNo Authorization: Bearer header.
401INVALID_API_KEY_FORMATThe bearer token is not an API key. Keys start with tb_live_.
401INVALID_API_KEYThe key is not recognised. Check that it was copied in full.
401API_KEY_REVOKEDThe key was revoked, or replaced by a newer key.
401API_KEY_REGENERATE_REQUIREDThe key was created before keys were stored securely. Generate a new one in Settings → API.
403PLAN_REQUIREDThe key’s account is not on a plan with API access, for example after its subscription ended or was downgraded.
403ACCOUNT_DEACTIVATEDThe key’s account has been deactivated.
404TENDER_NOT_FOUNDNo tender has that id.
404NOT_FOUNDNo such endpoint.
429RATE_LIMIT_EXCEEDEDA rate limit was reached. Wait for the seconds in the Retry-After header.
500INTERNAL_ERRORSomething failed on our side. Retry later.

Rate limits

  • 60 requests per minute and 10,000 requests per day, per account. The day resets at 00:00 UTC (05:30 IST). Regenerating the key does not reset either count.
  • Responses to requests with a valid key carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds) for whichever limit is closer to running out, and RateLimit-Policy: 60;w=60, 10000;w=86400.
  • Over a limit, the API answers 429 RATE_LIMIT_EXCEEDED with a Retry-After header in seconds. Refused requests do not count.
  • After 50 requests that fail authentication (401 or 403) from one IP address within 15 minutes, further requests from that address are refused with 429 until the 15-minute window ends.
A rate-limited response
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit of 60 requests per minute exceeded. Retry after 21 seconds.",
    "details": {
      "window": "minute",
      "limit": 60,
      "retryAfterSeconds": 21
    }
  }
}

Notes on the data

  • Amounts are in rupees (INR), not paise.
  • Dates are ISO 8601 in UTC. India Standard Time is UTC+05:30, so a deadline of 2026-11-12T09:30:00.000Z is 15:00 IST.
  • Tenders are collected from public procurement portals. Confirm the details on the source portal (sourceUrl) before you bid.
  • meta.total is exact up to 10,000; a larger result set may report 10,000. Paging reaches the first 10,000 results.
  • When the search index is unavailable, results come straight from the database: q then matches only title, description and organisation (a state name included, as text), and relevance sorts as newest.