2026
Radar de Licitações

Radar de Licitações

Small businesses miss public tenders because nobody has time to check the portal every day, and the existing tools are expensive. Radar ingests the PNCP public API (about 5,000 tenders a day), indexes them with Portuguese full-text search, and sends each company one daily digest with only the new tenders that match its keywords, sector, region, and budget range. For any tender, Claude reads the PDF and extracts deadlines, qualification documents, small-business exclusivity, and red flags.

Solo builder: product, backend, frontend, and infrastructure

Next.jsPostgreSQLClaude API

Features

  • Daily ingestion of every competitive procurement type, with pagination, exponential backoff, Retry-After handling, and idempotent upserts.
  • Accent-insensitive Portuguese full-text search with weighted ranking (Postgres unaccent + tsvector + GIN).
  • Alerts with sector presets mapped to CNAE codes, region, procurement type, and value range; each tender is sent once per alert.
  • AI tender summary with structured output validated by Zod, cached per tender, with a daily quota per user.
  • Passwordless magic-link login with single-use hashed tokens and HMAC-signed sessions.
  • 33 unit and integration tests against a real Postgres, CI on GitHub Actions, and a Docker Compose stack that runs in about 170 MB of RAM.

Technologies used

  • Next.js 16 — App Router, Server Actions, and Partial Prerendering.
  • PostgreSQL — Full-text search, idempotent upserts via jsonb_to_recordset.
  • Claude API — PDF reading with structured outputs.
  • PNCP API — Brazil's national public procurement portal.
  • Docker Compose — Web, jobs scheduler, and database containers.
  • Vitest — Unit and Postgres integration tests.

Build steps

  1. Clone the repository

    git clone https://github.com/giovaniohira/radar-licitacoes && cd radar-licitacoes
  2. Start Postgres and migrate

    docker run -d -p 5544:5432 -e POSTGRES_USER=radar -e POSTGRES_PASSWORD=radar -e POSTGRES_DB=radar postgres:17-alpine
    cp .env.example .env.local && npm install && npm run db:migrate
  3. Ingest one day and run

    npm run ingest -- 2026-10-15
    npm run dev

Share this project