API Documentation
Integrate Tender Impulse global procurement data into your own applications. This guide covers the Tender and Contract Award REST APIs - authentication, id-based paging, encrypted responses, data fields and ready-to-run examples in Node.js, Python, PHP and Java.
Overview
Tender Impulse provides access to over 20,000 global tenders daily, helping organisations discover procurement opportunities from government agencies, public-sector organisations and international institutions worldwide. Two REST APIs are available:
- Tender API - live global tender notices, including full authority details, CPV codes, sectors, deadlines and downloadable documents.
- Contract Award API - awarded-contract records, including the winning supplier, contract value, notice number and downloadable documents.
Both APIs share the same authentication, id-based paging model and AES-encrypted response format, so once you have integrated one, the other is nearly identical.
Getting Access
To obtain your API credentials - an Access Token and an Encryption Key - submit a request through our callback form. Once approved, you will receive the credentials required to authenticate and decrypt API responses, along with the starting lastid for your first call.
Authentication
Every request is authenticated with your Access Token sent as a Bearer token in the Authorization header:
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Requests are made over HTTPS with GET. Responses are AES-128-CBC encrypted and must be decrypted with your Encryption Key (see Response & Encryption).
How Paging Works
Both APIs return records in batches, and you page through them using an id rather than a date. Each call takes a lastid and returns the records that come after it, along with a fetchid - the id of the last record in that batch. The fetchid is what you pass as the lastid of your next call.
- Call the API with the
lastidyou have. On the very first run, use the starting id supplied by Tender Impulse. - Read the records and the
fetchidfrom the decrypted response, and store thefetchid. - If the batch contained records, wait a short while and call again with
lastidset to the storedfetchid. - Repeat until a call returns an empty batch - you are now up to date.
- Start again the next day to pick up newly published records.
fetchid only after you have finished handling the batch. If you store it first and then fail, those records are skipped permanently - there is no way to ask for them again.- Each call returns a limited number of records, so a full catch-up normally takes several calls.
- When a batch is empty,
fetchidcomes back equal to thelastidyou sent, so storing it is always safe. - Sending a
lastidhigher than the server's own last fetch id is rejected with a message telling you the maximum allowed value.
Tender API
Returns global tender notices published after the supplied lastid.
https://tenderimpulse.com/web-api/tender/v2/uat.php?lastid={lastid}Example request
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
"https://tenderimpulse.com/web-api/tender/v2/uat.php?lastid=6771840"
Decrypted payload
{
"status": "success",
"tenders": [
{
"tender_id": 13098184,
"title": "Request for Information - IT Managed Services",
"authority_name": "...",
"country": "Australia",
"cpv_codes": "...",
"deadline": "2026-06-22",
"value_of_contract": "...",
"filepath": "https://tenderimpulse.com/.../document.pdf",
"filename": "document.pdf"
}
],
"fetchid": 13098184
}
Tender fields
| Field | Description |
|---|---|
| tender_id | Unique Tender Impulse identifier for the tender. |
| title | Tender title / notice heading. |
| authority_name | Name of the contracting authority (buyer). |
| address | Postal address of the authority. |
| tel | Contact telephone number. |
| fax | Contact fax number. |
| Contact email address. | |
| web | Authority or source website. |
| contact_name | Named contact person for the tender. |
| contract_type | Type of contract (supply, works, services, etc.). |
| sectors | Classified sector(s) for the tender. |
| cpv_codes | CPV classification codes. |
| country | Country the tender belongs to. |
| original_source | Original publishing source / procurement portal. |
| location | Place of performance / delivery location. |
| reference | Tender reference number from the source. |
| contract_duration | Duration of the contract. |
| value_of_contract | Estimated value of the contract. |
| deadline | Submission deadline date. |
| other_information | Any additional notes or miscellaneous details. |
| filename | Local path/filename of the downloaded tender document. |
| filepath | Local path to the stored tender document. |
Each tender includes a filepath (document URL) and filename; the reference client downloads the file to your local store path and rewrites both fields to the saved location.
Contract Award API
Returns awarded-contract records published after the supplied lastid. Same authentication and paging as the Tender API.
https://tenderimpulse.com/web-api/contract-awards/v2/uat.php?lastid={lastid}Example request
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
"https://tenderimpulse.com/web-api/contract-awards/v2/uat.php?lastid=261374"
Decrypted payload
{
"status": "success",
"contracts": [
{
"ca_id": 261375,
"organisation": "...",
"contracting_authority": "...",
"contract_notice_no": "...",
"contract_awarded_to": "...",
"value_of_contract": "...",
"country": "...",
"publish_date": "2026-06-01",
"filepath": "https://tenderimpulse.com/.../award.pdf",
"filename": "award.pdf"
}
],
"fetchid": 261375
}
Contract award fields
| Field | Description |
|---|---|
| ca_id | Unique Tender Impulse identifier for the contract award. |
| organisation | Organisation associated with the award. |
| contracting_authority | Authority that awarded the contract. |
| contract_notice_no | Contract / notice reference number. |
| contract_awarded_to | Supplier the contract was awarded to. |
| description | Description of the awarded contract. |
| value_of_contract | Awarded contract value. |
| sectors | Classified sector(s). |
| cpv_codes | CPV classification codes. |
| country | Country the award belongs to. |
| publish_date | Date the award was published. |
| filename | Local path/filename of the document (null when none). |
| filepath | Local path to the stored document (null when none). |
filename and filepath are null and no file is downloaded.Response & Encryption
Every endpoint returns a small JSON envelope: an encrypted data payload plus a crc checksum used to verify integrity.
{
"data": "<base64-ciphertext>:<base64-iv>",
"crc": "<md5-hex-of-decrypted-json>"
}
To read a response, decrypt data and validate it against crc:
- Split
dataon the:character into[ciphertext, iv]. - Base64-decode both parts.
- Take your Encryption Key as UTF-8 bytes; pad with
0or truncate to exactly 16 bytes. - Decrypt with AES-128-CBC using that key and iv to get the plaintext JSON.
- Compute
MD5(plaintext)and confirm it equalscrc(case-insensitive). A mismatch means a transmission error - discard the batch. JSON.parsethe plaintext to getstatus, the records array andfetchid.
After a successful decrypt, every official client (Node.js, Python, PHP and Java) returns the same normalised shape:
// Tender client
{ "status": "success", "tenders": [ ... ], "last_fetch_id": 13098184 }
// Contract award client
{ "status": "success", "contracts": [ ... ], "last_fetch_id": 261375 }
All responses are encrypted using AES-128-CBC, verified using MD5 checksum validation, and authenticated using Bearer access tokens.
Error Handling
When something goes wrong, the decrypted payload carries status: "error" and a message:
{ "status": "error", "msg": "Error description" }
The reference clients catch every failure - network, decryption, checksum or API error - and return a uniform shape so your code only checks one thing:
{ "status": "error", "msg": "Error description" }
Common causes include an invalid or expired Access Token, a wrong Encryption Key (decryption fails), a checksum mismatch (Message transmission error), or a lastid higher than the server's current maximum.
Quick Start
Official reference implementations are available for Node.js, Python, PHP and Java. Each one handles authentication, decryption, checksum validation, document downloads and fetchid tracking for you - pick your language below.
Requires Node.js 16+. Install:
npm install axios
Tenders:
const TenderImpulseClient = require("./tenderImpulseClient");
const client = new TenderImpulseClient(
"tender-documents", // local folder for downloaded documents
accessToken, // provided by Tender Impulse
key // AES encryption key provided by Tender Impulse
);
const result = await client.getTenders(lastId);
if (result.status === "success") {
console.log(`Tenders fetched: ${result.tenders.length}`);
console.log(`Last fetch id: ${result.last_fetch_id}`);
// store result.last_fetch_id AFTER handling the batch
} else {
console.error(result.msg);
}
Contract awards use the same pattern, returning the records under contracts:
const TenderImpulseContractAwardClient = require("./tenderImpulseContractAwardClient");
const client = new TenderImpulseContractAwardClient(
"contract-award-documents", accessToken, key
);
const result = await client.getContractAwards(lastId);
if (result.status === "success") {
console.log(`Contract awards fetched: ${result.contracts.length}`);
console.log(`Last fetch id: ${result.last_fetch_id}`);
}
Full runnable examples and both clients: https://github.com/tenderimpulse/TenderImpulseAPIsNode
Requires Python 3.8+. Install:
pip install requests pycryptodome
Tenders:
from tender_impulse_client import TenderImpulseClient
client = TenderImpulseClient(
"tender-documents", # local folder for downloaded documents
access_token, # provided by Tender Impulse
key # AES decryption key provided by Tender Impulse
)
result = client.get_tenders(last_id)
if result["status"] == "success":
print(f"Tenders fetched: {len(result['tenders'])}")
print(f"Last fetch id: {result['last_fetch_id']}")
# store result["last_fetch_id"] AFTER handling the batch
else:
print(f"Error: {result['msg']}")
Contract awards use the same pattern, returning the records under contracts:
from tender_impulse_contract_award_client import TenderImpulseContractAwardClient
client = TenderImpulseContractAwardClient(
"contract-award-documents", access_token, key
)
result = client.get_contract_awards(last_id)
if result["status"] == "success":
print(f"Contract awards fetched: {len(result['contracts'])}")
print(f"Last fetch id: {result['last_fetch_id']}")
Full runnable examples and both clients: https://github.com/tenderimpulse/TenderImpulseAPIsPython
Requires PHP 7.4+ (cURL, OpenSSL, JSON). Install:
; No third-party packages required - enable these in php.ini
extension=curl
extension=openssl
Tenders:
require_once 'TenderImpulseClient.php';
$client = new TenderImpulseClient(
__DIR__ . '/tender-documents', // local folder for downloaded documents
$accessToken, // provided by Tender Impulse
$key // AES decryption key provided by Tender Impulse
);
$result = $client->getTenders($lastId);
if ($result['status'] === 'success') {
echo "Tenders fetched: " . count($result['tenders']) . PHP_EOL;
echo "Last fetch id: " . $result['last_fetch_id'] . PHP_EOL;
// store $result['last_fetch_id'] AFTER handling the batch
} else {
echo "Error: " . $result['msg'] . PHP_EOL;
}
Contract awards use the same pattern, returning the records under contracts:
require_once 'TenderImpulseContractAwardClient.php';
$client = new TenderImpulseContractAwardClient(
__DIR__ . '/contract-award-documents', $accessToken, $key
);
$result = $client->getContractAwards($lastId);
if ($result['status'] === 'success') {
echo "Contract awards fetched: " . count($result['contracts']) . PHP_EOL;
echo "Last fetch id: " . $result['last_fetch_id'] . PHP_EOL;
}
Full runnable examples and both clients: https://github.com/tenderimpulse/TenderImpulseAPIsPHP
Requires Java 17+ / Maven 3.6+. Install:
mvn clean compile
Tenders:
import client.TenderImpulseClient;
import org.json.JSONArray;
import org.json.JSONObject;
TenderImpulseClient client = new TenderImpulseClient(
"tender-documents", // local folder for downloaded documents
ACCESS_TOKEN, // provided by Tender Impulse
KEY // AES decryption key provided by Tender Impulse
);
JSONObject result = client.getTenders(lastId);
if ("success".equals(result.getString("status"))) {
JSONArray tenders = result.getJSONArray("tenders");
System.out.println("Tenders fetched: " + tenders.length());
System.out.println("Last fetch id: " + result.getLong("last_fetch_id"));
// store last_fetch_id AFTER handling the batch
} else {
System.out.println("Error: " + result.getString("msg"));
}
Contract awards use the same pattern, returning the records under contracts:
import client.TenderImpulseContractAwardClient;
import org.json.JSONArray;
import org.json.JSONObject;
TenderImpulseContractAwardClient client =
new TenderImpulseContractAwardClient("contract-award-documents", ACCESS_TOKEN, KEY);
JSONObject result = client.getContractAwards(lastId);
if ("success".equals(result.getString("status"))) {
JSONArray contracts = result.getJSONArray("contracts");
System.out.println("Contract awards fetched: " + contracts.length());
System.out.println("Last fetch id: " + result.getLong("last_fetch_id"));
}
Full runnable examples and both clients: https://github.com/tenderimpulse/TenderImpulseAPIsJava
Whichever language you use, each example stores the returned fetchid in a small JSON state file (tender-state.json / contract-award-state.json) so the next run resumes where the last one stopped:
{ "fetchid": 6771840 }
When the state file is missing, the example falls back to an initial-last-id constant - set that to the starting id supplied by Tender Impulse. In a real integration, store the fetchid in your database, updated in the same transaction that saves the batch.
Resources
Reference clients and runnable examples, one repository per language:
- About our data API: Global Tender API Feed
- Request credentials: Request API access
Ready to integrate?
Request your Access Token and Encryption Key to start pulling live tenders and contract awards.