Access live tenders from across Africa. Display, filter, and integrate tender data seamlessly into any application.
Access live tender data updated continuously from multiple African countries
Filter tenders by country, category, sector, county, and closing date
Download tender documents securely through our API gateway
Enterprise-grade API with rate limiting and 99.9% uptime guarantee
Use HTTP headers for API authentication instead of query parameters.
// SECURE: Use headers
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
Select from our pre-built solutions or build your own integration.
Deploy your integration and start displaying live tenders to your users.
Enter your API key to test connectivity and explore the API:
These endpoints trigger file downloads directly. Use the iframe method for reliable downloads.
// Response will appear here...
Get all available countries with their codes
Get all active tenders with optional filtering
?country=KE&category=5§or=12
Get detailed information about a specific tender
/api/v1/tenders/227817
Tender descriptions are automatically formatted and sanitized
Get tenders published today
Get tenders closing soon (default: 7 days)
?days=3&country=KE
Get all documents for a specific tender
/api/v1/tenders/227817/documents
Returns: { "status": true, "documents": [...] }
Download tender advertisement as PDF (Direct Download)
/api/v1/advert_doc/227817
Auto-downloads: tender-title-advert.pdf
Note: This is a binary download, not a JSON response
Download specific document (Direct Download)
/api/v1/documents/1/download
Auto-downloads: document-title.extension
Note: This is a binary download, not a JSON response
Get all tender categories
Get all tender sectors
Get all tender statuses
Get all counties (optionally filter by country_id)
?country_id=1
Get company logo by company ID
/api/v1/company_logo/6
Response: Binary image file (PNG/JPG/JPEG)
Flow:
// Use as image source in HTML:
<img src="https://api.tendersoko.africa/api/v1/company_logo/6" alt="Company Logo" />
// Example with fallback:
<img src="https://api.tendersoko.africa/api/v1/company_logo/6"
onerror="this.src='https://api.tendersoko.africa/images/companies/briefcase.png'"
alt="Company Logo" />
Note: This endpoint requires API authentication via headers
Get tender with sanitized HTML descriptions
All tender descriptions are automatically cleaned and formatted for safe display
Use this PHP function to clean and safely display tender descriptions:
1/**
2 * Clean and format HTML content safely
3 * Removes dangerous tags while preserving formatting
4 */
5function clean_html_content($html) {
6 if (empty($html)) {
7 return 'No description available.';
8 }
9
10 // Decode HTML entities
11 $html = html_entity_decode($html, ENT_QUOTES | ENT_HTML5, 'UTF-8');
12
13 // Allow safe tags but remove dangerous ones
14 $allowed_tags = '<p><br><strong><b><em><i><u><ul><ol><li><table><tr><td><th><tbody><thead><tfoot><div><span><h1><h2><h3><h4><h5><h6><a><img><sup><sub>';
15
16 // Clean HTML while preserving basic formatting
17 $html = strip_tags($html, $allowed_tags);
18
19 // Fix common MS Word HTML issues
20 $html = preg_replace('/class="[^"]*"/', '', $html); // Remove class attributes
21 $html = preg_replace('/style="[^"]*"/', '', $html); // Remove style attributes
22 $html = preg_replace('/\s+/', ' ', $html); // Normalize whitespace
23
24 // Ensure links have proper attributes
25 $html = preg_replace_callback('/<a[^>]*>/', function($matches) {
26 $tag = $matches[0];
27 // Add target="_blank" and rel="noopener noreferrer"
28 if (strpos($tag, 'target=') === false) {
29 $tag = str_replace('<a ', '<a target="_blank" rel="noopener noreferrer" ', $tag);
30 }
31 if (strpos($tag, 'href=') === false) {
32 $tag = str_replace('<a ', '<a href="#" ', $tag);
33 }
34 return $tag;
35 }, $html);
36
37 return trim($html);
38}
39
40// Usage example:
41$tender_description = $tender['description'] ?? '';
42$clean_description = clean_html_content($tender_description);
43echo "<div class='tender-description'>" . $clean_description . "</div>";
Add these CSS styles to make tender descriptions look professional:
1.tender-description {
2 font-size: 1rem;
3 line-height: 1.8;
4 color: #334155;
5}
6
7.tender-description p {
8 margin-bottom: 1rem;
9}
10
11.tender-description table {
12 width: 100%;
13 border-collapse: collapse;
14 margin: 1rem 0;
15}
16
17.tender-description td, .tender-description th {
18 border: 1px solid #ddd;
19 padding: 8px;
20}
21
22.tender-description tr:nth-child(even) {
23 background-color: #f9f9f9;
24}
25
26.tender-description ul, .tender-description ol {
27 margin: 1rem 0;
28 padding-left: 2rem;
29}
30
31.tender-description li {
32 margin-bottom: 0.5rem;
33}
34
35.tender-description a {
36 color: #1a5f7a;
37 text-decoration: underline;
38}
39
40.tender-description a:hover {
41 color: #e74c3c;
42}
Removes all dangerous HTML tags and attributes to prevent cross-site scripting attacks
Keeps tables, lists, paragraphs, headings, and basic formatting intact
Automatically adds target="_blank" and rel="noopener" to all links for security
Removes MS Word classes, inline styles, and unnecessary whitespace
Here's a complete example of displaying a tender with clean description:
1<div class="tender-detail-section">
2 <h3>Tender Description</h3>
3 <div class="tender-description">
4 <?php
5 // $tender is from API response
6 $description = $tender['description'] ?? '';
7 echo clean_html_content($description);
8 ?>
9 </div>
10</div>
11
12<?php if (!empty($tender['eligibility_requirements'])): ?>
13<div class="tender-detail-section">
14 <h3>Eligibility Requirements</h3>
15 <div class="tender-description">
16 <?php echo clean_html_content($tender['eligibility_requirements']); ?>
17 </div>
18</div>
19<?php endif; ?>
1// Tendersoko API - Document Download Solution
2const API_KEY = 'YOUR_API_KEY_HERE';
3const API_BASE = 'https://api.tendersoko.africa/api/v1';
4
5/**
6 * Download tender advert - uses hidden iframe method
7 * This method avoids console errors and works reliably
8 */
9function downloadAdvert(tenderId) {
10 const iframe = document.createElement('iframe');
11 iframe.style.display = 'none';
12 iframe.src = `${API_BASE}/advert_doc/${tenderId}`;
13 document.body.appendChild(iframe);
14
15 setTimeout(() => {
16 if (iframe.parentNode) {
17 document.body.removeChild(iframe);
18 }
19 }, 5000);
20
21 return true;
22}
23
24/**
25 * Download document - works for all document types (PDF, DOC, etc.)
26 */
27function downloadDocument(docId) {
28 const iframe = document.createElement('iframe');
29 iframe.style.display = 'none';
30 iframe.src = `${API_BASE}/documents/${docId}/download`;
31 document.body.appendChild(iframe);
32
33 setTimeout(() => {
34 if (iframe.parentNode) {
35 document.body.removeChild(iframe);
36 }
37 }, 5000);
38
39 return true;
40}
41
42/**
43 * Alternative method using fetch (may show console errors but works)
44 */
45async function downloadDocumentWithFetch(docId, docTitle, docExtension) {
46 try {
47 const response = await fetch(${API_BASE}/documents/${docId}/download, {
48 headers: {
49 'Authorization': 'Bearer ' + API_KEY
50 }
51 });
52
53 if (!response.ok) {
54 throw new Error(HTTP ${response.status});
55 }
56
57 const blob = await response.blob();
58 const url = window.URL.createObjectURL(blob);
59 const a = document.createElement('a');
60 a.href = url;
61
62 // Create safe filename
63 const safeTitle = docTitle
64 .replace(/[^a-zA-Z0-9\s-]/g, '')
65 .replace(/\s+/g, '-')
66 .toLowerCase()
67 .substring(0, 100);
68
69 a.download = ${safeTitle}.${docExtension.toLowerCase()};
70 document.body.appendChild(a);
71 a.click();
72 window.URL.revokeObjectURL(url);
73 document.body.removeChild(a);
74
75 return { success: true };
76 } catch (error) {
77 console.warn('Fetch method failed, falling back to iframe');
78 downloadDocument(docId);
79 return { success: true, method: 'fallback' };
80 }
81}
82
83// Example usage:
84// downloadAdvert(227817); // Downloads PDF advert for tender 227817
85// downloadDocument(1); // Downloads specific document
downloadAdvert(227817)
Downloads: tender-advert.pdf
<img src="/api/v1/company_logo/6" />
Displays: USAID logo (6.jpg)
Copy and paste this code to display tenders on your website:
1<!-- Complete HTML example with styling and JavaScript -->
2<!DOCTYPE html>
3<html lang="en">
4<head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>Tenders Display</title>
8 <style>
9 .tenders-container {
10 max-width: 1200px;
11 margin: 0 auto;
12 padding: 20px;
13 }
14 .tender-card {
15 background: white;
16 border-radius: 12px;
17 padding: 20px;
18 margin: 15px 0;
19 border: 1px solid #e2e8f0;
20 box-shadow: 0 2px 8px rgba(0,0,0,0.1);
21 transition: all 0.3s;
22 }
23 .tender-card:hover {
24 transform: translateY(-2px);
25 box-shadow: 0 8px 25px rgba(0,0,0,0.15);
26 }
27 .tender-title {
28 font-size: 1.1rem;
29 font-weight: 600;
30 margin-bottom: 12px;
31 color: #334155;
32 cursor: pointer;
33 }
34 </style>
35</head>
36<body>
37 <div class="tenders-container" id="tenders-container">
38 <div style="text-align: center; padding: 40px;">Loading tenders...</div>
39 </div>
40
41 <script>
42 const API_KEY = 'YOUR_API_KEY_HERE';
43 const API_BASE = 'https://api.tendersoko.africa/api/v1';
44
45 // Load tenders on page load
46 document.addEventListener('DOMContentLoaded', function() {
47 loadTenders();
48 });
49
50 async function loadTenders() {
51 try {
52 const response = await fetch(${API_BASE}/tenders, {
53 headers: {
54 'Authorization': 'Bearer ' + API_KEY
55 }
56 });
57 const data = await response.json();
58
59 if (data.status === 'OK') {
60 displayTenders(data.tenders);
61 } else {
62 showError('Failed to load tenders: ' + data.message);
63 }
64 } catch (error) {
65 showError('Network error: ' + error.message);
66 }
67 }
68
69 function displayTenders(tenders) {
70 const container = document.getElementById('tenders-container');
71 // ... rendering code ...
72 }
73 </script>
74</body>
75</html>
Enhanced integration with filtering capabilities:
1// Advanced integration with filtering
2const API_KEY = 'YOUR_API_KEY_HERE';
3const API_BASE = 'https://api.tendersoko.africa/api/v1';
4
5let countries = [];
6let categories = [];
7let sectors = [];
8
9// Initialize on page load
10document.addEventListener('DOMContentLoaded', async function() {
11 await loadReferenceData();
12 await loadTenders();
13});
14
15// Load reference data for filters
16async function loadReferenceData() {
17 try {
18 // Load countries with secure headers
19 const countriesResponse = await fetch(${API_BASE}/countries, {
20 headers: {
21 'Authorization': 'Bearer ' + API_KEY
22 }
23 });
24 const countriesData = await countriesResponse.json();
25 if (countriesData.status === true) {
26 countries = countriesData.countries;
27 populateCountryFilter();
28 }
29 } catch (error) {
30 console.error('Failed to load reference data:', error);
31 }
32}
33
34// Load tenders with filters using secure headers
35async function loadTenders(filters = {}) {
36 try {
37 const params = new URLSearchParams();
38
39 // Add filters but NOT the API key
40 if (filters.country) params.append('country', filters.country);
41 if (filters.category) params.append('category', filters.category);
42
43 const endpoint = filters.type === 'today' ? '/today' : '/tenders';
44
45 const response = await fetch(${API_BASE}${endpoint}?${params.toString()}, {
46 headers: {
47 'Authorization': 'Bearer ' + API_KEY
48 }
49 });
50 const data = await response.json();
51
52 if (data.status === 'OK') {
53 displayTenders(data.tenders);
54 }
55 } catch (error) {
56 showError('Network error: ' + error.message);
57 }
58}
Complete solution for handling document downloads:
1// Professional Document Manager for Tendersoko API
2class TendersokoDocumentManager {
3 constructor(apiKey) {
4 this.apiKey = apiKey;
5 this.apiBase = 'https://api.tendersoko.africa/api/v1';
6 }
7
8 /**
9 * Download tender advert - uses iframe method
10 */
11 downloadAdvert(tenderId) {
12 const iframe = document.createElement('iframe');
13 iframe.style.display = 'none';
14 iframe.src = `${this.apiBase}/advert_doc/${tenderId}`;
15 document.body.appendChild(iframe);
16
17 setTimeout(() => {
18 if (iframe.parentNode) {
19 document.body.removeChild(iframe);
20 }
21 }, 5000);
22
23 return { success: true };
24 }
25}
1// Node.js/Express example for secure server-side proxy
2const express = require('express');
3const axios = require('axios');
4const app = express();
5
6// Your secure endpoint that hides the API key
7app.get('/api/tenders', async (req, res) => {
8 try {
9 const response = await axios.get('https://api.tendersoko.africa/api/v1/tenders', {
10 headers: {
11 'Authorization': 'Bearer YOUR_SERVER_API_KEY'
12 },
13 params: req.query
14 });
15
16 res.json(response.data);
17 } catch (error) {
18 res.status(error.response?.status || 500).json({
19 error: 'Failed to fetch tenders'
20 });
21 }
22});