01Busca Vagas
Tech jobs in Goiânia and remote ones, in one place.
A crawler reads nine job boards, merges the same job posted in different places and makes everything searchable, with filters and a link to the original posting.
No AI, on purpose: clear rules, explainable results and zero model cost.
- Next.js
- Fastify
- PostgreSQL
- GSAP
- Vitest


The problem
Anyone looking for a tech job in Goiânia opens five sites a day and finds the same posting repeated on three of them. Remote jobs are scattered across a handful more.
What I built
- Automatic collection from nine job boards, each with its own schedule and pace.
- The same job posted in different places becomes a single entry.
- Portuguese full-text search that handles accents, with filters for category, salary, work model, contract, seniority and city.
- Coverage of Goiânia and the 21 cities of its metro area, plus remote jobs from all over Brazil.
- User accounts with saved jobs, a résumé builder with six templates and a score for how applicant tracking systems read it, and an admin panel.
The screens
Screenshots of the real system. Click a screen to see it full size.

Search: filters by location and area, and every job with its source, date and a link to the original posting.

Job page: every detail in one place, with a warning when a posting has been up for too long.

Market: open jobs, companies, new jobs this week and median salary, recalculated every 10 minutes.

Where the jobs are and the most requested technologies, counted in the title and description.

Pay by area: the middle range and the median, only where there are enough salaries.

Employee or contractor calculator: what you actually keep in each case, using this year's tax tables.
How it works
Collect
A worker reads each source on its own schedule and writes what it found to a queue inside PostgreSQL itself.
Clean up
Each job goes through classification rules editable in the admin panel and is compared with existing ones to avoid duplicates.
Search
The Fastify API answers with Portuguese full-text search, filters and pagination.
Site
The Next.js site shows everything, and the button takes you straight to the original posting.
Decisions that mattered
No AI
Why: Classifying a job is a rule, not an opinion. Written rules are predictable, testable and cost nothing per job.
What to collect is data, not code
Why: Source, search term, city and field live in tables. Taking the site to another city or field means adding rows, not writing code.
A queue inside PostgreSQL
Why: One less database to look after. The same pattern had already proven itself in another system I built.
Site and API on the same origin
Why: No CORS, and the session cookie stays protected. nginx splits the API path from the rest.
What went wrong
Every real system breaks somehow. These were the stumbles that taught the most, and what changed because of them.
Collection kept running, and new jobs stopped coming in
What happened
In September, the intake of new jobs dropped to almost nothing without a single error. Every collection finished fine: the saved searches were just rereading the same set over and over.
What changed
I started watching what comes in, not just whether it ran. Today there is an alert when the day's new jobs fall below a quarter of the weekly average, and each search shows how much of what it reads it actually keeps.
A page that didn't exist said it did
What happened
A removed job showed the not-found screen, but the server answered 200. To Google that is content, and on a job site, where postings expire all the time, search fills up with dead pages.
What changed
The loading screen that caused it now lives only where it's needed. And after touching a route, I check the status code, not just the screen.
A new field broke every older résumé
What happened
I added certifications to the résumé and made the field required. Every résumé generated before that started returning a 500 error, and the tests passed because they only created new ones.
What changed
A new field in stored data gets filled in when it's read. And there is a test that writes the old format by hand and requires it to open.
Where it stands
Live since September 2026, updating itself.