TENDER is an autonomous UN tender search system: automated UN tender monitoring across 28 international sources with AI matching against an expert's profile. Every day it crawls the sites of UN agencies, development banks, and EU/OSCE portals, collects thousands of UN tenders, procurement notices, and competitions, discards the noise and the expired, and runs the rest past an AI that matches each opportunity against an expert's full résumé. Fitting UN bids land in a Telegram bot and a web admin panel. All on a single backend with a shared database.
Why you need a UN tender search system
What UN tenders are. These are competitive procurements and bids run by UN agencies and development banks: consultancy services, expertise, research. The notices are scattered across dozens of sites (UNGM, UNICEF, UNDP, the World Bank, and more), and manual UN tender monitoring eats hours every day.
How to find UN tenders. Instead of crawling by hand, the system collects international procurement from every platform automatically, normalizes and deduplicates it, drops the irrelevant by theme, region, language, and deadline, and lets the AI judge each vacancy by the substance of the role. The expert sees only fitting UN tenders — the ones actually worth applying to.
Participation in UN tenders. The system doesn't replace UN tender registration or application submission on the buyer's side, but it closes the first and most time-consuming stage — search and selection. For every tender you see the requirements, deadline, country, attachments (Terms of Reference), and a direct link to submit a UN tender application.
Order a UN tender search. The system is tuned to a specific expert or company: their own résumé, their own keywords, their own country list. The tender search price comes down to cheap upkeep (a few dollars a month for AI and proxies) — the exact cost depends on the number of sources and crawl frequency.
Tech stack
- Parsing: Python 3.12 + httpx + BeautifulSoup + lxml; for JS sites (Asian and African banks) — Playwright via Bright Data Scraping Browser (real Chrome, remote rendering, no local browser).
- Anti-bot: Bright Data Web Unlocker (Cloudflare/JS) + Scraping Browser (full JS rendering).
- AI: DeepSeek via an OpenAI-compatible client; Russian prompts editable from the panel.
- Backend / panel: Flask 3 + gunicorn, Jinja2 templates, custom CSS (two themes, responsive, mobile).
- Bot: aiogram 3 (long-polling, no open ports).
- Database: SQLite (isolated, no shared MySQL).
- Export: openpyxl (Excel), reportlab (PDF with Cyrillic).
- Infrastructure: nginx (own block + basic auth), systemd (4 services + timers), isolated server user that never touches other projects.
- Integrations: DeepSeek and Bright Data balance APIs, National Bank of Kazakhstan exchange rate.
Full list of implemented features
🤖 Parsers and collection
- 28 logical sources across 17 connectors under a single
Connector → Vacancyinterface: UN agencies (UNICEF, UN Women, UNFPA, IOM, UNGM, UNDP), banks (World, Asian, Islamic, African, EBRD, EABR, EFSD), EU/OSCE (TED, OSCE, ILO). - 5 protocol types under one abstraction: server HTML, JSON REST API, Oracle Recruiting Cloud (one engine for 3 agencies), POST + HTML fragment, sitemap-XML.
- JS-site rendering with a real browser (Scraping Browser) where the list is built by a client-side script.
- AI recovery controller: if a source's markup changes and the parser fails, the AI reconstructs the card's fields (grounded in the HTML, with an anti-avalanche limit).
- Resilience: one source failing doesn't stop the run; anti-throttling pauses; within-source card deduplication.
⚙️ Processing pipeline (5+ phases)
- Normalization: dates from 6 incompatible formats → ISO; countries from NUTS codes, "city, country" and official forms → name + region (reference of 249 countries, ISO 3166-1).
- Country resolution (phase 2.5): strict text heuristic, with an AI fallback when unrecognized.
- Cross-source deduplication: the same tender appears on several portals; the system merges duplicates, keeping the fullest record and enriching it from the rest.
- Two-level prefilter: STOP words (noise), CORE words (theme), territorial filter (allow-list of 249 countries), language (EN/RU only), national consultant, deadline. Every rule is editable in the panel; the journal shows all triggered rules as tags.
- Expired drop at two points: at the prefilter and again after details are loaded (banks expose the deadline only inside).
🌍 Territorial filter
- Reference of 249 countries (ISO 3166-1, RU/EN/codes + normalization).
- Editable country allow-list with search across all 249.
- Applied twice: at the prefilter (country off-list → dropped) and after the AI (AI-returned country off-list → no).
🧠 AI matching
- Full résumé in context: the expert's entire CV (69 projects, publications, education, languages; ~34,000 characters) is sent to the AI, not an excerpt.
- Judgment by the role's substance, not its title: a project may be called "Public Financial Management," but the position inside may be an environmental officer or a local consultant with a mandatory language. The AI reads the full description and rejects it.
- Structured verdict: yes/no + short cause (rule) + long cause (description) + RU/EN summary + country + Terms-of-Reference presence flag.
- Calibration proven on reference examples; full log of the AI dialogue for every opportunity.
- Editable prompts and profile from the panel, protected against breakage.
🖥 Web admin panel (13 pages)
- Dashboard: real-time server health (CPU/RAM/disk, service and DB status, automatic mode), graphical funnel of all phases, per-parser status.
- Finance: live balances (DeepSeek, Bright Data) with top-up buttons, spend by balance and by runs, monthly forecast by median, cost per run and per found opportunity.
- Selected applications + application card: full description, attachments (PDF/ToR), delivery log; manual rejection with reason + STOP word.
- Accepted, Rejected, Journal of all applications (feed + search + rule-tags with filter).
- Sources: a card for each of the 28 (what it collects, method, anti-bot, schedule).
- STOP/SIGNAL editor + Territory tab, Logs, Algorithm (with the real AI prompts), Prompt editor, Recipients, Settings.
- Manual override (reject/accept + word), favorites, highlighting of the cause's key words, column sorting.
- Export of lists to CSV/Excel/JSON/PDF (landscape, Cyrillic), respecting the active filter.
- Panel user management + password-free access via a Telegram link (token).
- Two themes (light/dark), responsive, mobile version with cards.
🤖 Telegram bot
- Menu with permanent bottom buttons: All applications · Sources · Statistics · Help.
- Automatic delivery of every new fitting application to active recipients.
- In-chat control: a "Not a fit" button that rejects the application directly in Telegram (synced with the panel).
- Files to chat: attachments (ToR/PDF) are sent as documents.
- Extended statistics (funnel, base, balances, efficiency) and a source list with collected counts.
- Password-free panel access via a personal link, if the operator enables it.
🚀 Automation and production
- Daily pipeline on a systemd timer (collection → AI → delivery), with catch-up of missed runs.
- Report scheduler — an hourly timer that checks who's due: interim reports at the chosen hour and a weekly report on Mondays, in each recipient's time zone.
- Manual run via the "Run now" button.
- 4 production services (bot, panel, pipeline, reports), isolated from the server's other projects.
💳 Financial accounting
- Two views of spend: by balance (start − current, real) and by runs (AI tokens + proxy/browser traffic).
- Efficiency: cost of a run and of a found opportunity, monthly forecast.
- Live balances via API + top-up buttons.

