💼 freehire — Buscador de Empleo IT Open Source

Every IT job, straight from the source

Buscador de empleo open source que indexa 3.3M+ vacantes IT en vivo directamente desde las career pages y ATS de las empresas: sin recruiters, sin reposts, sin links muertos. Todo normalizado, deduplicado y buscable con facetas. MIT y self-hosteable.

  • 💻 Repositorio: github.com/strelov1/freehire
  • 🌐 Demo: freehire.me · API docs
  • 🛠️ Stack: Go + Fiber · PostgreSQL + pgvector · Meilisearch · SvelteKit 2 + Tailwind 4
  • 📜 Licencia: MIT
  • ⭐ ~2.4k estrellas · 244 forks (al 2026-10-21)

🎯 Qué es

Un motor de búsqueda de empleo que crawlea la fuente original: la página de carreras o el ATS de cada empresa. No es un agregador que repite lo que otro agregador publicó.

  • Straight from the source: Workday, Greenhouse, Lever, Ashby, iCIMS y una cola larga de ATS. Cada listing enlaza al posting original.
  • Un schema, deduplicado: el mismo rol publicado en tres boards colapsa en una sola entrada, con clave estable.
  • Búsqueda que entiende empleos: full-text con facetas por región, modalidad, seniority, skills y salario, derivadas de diccionarios curados (no adivinadas).
  • Realmente abierto: MIT, pipeline y datos en abierto, self-hosteable.
  • Construible encima: API HTTP pública, CLI, digests de Telegram y un workspace completo.

Escala del catálogo (snapshot 2026-10-20)

3,371,563 vacantes únicas · 477,323 empresas · 320 fuentes (304 con postings ese día). Los reposts entre boards se colapsan, así que la cifra refleja lo que realmente muestra freehire.me/jobs.

De dónde salen las vacantes

Tipo de fuentePlataformas con postingsVacantes abiertas
ATS platforms1172,936,676
Aggregators & job boards (incl. 97 canales de Telegram)133444,654
Company career sites (Amazon, Apple, Google, Yandex, Sber…)5223,293

FaseFunciones
FindBúsqueda facetada, colecciones curadas, búsquedas guardadas con digests por email/Telegram, boards compartidos, analytics de mercado, señal de ghost job
ApplyCV builder con templates PDF ATS-safe, scoring determinista CV↔vacante, análisis de fit con IA, tailoring que no inventa nada, tracer links, referidos
TrackBoard de aplicaciones con etapas, inbox que vincula respuestas de recruiters a su aplicación, ledger de eventos append-only, recordatorios
AskAgente in-process con cinco presets: chat, browse, profile, CV tailoring, interview rehearsal
Build onAPI pública sin API key, CLI, MCP server para Claude Desktop/Code, extensión de Chrome, ChatGPT Actions

🏗️ Stack

CapaTecnología
BackendGo + Fiber v2
Base de datosPostgreSQL + pgvector (filtros y embeddings semánticos)
Acceso a datossqlc (SQL tipado, sin ORM)
BúsquedaMeilisearch (full-text + facetas)
LLMlangchaingo sobre cualquier endpoint compatible con OpenAI
Gateway LLMBifrost (virtual keys por usuario, fallback entre providers, spend tracking)
Observabilidad LLMLangfuse
FrontendSvelteKit 2 (Svelte 5 runes) + Tailwind 4
InfraRedis (rate limit + fan-out), S3-compatible (CVs, fotos), Prometheus + Grafana
LocalDocker Compose

🚀 Quick start

Levantar todo el stack

# api + web + postgres + meilisearch + redis + s3
make up
 
# Verificar
curl localhost:8080/health
curl localhost:8080/api/v1/jobs

Si el puerto 8080 está ocupado:

HIRE_HOST_PORT=8090 make up

Migraciones y volúmenes

Las migraciones corren solas al inicializar el volumen de Postgres por primera vez. Si cambias una migración y el volumen ya existe, no se re-aplica: recrea con docker compose down -v && make up, o aplica pendientes con make migrate.

Desarrollo local

# Solo la base de datos
docker compose up -d db
 
# Servidor en el host, leyendo DATABASE_URL
make run

Copiar .env.example a .env. JWT_SECRET es obligatorio para arrancar; OAuth y credenciales LLM son opcionales (sus features quedan deshabilitadas si faltan).


🧰 Comandos

