Brazil Tech Jobs Scraper - Stack, Seniority, Salary avatar

Brazil Tech Jobs Scraper - Stack, Seniority, Salary

Pricing

from $1.70 / 1,000 job listing delivereds

Go to Apify Store
Brazil Tech Jobs Scraper - Stack, Seniority, Salary

Brazil Tech Jobs Scraper - Stack, Seniority, Salary

Brazilian tech jobs from Programathor and Remotar with stack, seniority, salary band, contract type (PJ, CLT), work model (remote, hybrid, on site) and English requirement read from the posting, no AI. Filter by any of them. No login. US$ 2 per 1,000 jobs; failed items are free.

Pricing

from $1.70 / 1,000 job listing delivereds

Rating

0.0

(0)

Developer

Scrapewise Data

Scrapewise Data

Maintained by Community

Actor stats

0

Bookmarked

4

Total users

4

Monthly active users

8 days ago

Last modified

Categories

Share

Returns open software jobs posted in Brazil, one row per job, with the job title (vaga), the hiring company (empresa), the technology stack as a list (stack), the seniority (senioridade), a remote flag (remoto), the salary band in reais (faixa) and the link to the posting (url). Every job also carries its contract type (contractType: PJ, CLT, internship...), its work model (workModel: remote, hybrid, on site) and whether English is required (englishRequired), read from explicit mentions, no AI.

Built for companies hiring developers in Brazil, recruiters building shortlists by stack, analysts benchmarking Brazilian salaries and nearshore capacity, and developers tracking demand for a technology. AI agents can call it through the Apify MCP server.

Sources

  • Programathor (default): a Brazilian board with technology roles only. Every card already carries stack tags, seniority, contract, salary band and location. The listing is plain server-rendered HTML, read with HTTP requests, up to 60 pages per run. It keeps a few hundred open jobs plus an archive of expired ones.
  • Remotar (optional, sources): a Brazilian board of remote and hybrid jobs. The Actor reads only its technology categories (programming, mobile, DevOps, sysadmin, QA, data, UX/UI), newest first: over 5,000 open jobs on 2026-10-09. Remotar has no stack tags, so stack is built from the technologies named in the title, description and requirements. Remotar rows also carry postedAt and applyUrl.

With both boards on, pages are read in turns, maxItems is shared, and a job found on both (same title, same company) is delivered once.

At a glance

Apify planPer 1,000 jobs
FreeUS$ 2.00
BronzeUS$ 1.90
SilverUS$ 1.80
Gold and aboveUS$ 1.70

One event, "Job listing delivered", charged once per job added to the dataset. No monthly fee, no start fee, proxy included. 30 jobs cost US$ 0.06.

Not charged: expired postings (skipped by default), jobs removed by your filters, the same job found on both boards or twice in one run, jobs already delivered when onlyNewJobs is on, and a run that fails.

Input examples

Every field is optional; an empty input returns the newest 30 open jobs from Programathor. The input form describes each field.

Python jobs, remote only:

{
"stack": ["python", "django"],
"remoto": true,
"maxItems": 50
}

Both boards, remote PJ jobs only:

{
"sources": ["programathor", "remotar"],
"contractTypes": ["pj"],
"workModels": ["remote"],
"maxItems": 50
}

A daily job alert: turn on onlyNewJobs, save the input as a Task and attach a daily Schedule. Each run then delivers, and charges, only jobs not delivered before with the same filters. A day with nothing new costs nothing.

{
"stack": ["kubernetes"],
"senioridade": "senior",
"onlyNewJobs": true,
"maxItems": 50
}

stack keeps a job when any of your terms appears in its stack as a substring, so java also matches JavaScript.

Output example

A real Remotar row from a run with both boards, contractTypes ["pj"] and workModels ["remote"] on 2026-10-09 (run RL03ERsZQjhxL3vl4, build 0.1.13):

{
"fonte": "remotar",
"vaga": "Senior Backend Developer",
"empresa": "Quartile",
"stack": ["Python", "Flask", "C#", ".NET", "SQL", "SQL Server", "Azure", "Git"],
"senioridade": "senior",
"remoto": true,
"faixa": null,
"presencial_somente": false,
"vencida": false,
"url": "https://remotar.com.br/job/165752",
"coletado_em": "2026-10-09T03:18:05.884186+00:00",
"url_origem": "https://remotar.com.br/job/165752",
"location": "Remoto",
"postedAt": "2026-10-07T21:06:23.590-03:00",
"applyUrl": "https://ats.rippling.com/pt-BR/careers-quartile/jobs/65ea83ce-24d2-433d-90d6-cabe570fdce1?utm_source=remotar",
"contractType": "pj",
"contractTypesFound": ["pj"],
"workModel": "remote",
"englishRequired": true,
"classificationEvidence": {
"contractType": "tag: pj",
"workModel": "tag: 100% remoto",
"englishRequired": "excellent english communication skills (fl"
}
}

Output fields

Fill rates from a real run of 80 Programathor rows on 2026-09-11 (run X7aOuRosJlRffBdhf).

