search_tenders
Search Turkish government tenders from EKAP v2 portal using text, date ranges, tender types, provinces, and detailed criteria to find relevant procurement opportunities.
Instructions
Search Turkish government tenders from EKAP v2 portal.
Tender types: 1=Mal, 2=Yapım, 3=Hizmet, 4=Danışmanlık Provinces: Use plate numbers (6=Ankara, 34=İstanbul, 35=İzmir) IKN format: YEAR/NUMBER, dates: YYYY-MM-DD
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| alt_yuklenici_calistirilabilir_mi | No | Filter for subcontractor employment allowed | |
| alternatif_teklif_verilebilir_mi | No | Filter for alternative proposals allowed | |
| announcement_date_end | No | End date for announcement dates (YYYY-MM-DD format) | |
| announcement_date_filter | No | Announcement date filter type | |
| announcement_date_start | No | Start date for announcement dates (YYYY-MM-DD format) | |
| announcement_types | No | Announcement type IDs: 1=Ön İlan, 2=İhale İlanı, 3=Sonuç İlanı, etc. | |
| authority_ids | No | Authority/institution IDs to filter by | |
| avans_verilecek_mi | No | Filter for advance payment to be given | |
| cerceve_anlasmasi_mi | No | Filter for framework agreements | |
| e_eksiltme_yapilacak_mi | No | Filter for electronic auctions (Elektronik eksiltme yapılacak mı) | |
| e_ihale | No | Filter for electronic tenders (e-İhale) | |
| ekonomik_mali_yeterlilik_belgeleri_isteniyor_mu | No | Filter for economic/financial qualification documents required | |
| fiyat_disi_unsur_varmi | No | Filter for non-price factors (Fiyat dışı unsur var mı) | |
| fiyat_farki_verilecek_mi | No | Filter for price difference to be given | |
| ikn_number | No | IKN number | |
| ikn_year | No | IKN year (e.g., 2025) | |
| is_deneyimi_gosteren_belgeler_isteniyor_mu | No | Filter for work experience documents required | |
| kismi_teklif_mi | No | Filter for partial proposals (Kısmi teklif verilebilir mi) | |
| konsorsiyum_katilabilir_mi | No | Filter for consortium participation allowed | |
| limit | No | Maximum number of results to return (1-100) | |
| mesleki_teknik_yeterlilik_belgeleri_isteniyor_mu | No | Filter for professional/technical qualification documents required | |
| okas_codes | No | OKAS classification codes to filter by | |
| order_by | No | Order results by: ihaleTarihi=date, ihaleAdi=name, idareAdi=authority | ihaleTarihi |
| ortak_alim_mi | No | Filter for joint procurement (Ortak alım mı) | |
| personel_calistirilmasina_dayali_mi | No | Filter for personnel employment based tenders | |
| proposal_types | No | Proposal type IDs: 1=Götürü-Anahtar Teslimi Götürü, 2=Birim Fiyat, 3=Karma | |
| provinces | No | Province plate numbers to filter by (1-81, e.g., 6=Ankara, 34=İstanbul, 35=İzmir). Accepts integers or strings. | |
| search_in_admin_spec | No | Search in administrative specifications | |
| search_in_announcement | No | Search in tender announcement | |
| search_in_bid_form | No | Search in bid form | |
| search_in_contract_draft | No | Search in contract draft | |
| search_in_ikn | No | Search in IKN (tender reference number) | |
| search_in_location | No | Search in work location clause | |
| search_in_nature_quantity | No | Search in nature/quantity clause | |
| search_in_similar_work | No | Search in similar work clause | |
| search_in_tech_spec | No | Search in technical specifications | |
| search_in_tender_info | No | Search in tender information | |
| search_in_title | No | Search in tender title | |
| search_text | No | Text to search for in tender titles, descriptions, and specifications | |
| search_type | No | Search type: GirdigimGibi=exact match, TumKelimeler=all words | GirdigimGibi |
| skip | No | Number of results to skip for pagination | |
| sort_order | No | Sort order | desc |
| tender_date_end | No | End date for tender dates (YYYY-MM-DD format) | |
| tender_date_filter | No | Tender date filter type | |
| tender_date_start | No | Start date for tender dates (YYYY-MM-DD format) | |
| tender_methods | No | Tender method IDs to filter by | |
| tender_statuses | No | Tender status IDs to filter by | |
| tender_sub_methods | No | Tender sub-method IDs to filter by | |
| tender_types | No | Tender types: 1=Mal (Goods), 2=Yapım (Construction), 3=Hizmet (Service), 4=Danışmanlık (Consultancy) | |
| yabanci_isteklilere_izin_veriliyor_mu | No | Filter for foreign bidders allowed | |
| yerli_istekliye_fiyat_avantaji_uygulanıyor_mu | No | Filter for domestic bidder price advantage applied |
Implementation Reference
- ihale_mcp.py:37-37 (registration)The @mcp.tool decorator registers the search_tenders function as an MCP tool.@mcp.tool
- ihale_mcp.py:38-93 (schema)Input schema and type definitions for the search_tenders tool using Pydantic Annotated fields with descriptions, defaults, enums, and validation.async def search_tenders( search_text: Annotated[str, "Text to search for in tender titles, descriptions, and specifications"] = "", ikn_year: Annotated[Optional[int], "IKN year (e.g., 2025)"] = None, ikn_number: Annotated[Optional[int], "IKN number"] = None, tender_types: Annotated[List[Literal[1, 2, 3, 4]], "Tender types: 1=Mal (Goods), 2=Yapım (Construction), 3=Hizmet (Service), 4=Danışmanlık (Consultancy)"] = None, tender_date_start: Annotated[Optional[str], "Start date for tender dates (YYYY-MM-DD format)"] = None, tender_date_end: Annotated[Optional[str], "End date for tender dates (YYYY-MM-DD format)"] = None, announcement_date_start: Annotated[Optional[str], "Start date for announcement dates (YYYY-MM-DD format)"] = None, announcement_date_end: Annotated[Optional[str], "End date for announcement dates (YYYY-MM-DD format)"] = None, announcement_date_filter: Annotated[Literal["today", "date_range"], "Announcement date filter type"] = None, tender_date_filter: Annotated[Literal["from_today", "date_range"], "Tender date filter type"] = None, search_type: Annotated[Literal["GirdigimGibi", "TumKelimeler"], "Search type: GirdigimGibi=exact match, TumKelimeler=all words"] = "GirdigimGibi", order_by: Annotated[Literal["ihaleTarihi", "ihaleAdi", "idareAdi"], "Order results by: ihaleTarihi=date, ihaleAdi=name, idareAdi=authority"] = "ihaleTarihi", sort_order: Annotated[Literal["asc", "desc"], "Sort order"] = "desc", # Boolean filters e_ihale: Annotated[Optional[bool], "Filter for electronic tenders (e-İhale)"] = None, e_eksiltme_yapilacak_mi: Annotated[Optional[bool], "Filter for electronic auctions (Elektronik eksiltme yapılacak mı)"] = None, ortak_alim_mi: Annotated[Optional[bool], "Filter for joint procurement (Ortak alım mı)"] = None, kismi_teklif_mi: Annotated[Optional[bool], "Filter for partial proposals (Kısmi teklif verilebilir mi)"] = None, fiyat_disi_unsur_varmi: Annotated[Optional[bool], "Filter for non-price factors (Fiyat dışı unsur var mı)"] = None, ekonomik_mali_yeterlilik_belgeleri_isteniyor_mu: Annotated[Optional[bool], "Filter for economic/financial qualification documents required"] = None, mesleki_teknik_yeterlilik_belgeleri_isteniyor_mu: Annotated[Optional[bool], "Filter for professional/technical qualification documents required"] = None, is_deneyimi_gosteren_belgeler_isteniyor_mu: Annotated[Optional[bool], "Filter for work experience documents required"] = None, yerli_istekliye_fiyat_avantaji_uygulanıyor_mu: Annotated[Optional[bool], "Filter for domestic bidder price advantage applied"] = None, yabanci_isteklilere_izin_veriliyor_mu: Annotated[Optional[bool], "Filter for foreign bidders allowed"] = None, alternatif_teklif_verilebilir_mi: Annotated[Optional[bool], "Filter for alternative proposals allowed"] = None, konsorsiyum_katilabilir_mi: Annotated[Optional[bool], "Filter for consortium participation allowed"] = None, alt_yuklenici_calistirilabilir_mi: Annotated[Optional[bool], "Filter for subcontractor employment allowed"] = None, fiyat_farki_verilecek_mi: Annotated[Optional[bool], "Filter for price difference to be given"] = None, avans_verilecek_mi: Annotated[Optional[bool], "Filter for advance payment to be given"] = None, cerceve_anlasmasi_mi: Annotated[Optional[bool], "Filter for framework agreements"] = None, personel_calistirilmasina_dayali_mi: Annotated[Optional[bool], "Filter for personnel employment based tenders"] = None, # List filters provinces: Annotated[List[int], "Province plate numbers to filter by (1-81, e.g., 6=Ankara, 34=İstanbul, 35=İzmir)"] = None, tender_statuses: Annotated[List[int], "Tender status IDs to filter by"] = None, tender_methods: Annotated[List[int], "Tender method IDs to filter by"] = None, tender_sub_methods: Annotated[List[int], "Tender sub-method IDs to filter by"] = None, okas_codes: Annotated[List[str], "OKAS classification codes to filter by"] = None, authority_ids: Annotated[List[int], "Authority/institution IDs to filter by"] = None, proposal_types: Annotated[List[int], "Proposal type IDs: 1=Götürü-Anahtar Teslimi Götürü, 2=Birim Fiyat, 3=Karma"] = None, announcement_types: Annotated[List[int], "Announcement type IDs: 1=Ön İlan, 2=İhale İlanı, 3=Sonuç İlanı, etc."] = None, # Search scope parameters search_in_ikn: Annotated[bool, "Search in IKN (tender reference number)"] = True, search_in_title: Annotated[bool, "Search in tender title"] = True, search_in_announcement: Annotated[bool, "Search in tender announcement"] = True, search_in_tech_spec: Annotated[bool, "Search in technical specifications"] = True, search_in_admin_spec: Annotated[bool, "Search in administrative specifications"] = True, search_in_similar_work: Annotated[bool, "Search in similar work clause"] = True, search_in_location: Annotated[bool, "Search in work location clause"] = True, search_in_nature_quantity: Annotated[bool, "Search in nature/quantity clause"] = True, search_in_tender_info: Annotated[bool, "Search in tender information"] = True, search_in_contract_draft: Annotated[bool, "Search in contract draft"] = True, search_in_bid_form: Annotated[bool, "Search in bid form"] = True, limit: Annotated[int, "Maximum number of results to return (1-100)"] = 10, skip: Annotated[int, "Number of results to skip for pagination"] = 0 ) -> Dict[str, Any]:
- ihale_mcp.py:102-202 (handler)Handler logic: validates limits, handles special date filters like 'today', converts province plates to API IDs, calls EKAPClient, augments result with search params.# Validate limit if limit > 100: limit = 100 elif limit < 1: limit = 1 # Handle special date filters if announcement_date_filter == "today": today = datetime.now().strftime("%Y-%m-%d") announcement_date_start = today announcement_date_end = today if tender_date_filter == "from_today": today = datetime.now().strftime("%Y-%m-%d") tender_date_start = today tender_date_end = None # Convert plate numbers to API IDs api_province_ids = None if provinces: api_province_ids = [] for plate_number in provinces: api_id = PLATE_TO_API_ID.get(plate_number) if api_id: api_province_ids.append(api_id) # If no valid plate numbers, set to None to avoid empty filter if not api_province_ids: api_province_ids = None # Use the client to search tenders result = await ekap_client.search_tenders( search_text=search_text, ikn_year=ikn_year, ikn_number=ikn_number, tender_types=tender_types, tender_date_start=tender_date_start, tender_date_end=tender_date_end, announcement_date_start=announcement_date_start, announcement_date_end=announcement_date_end, search_type=search_type, order_by=order_by, sort_order=sort_order, # Boolean filters e_ihale=e_ihale, e_eksiltme_yapilacak_mi=e_eksiltme_yapilacak_mi, ortak_alim_mi=ortak_alim_mi, kismi_teklif_mi=kismi_teklif_mi, fiyat_disi_unsur_varmi=fiyat_disi_unsur_varmi, ekonomik_mali_yeterlilik_belgeleri_isteniyor_mu=ekonomik_mali_yeterlilik_belgeleri_isteniyor_mu, mesleki_teknik_yeterlilik_belgeleri_isteniyor_mu=mesleki_teknik_yeterlilik_belgeleri_isteniyor_mu, is_deneyimi_gosteren_belgeler_isteniyor_mu=is_deneyimi_gosteren_belgeler_isteniyor_mu, yerli_istekliye_fiyat_avantaji_uygulanıyor_mu=yerli_istekliye_fiyat_avantaji_uygulanıyor_mu, yabanci_isteklilere_izin_veriliyor_mu=yabanci_isteklilere_izin_veriliyor_mu, alternatif_teklif_verilebilir_mi=alternatif_teklif_verilebilir_mi, konsorsiyum_katilabilir_mi=konsorsiyum_katilabilir_mi, alt_yuklenici_calistirilabilir_mi=alt_yuklenici_calistirilabilir_mi, fiyat_farki_verilecek_mi=fiyat_farki_verilecek_mi, avans_verilecek_mi=avans_verilecek_mi, cerceve_anlasmasi_mi=cerceve_anlasmasi_mi, personel_calistirilmasina_dayali_mi=personel_calistirilmasina_dayali_mi, # List filters (provinces converted to API IDs) provinces=api_province_ids, tender_statuses=tender_statuses, tender_methods=tender_methods, tender_sub_methods=tender_sub_methods, okas_codes=okas_codes, authority_ids=authority_ids, proposal_types=proposal_types, announcement_types=announcement_types, # Search scope search_in_ikn=search_in_ikn, search_in_title=search_in_title, search_in_announcement=search_in_announcement, search_in_tech_spec=search_in_tech_spec, search_in_admin_spec=search_in_admin_spec, search_in_similar_work=search_in_similar_work, search_in_location=search_in_location, search_in_nature_quantity=search_in_nature_quantity, search_in_tender_info=search_in_tender_info, search_in_contract_draft=search_in_contract_draft, search_in_bid_form=search_in_bid_form, skip=skip, limit=limit ) # Add search parameters to result for logging if "search_params" not in result: result["search_params"] = { "search_text": search_text, "ikn_year": ikn_year, "ikn_number": ikn_number, "tender_types": tender_types, "date_range": { "tender_start": tender_date_start, "tender_end": tender_date_end, "announcement_start": announcement_date_start, "announcement_end": announcement_date_end } } return result
- ihale_client.py:253-442 (helper)Core helper method in EKAPClient class that constructs the detailed API payload for EKAP v2 tender search endpoint, performs the HTTP request, processes and formats the raw API response into structured tender objects, including optional document URL fetching.async def search_tenders( self, search_text: str = "", ikn_year: Optional[int] = None, ikn_number: Optional[int] = None, tender_types: List[int] = None, tender_date_start: Optional[str] = None, tender_date_end: Optional[str] = None, announcement_date_start: Optional[str] = None, announcement_date_end: Optional[str] = None, search_type: Literal["GirdigimGibi", "TumKelimeler"] = "GirdigimGibi", order_by: Literal["ihaleTarihi", "ihaleAdi", "idareAdi"] = "ihaleTarihi", sort_order: Literal["asc", "desc"] = "desc", # Boolean filters e_ihale: Optional[bool] = None, e_eksiltme_yapilacak_mi: Optional[bool] = None, ortak_alim_mi: Optional[bool] = None, kismi_teklif_mi: Optional[bool] = None, fiyat_disi_unsur_varmi: Optional[bool] = None, ekonomik_mali_yeterlilik_belgeleri_isteniyor_mu: Optional[bool] = None, mesleki_teknik_yeterlilik_belgeleri_isteniyor_mu: Optional[bool] = None, is_deneyimi_gosteren_belgeler_isteniyor_mu: Optional[bool] = None, yerli_istekliye_fiyat_avantaji_uygulanıyor_mu: Optional[bool] = None, yabanci_isteklilere_izin_veriliyor_mu: Optional[bool] = None, alternatif_teklif_verilebilir_mi: Optional[bool] = None, konsorsiyum_katilabilir_mi: Optional[bool] = None, alt_yuklenici_calistirilabilir_mi: Optional[bool] = None, fiyat_farki_verilecek_mi: Optional[bool] = None, avans_verilecek_mi: Optional[bool] = None, cerceve_anlasmasi_mi: Optional[bool] = None, personel_calistirilmasina_dayali_mi: Optional[bool] = None, # List filters provinces: List[int] = None, tender_statuses: List[int] = None, tender_methods: List[int] = None, tender_sub_methods: List[int] = None, okas_codes: List[str] = None, authority_ids: List[int] = None, proposal_types: List[int] = None, announcement_types: List[int] = None, # Search scope parameters search_in_ikn: bool = True, search_in_title: bool = True, search_in_announcement: bool = True, search_in_tech_spec: bool = True, search_in_admin_spec: bool = True, search_in_similar_work: bool = True, search_in_location: bool = True, search_in_nature_quantity: bool = True, search_in_tender_info: bool = True, search_in_contract_draft: bool = True, search_in_bid_form: bool = True, skip: int = 0, limit: int = 10 ) -> Dict[str, Any]: """Search for Turkish government tenders""" # Province filtering is now handled by the API directly # Build API request payload api_params = { "searchText": search_text, "filterType": None, "ikNdeAra": search_in_ikn, "ihaleAdindaAra": search_in_title, "ihaleIlanindaAra": search_in_announcement, "teknikSartnamedeAra": search_in_tech_spec, "idariSartnamedeAra": search_in_admin_spec, "benzerIsMaddesindeAra": search_in_similar_work, "isinYapilacagiYerMaddesindeAra": search_in_location, "nitelikTurMiktarMaddesindeAra": search_in_nature_quantity, "ihaleBilgilerindeAra": search_in_tender_info, "sozlesmeTasarisindaAra": search_in_contract_draft, "teklifCetvelindeAra": search_in_bid_form, "searchType": search_type, "iknYili": ikn_year, "iknSayi": ikn_number, "ihaleTarihSaatBaslangic": self._format_date_for_api(tender_date_start), "ihaleTarihSaatBitis": self._format_date_for_api(tender_date_end), "ilanTarihSaatBaslangic": self._format_date_for_api(announcement_date_start), "ilanTarihSaatBitis": self._format_date_for_api(announcement_date_end), "yasaKapsami4734List": [], "ihaleTuruIdList": tender_types or [], "ihaleUsulIdList": tender_methods or [], "ihaleUsulAltIdList": tender_sub_methods or [], "ihaleIlIdList": provinces or [], "ihaleDurumIdList": tender_statuses or [], "idareIdList": authority_ids or [], "ihaleIlanTuruIdList": announcement_types or [], "teklifTuruIdList": proposal_types or [], "asiriDusukTeklifIdList": [], "istisnaMaddeIdList": [], "okasBransKodList": okas_codes or [], "okasBransAdiList": [], "titubbKodList": [], "gmdnKodList": [], # Boolean filters "eIhale": e_ihale, "eEksiltmeYapilacakMi": e_eksiltme_yapilacak_mi, "ortakAlimMi": ortak_alim_mi, "kismiTeklifMi": kismi_teklif_mi, "fiyatDisiUnsurVarmi": fiyat_disi_unsur_varmi, "ekonomikVeMaliYeterlilikBelgeleriIsteniyorMu": ekonomik_mali_yeterlilik_belgeleri_isteniyor_mu, "meslekiTeknikYeterlilikBelgeleriIsteniyorMu": mesleki_teknik_yeterlilik_belgeleri_isteniyor_mu, "isDeneyimiGosterenBelgelerIsteniyorMu": is_deneyimi_gosteren_belgeler_isteniyor_mu, "yerliIstekliyeFiyatAvantajiUgulaniyorMu": yerli_istekliye_fiyat_avantaji_uygulanıyor_mu, "yabanciIsteklilereIzinVeriliyorMu": yabanci_isteklilere_izin_veriliyor_mu, "alternatifTeklifVerilebilirMi": alternatif_teklif_verilebilir_mi, "konsorsiyumKatilabilirMi": konsorsiyum_katilabilir_mi, "altYukleniciCalistirilabilirMi": alt_yuklenici_calistirilabilir_mi, "fiyatFarkiVerilecekMi": fiyat_farki_verilecek_mi, "avansVerilecekMi": avans_verilecek_mi, "cerceveAnlasmaMi": cerceve_anlasmasi_mi, "personelCalistirilmasinaDayaliMi": personel_calistirilmasina_dayali_mi, "orderBy": order_by, "siralamaTipi": sort_order, "paginationSkip": skip, "paginationTake": limit } try: # Make API request response_data = await self._make_request(self.tender_endpoint, api_params) # Parse and format the response tenders = response_data.get("list", []) total_count = response_data.get("totalCount", 0) # Province filtering is now handled by the API directly # Format each tender for better readability formatted_tenders = [] for tender in tenders: tender_id = tender.get("id") # Get document URL for this tender document_url = None if tender_id and tender.get("dokumanSayisi", 0) > 0: try: doc_result = await self.get_tender_document_url(tender_id) if doc_result.get("success"): document_url = doc_result.get("document_url") except Exception: # If document URL fails, continue without it pass formatted_tender = { "id": tender_id, "name": tender.get("ihaleAdi"), "ikn": tender.get("ikn"), "type": { "code": tender.get("ihaleTip"), "description": tender.get("ihaleTipAciklama") }, "method": tender.get("ihaleUsulAciklama"), "status": { "code": tender.get("ihaleDurum"), "description": tender.get("ihaleDurumAciklama") }, "authority": tender.get("idareAdi"), "province": tender.get("ihaleIlAdi"), "tender_datetime": tender.get("ihaleTarihSaat"), "document_count": tender.get("dokumanSayisi", 0), "has_announcement": tender.get("ilanVarMi", False), "document_url": document_url } formatted_tenders.append(formatted_tender) result = { "tenders": formatted_tenders, "total_count": total_count, "returned_count": len(formatted_tenders) } # Province filtering is now handled by the API directly return result except httpx.HTTPStatusError as e: return { "error": f"API request failed with status {e.response.status_code}", "message": str(e) } except Exception as e: return { "error": "Request failed", "message": str(e) }