ComandoPara qué
make helpListar todos los comandos
make upBuild + start de todo el stack
make sqlcRegenerar código desde SQL (vía Docker)
make tidygo mod tidy
make psqlpsql dentro del contenedor
make reindexRebuild del índice de Meilisearch desde Postgres
make migrateAplicar migraciones a un volumen existente

⚙️ Workers

El server solo sirve la API. Todo lo demás — crawl, enriquecimiento, indexado, notificaciones — son workers standalone de una sola corrida, pensados para cron:

go run ./cmd/migrate            # migraciones pendientes (antes de deployar código nuevo)
go run ./cmd/ingest greenhouse  # crawlear un provider y hacer upsert
go run ./cmd/enrich             # drenar cola de enriquecimiento LLM
go run ./cmd/embed              # drenar cola semántica hacia pgvector
go run ./cmd/search-drain       # empujar writes al índice (cada 1-2 min)
go run ./cmd/reindex            # rebuild completo de Meilisearch
go run ./cmd/tg-ingest          # crawlear canales activos de Telegram
go run ./cmd/tg-extract         # extraer vacantes de posts con LLM
go run ./cmd/capture-apply-form # capturar formularios ATS encolados
go run ./cmd/backfill-derive    # re-derivar facetas, fingerprints y slugs

Todos necesitan DATABASE_URL y salen con código distinto de cero si fallan. Ningún worker borra jobs: el ciclo de vida solo los cierra suavemente (soft-close).


🗂️ Layout del repo

cmd/                 server + workers
migrations/          schema SQL (fuente para sqlc e initdb)
web/                 frontend SvelteKit
extension/           extensión Chrome (side panel)
design-system/       tokens y componentes compartidos
internal/            ~130 paquetes de dominio
  sources/           adapters por ATS + registry
  boardcatalog/      qué empresa se crawlea en qué ATS y con qué board id
  pipeline/          ingest: fetch → normalize → dedup → upsert
  search/            indexado y query en Meilisearch
  embed/             embeddings hacia pgvector
  ghost/             señal de "¿esta vacante es real?"
  cv/ cvedit/        CVs estructurados + render PDF
  cvmatch/           scoring determinista CV↔vacante
  assistant/         agente in-process: loop, tools, transcripts

La arquitectura completa (topología crawl-to-search, auth, notificaciones, ciclo de vida del job) está en docs/architecture.md. Cada paquete tiene su propio AGENTS.md.


🔌 API pública

Sin credenciales:

curl https://freehire.me/api/v1/jobs

Respuestas con forma consistente:

{ "data": ... }
{ "data": ..., "meta": { ... } }
{ "error": "..." }

Jobs y companies se direccionan por slug público. La referencia completa — endpoints, parámetros, auth y vocabulario de filtros — está en freehire.me/docs/api.


➕ Agregar una fuente

  • Agregar una empresa: una fila en el catálogo boards (company + provider + board), vía el formulario “contribute a board” del sitio o con go run ./cmd/add-board.
  • Agregar una plataforma ATS: un adapter nuevo en internal/ingest/sources + una línea en sources.All. Todos hablan la misma interfaz Source; el board se valida contra el registry antes del crawl.

Para la mayoría de empresas el ATS ya está soportado, así que agregarlas es solo una fila.


🧩 Frontend y extensión

  • Web: SvelteKit 2 server-rendered (adapter-node) bajo web/, consume la API same-origin.
  • Design system: paquete hermano enlazado por symlink, no copiado. Instalarlo antes de buildear web/ o extension/.
  • Extensión Chrome: side panel junto al posting; lee la página, muestra skill-coverage contra tu perfil de freehire y puede llenar el formulario. Disponible en la Chrome Web Store.

⚠️ Notas prácticas

  • No es solo un job board: es un producto full-stack con API, CLI, MCP, extensión y agente propio. Buen repo de referencia para arquitectura Go + SvelteKit.
  • LLM opcional: las features de IA dependen de configurar LLM_*; sin eso, el catálogo y la búsqueda siguen funcionando.
  • Self-host con datos: el crawl consume recursos y las fuentes pueden tener sus propios ToS. Revisar antes de correrlo a escala.
  • MCP server aparte: freehire-mcp conecta el catálogo a Claude Desktop/Code.
  • Calidad de datos: las facetas salen de diccionarios curados; si un campo no se puede derivar, se emite vacío en vez de adivinar.

🔗 Enlaces relacionados