FieldWhat it holdsFilled
fonteprogramathor or remotar100%
vagajob title as posted100%
empresahiring company100%
stacktechnologies, as a list100%
senioridadeestagio, junior, pleno or senior; null when no level is named100%
remototrue when the location says remote100%
faixamonthly salary band in reais, as text ("Até R$5.000", "Acima de R$18.000")16% to 39%
presencial_somentetrue when the card carries the on-site only badge100%
vencidatrue when the posting is in the expired archive100%
url, url_origemlink to the posting100%
coletado_emISO 8601 collection time100%
contractTypepj, clt, internship, temporary, freelance, multiple or unknownsee below
contractTypesFoundevery contract type stated, e.g. ["pj", "clt"]when stated
workModelremote, hybrid, onsite or unknownsee below
englishRequiredtrue when English is stated as a requirement100%
classificationEvidencethe text each of the three values above came from100%
locationlocation line as published100%
postedAt, applyUrlposting date and the company's application page; Remotar onlyRemotar 100%

The original field names are in Portuguese because the sources are Brazilian; the fields added on 2026-10-09 are in English. "Pleno" sits between junior and senior, closest to mid level. Brazilian companies are not required to publish pay, so faixa is often null and is a band, never a number.

What it fixes

Brazilian job postings leave contract, work model and English in free text, and a keyword search for "PJ" or "remoto" returns banking products, benefits and hybrid cloud jobs. This Actor reads those three values only from an explicit mention, in this order:

  1. The job title: "Desenvolvedor Python (PJ, 100% remoto)", "Estágio em Dados".
  2. A field of the board: the contract and location lines of a Programathor card, the CLT, PJ, internship, remote, hybrid and English labels of a Remotar job.
  3. The description and requirements (Remotar): "Regime: PJ", "Contratação: CLT", "modelo híbrido", "presencial 3x por semana" (read as hybrid), "inglês avançado".

The first place with an answer decides. "CLT ou PJ" gives multiple, and the contractTypes filter matches either. Deliberately not read: "conta PJ", "crédito PJ", "clientes pessoa jurídica", "legislação trabalhista (CLT)", "auxílio home office", "ambientes híbridos". English that is "um diferencial" or "desejável" gives englishRequired: false. Nothing explicit means unknown, never a guess.

Measured on 2026-10-09: Programathor contract and work model filled in 26 of 26 open jobs; on Remotar, work model in 200 of 200 and contract in 89 of 200 (the rest never stated it). A hand check of 60 jobs against the full posting found no wrong value.

Other fixes shipped before release: the seniority output uses the same values as the input filter, so senior matches; expired postings are skipped instead of charged; badges printed inside the title moved into vencida and presencial_somente.

Speed and reliability

Real runs on Apify, HTTP requests only, through the datacenter proxy:

InputRowsTime
Default, maxItems 505010 s
stack python, open jobs only, maxItems 200126 s
stack python, expired included, maxItems 808024 s

The 12 rows in the second run were every open Python job that day: maxItems is a ceiling, not a promise of volume.

Use it from code and AI agents

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("scrapewise/vagas-tech-br").call(run_input={"stack": ["python"], "senioridade": "senior"})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
print(job["vaga"], job["empresa"], job.get("faixa"), job["url"])

The same works from Node.js, the HTTP API (run-sync-get-dataset-items), n8n, Make and Zapier. For AI agents, add the Apify MCP server with this Actor as a tool: https://mcp.apify.com/?tools=scrapewise/vagas-tech-br.

FAQ

What does "vaga" mean? It is Portuguese for a job opening; "vagas tech" is how people in Brazil search for tech jobs.

Why did my run return fewer rows than maxItems? The boards had fewer open jobs matching your filters.

Why does a run with no results fail? On purpose: an empty dataset reported as success hides a schedule that stopped working. The message says whether the filters were too narrow or the layout changed.

What about expired postings? Skipped and not charged by default. With incluirVencidas on they are delivered with vencida: true and charged like any other row.

Does it collect personal data? No. It reads public job postings without login; every field is about a job or a company, never a candidate or recruiter.

Other Brazilian job boards? scrapewise/gupy-vagas and scrapewise/catho-vagas cover general jobs. Open an issue if you need another tech board here.

Limitations

  • Two sources, not the whole Brazilian market. Remotar covers remote and hybrid jobs only.
  • location is the published text; there is no city filter.
  • Remotar stack comes from technologies named in the text, from a fixed list of about 85 plus your own stack terms.
  • No job description or contact details in the row; url opens the full posting.
  • Up to 60 listing pages per run.

Errors

  • The source answers anything other than HTTP 200, or the card layout stops matching: the run fails with a message naming the problem.
  • Zero rows collected: the run fails instead of finishing with an empty dataset.
  • Something broke or a field is missing? Open an issue on the Issues tab.

Changelog

  • 2026-10-09: Remotar as an optional second source, with cross-board deduplication, postedAt and applyUrl. Every row now has contractType, workModel, englishRequired, location and classificationEvidence, plus the contractTypes, workModels and keywords filters. Existing inputs return the same jobs and charges.
  • 2026-10-04: onlyNewJobs for scheduled job alerts.
  • 2026-09-14: Discounts by plan; every input field got a default, so an empty API call runs.