# Gupy Jobs Scraper: Brazil Job Listings (`nanoscrape/gupy-jobs-scraper`) Actor

Scrape Brazilian job listings from the Gupy job portal. Search by keyword, state, city and work mode and get title, company, location, job type, dates, full description and job link. Listings are in Portuguese.

- **URL**: https://apify.com/nanoscrape/gupy-jobs-scraper.md
- **Developed by:** [NanoScrape](https://apify.com/nanoscrape) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Gupy Jobs Scraper: Job Listings in Brazil

Get job listings from [Gupy](https://portal.gupy.io/), Brazil's largest job portal for company career pages, as clean dataset rows. The listings are Brazilian jobs and are written in Portuguese. Search by keyword and narrow the search by state, city and work mode (remote, hybrid, on-site). Each row has the title, company, location, job type, publication date, deadline, the full description and the link to the job page.

### What you can do with it

- Build a list of companies that are hiring in Brazil, by city, state or role, for recruiting, sales or market research.
- Track the Brazilian job market over time: run it on a schedule and watch new jobs, work modes and job types.
- Feed a job board, newsletter or alert service with current Gupy jobs.
- Count open jobs per keyword, state or company type (internship, trainee, apprentice, full-time employee).

### Input

| Field | Type | Description |
|---|---|---|
| `searchQueries` | string\[] | Keywords or job titles, one search each (default `python`). Portuguese keywords find the most jobs |
| `state` | string | Brazilian state, for example `São Paulo`. Combined with every keyword |
| `city` | string | City, for example `Belo Horizonte`. Combined with every keyword |
| `workplaceType` | string | `remote`, `hybrid` or `on-site`. Portuguese words (`remoto`, `híbrido`, `presencial`) are accepted |
| `includeDetails` | boolean | Open each job page for extra fields (default `false`) |
| `maxResultsPerQuery` | integer | Cap per keyword, 1 to 1000 (default 50) |
| `maxResults` | integer | Cap for the whole run (0 = no limit) |

Without keywords but with a state, city or work mode, the actor runs one search for that filter alone. With no input at all it runs the sample below.

#### Example

```json
{"searchQueries": ["python"], "maxResults": 20}
```

### What you get

One dataset row per job. A job found by several keywords is returned once, and duplicates are removed before anything is charged, so the number of rows is the number of results you pay for. Missing values are `null`, never an empty text.

| Field | Description |
|-------|-------------|
| `id` | Gupy job id. The dedup key |
| `title` | Job title |
| `company_name`, `company_logo_url` | Hiring company and its logo |
| `company_id`, `career_page_id` | Gupy ids of the company and its career page. They are read from the logo address of the listing, so they are filled without `includeDetails`. Null when the company uses a default logo |
| `company_career_page_url`, `company_subdomain` | The company's career page on gupy.io |
| `job_type`, `job_type_raw`, `job_type_pt` | Readable job type (Full-time employee (CLT), Internship, Apprentice (Jovem Aprendiz), Trainee program, Temporary, Freelancer, Talent pool, Self-employed, Lecturer and more), the raw Gupy code (`vacancy_type_effective`) and the Portuguese label (Efetivo) |
| `workplace_type`, `is_remote` | `remote`, `hybrid` or `on-site`, and a remote flag |
| `location`, `city`, `state` | Where the job is. City and state are empty (`null`) for remote jobs |
| `published_at`, `application_deadline` | Publication time and last day to apply (ISO 8601) |
| `description`, `description_text` | Full job description as HTML and as plain text |
| `open_to_disabilities`, `badge_pwd`, `badge_friendly` | Open to people with disabilities (PcD), Gupy PcD badge, Gupy feedback badge. The badges are `null` when the listing carries none |
| `job_url` | Job page on the company career page. Apply there |
| `search_query`, `source`, `scraped_at` | Keyword that found the job (`null` for a search without keyword), source site, scrape time (UTC) |

With `includeDetails` on, each job page is read and these fields are filled as well (otherwise `null`, or an empty list):

| Field | Description |
|-------|-------------|
| `address`, `state_code`, `country`, `country_code` | Street address as published, state code (MG), country (Brasil, BR) |
| `employment_type` | schema.org employment type (FULL_TIME, ...) |
| `expires_at` | Date the posting expires |
| `requirements`, `requirements_text` | Requirements and qualifications (HTML and text) |
| `responsibilities`, `responsibilities_text` | Responsibilities and assignments (HTML and text) |
| `additional_info`, `additional_info_text` | Additional information block (HTML and text): workplace, hours, salary and benefits when the company writes them |
| `salary_text` | The salary as the company wrote it (for example `R$ 4.000,00`), only when the posting has a Salário or Remuneração line. Gupy has no structured salary and nothing is estimated |
| `benefits` | Benefits listed under a Benefícios heading, as a list |
| `selection_steps` | Stages of the hiring process (name, order, type, category) |
| `job_code`, `status`, `opening_reason`, `quick_apply`, `publication_type`, `is_internal_mobility` | Posting data from the job page |
| `company_about`, `company_about_text`, `company_website`, `company_linkedin`, `company_instagram`, `company_banner_url`, `company_timezone` | Company profile from the career page |
| `details_fetched` | `true` when the job page was read for the row. A job page that cannot be read (for example because the job was closed) still gives a row from the search data |

Arrays are always arrays (`[]` when empty). Reading job pages makes the run slower, because each job is one more request. The search already returns the full description, so turn `includeDetails` on only if you need the extra fields.

### Sample output

```json
{
  "id": "12843282",
  "title": "DESENVOLVEDOR PYTHON",
  "company_name": "Grupo Roma Brasil",
  "company_id": 40198,
  "career_page_id": 85814,
  "company_logo_url": "https://attachments.gupy.io/production/companies/40198/career/85814/images/2022-08-19_12-02_logo.png",
  "company_career_page_url": "https://gruporomabrasil.gupy.io",
  "company_subdomain": "gruporomabrasil",
  "company_about": "<p class=\"ql-align-justify\"><span style=\"color: rgb(58, 58, 58);\">Somos uma das maiores empresas do Brasil que...",
  "company_about_text": "Somos uma das maiores empresas do Brasil que atua no segmento automotivo!\nCom uma história de mais de 40 anos,...",
  "company_website": "https://www.gruporomabrasil.com.br/",
  "company_linkedin": "https://www.linkedin.com/company/grupo-roma-brasil/mycompany/?viewAsMember=true",
  "company_instagram": "https://www.instagram.com/gruporomabrasil/",
  "company_banner_url": "https://attachments.gupy.io/production/companies/40198/career/85814/images/2026-02-12_15-48_bannerUrl.png",
  "company_timezone": "America/Sao_Paulo",
  "job_type": "Full-time employee (CLT)",
  "job_type_raw": "vacancy_type_effective",
  "job_type_pt": "Efetivo",
  "employment_type": "FULL_TIME",
  "workplace_type": "on-site",
  "is_remote": false,
  "location": "Belo Horizonte, Minas Gerais",
  "city": "Belo Horizonte",
  "state": "Minas Gerais",
  "state_code": "MG",
  "country": "Brasil",
  "country_code": "BR",
  "address": "Avenida do Contorno, Belo Horizonte, Minas Gerais, Brasil, 30110-043",
  "published_at": "2026-11-05T18:48:01.505Z",
  "application_deadline": "2027-01-31T00:00:00.000Z",
  "expires_at": "2027-01-25",
  "description": "Você é apaixonado pela área de Tecnologia da Informação?Gostaria de trabalhar em uma grande empresa?Então venh...",
  "description_text": "Você é apaixonado pela área de Tecnologia da Informação?Gostaria de trabalhar em uma grande empresa?Então venh...",
  "requirements": "<ul><li>Ensino Superior completo ou cursando em&nbsp;<span style=\"color: rgb(0, 0, 0);\">Ciências da Computação...",
  "requirements_text": "- Ensino Superior completo ou cursando em Ciências da Computação ou Sistemas de informação;\n- Experiência prát...",
  "responsibilities": "<ul><li><p></p>Desenvolver scripts e aplicações utilizando Python para automação e otimização de processos int...",
  "responsibilities_text": "- Desenvolver scripts e aplicações utilizando Python para automação e otimização de processos internos ;\n- Uti...",
  "additional_info": "<p class=\"ql-align-justify\"><strong>Local de trabalho: </strong>Belo Horizonte/MG</p><p class=\"ql-align-justif...",
  "additional_info_text": "Local de trabalho: Belo Horizonte/MG\nHorário: Segunda a Sexta-feira de 08h as 18h.\nSalário: R$ 4.000,00\nBenefí...",
  "salary_text": "R$ 4.000,00",
  "benefits": [
    "Vale-transporte",
    "Vale-refeição",
    "Plano de saúde"
  ],
  "selection_steps": [
    {
      "name": "Cadastro",
      "order": 0,
      "type": "register",
      "category": "registration"
    },
    {
      "name": "Entrevista com R.H",
      "order": 2,
      "type": "offline",
      "category": "interview"
    }
  ],
  "job_code": "40198-12843282",
  "status": "published",
  "opening_reason": "staff_increase",
  "quick_apply": false,
  "publication_type": "external",
  "is_internal_mobility": false,
  "open_to_disabilities": true,
  "badge_pwd": true,
  "badge_friendly": true,
  "job_url": "https://gruporomabrasil.gupy.io/job/eyJqb2JJZCI6MTI4NDMyODIsInNvdXJjZSI6Imd1cHlfcG9ydGFsIn0=?jobBoardSource=gupy_portal",
  "details_fetched": true,
  "search_query": "python",
  "source": "portal.gupy.io",
  "scraped_at": "2026-11-06T09:00:00Z"
}
```

The sample is a run with `includeDetails` on, shortened. Without it the detail fields are `null`.

### Pricing (Pay-per-event)

Price: $0.001 start fee per run, then $0.002 per job result ($2.00 per 1,000 jobs).

The start fee is charged on top of the results. Duplicates are never charged. Set a maximum total charge on the run to stop at a budget: the run ends cleanly when it is reached, and the status message says how many jobs were delivered. The pricing box on the actor page always shows the current rates.

### FAQ

**Are the listings in English?**
No. Gupy is a Brazilian platform and the job texts are written in Portuguese. Field names and the readable job type are in English. Use Portuguese keywords (for example `desenvolvedor`, `enfermeiro`, `analista de dados`) to find the most jobs.

**How many jobs can one keyword return?**
Up to 1000 per keyword (default 50). The portal returns 10 jobs per page and the actor reads as many pages as the limit needs. For more jobs, split the search by state or city.

**What happens when nothing matches?**
The run ends with the status `0 jobs found for <keywords>` and exit code 0. If the portal blocks or fails every search request, the run fails with a clear status instead, so an empty dataset is never mistaken for a quiet market.

**Does it find salaries?**
Gupy has no salary field. When a company writes the salary in the posting, `salary_text` shows that line (needs `includeDetails`). Many postings state no salary.

You are responsible for using the data lawfully, including the LGPD and the GDPR, national data-protection laws and unfair-competition rules for marketing and outreach.

# Changelog

This Actor's version history is a separate document: https://apify.com/nanoscrape/gupy-jobs-scraper/changelog.md

# Actor input Schema

## `searchQueries` (type: `array`):

Job titles or keywords, for example python, enfermeiro or analista de dados. Portuguese keywords find the most jobs. Each keyword runs as its own search; jobs found by several searches are returned once. Leave empty to search by state, city or work mode only.

## `state` (type: `string`):

Brazilian state, for example São Paulo or Minas Gerais. Combined with every keyword. Leave empty for all of Brazil.

## `city` (type: `string`):

City, for example Belo Horizonte. Combined with every keyword. Leave empty for all cities.

## `workplaceType` (type: `string`):

Only jobs with this work mode. Leave empty for all.

## `includeDetails` (type: `boolean`):

Open each job page for extra fields: address, country, requirements, responsibilities, benefits, salary text, hiring steps, company links and more. Slower. The search already returns the full description.

## `maxResultsPerQuery` (type: `integer`):

Maximum number of jobs per keyword (1 to 1000). Empty or 0 uses 50.

## `maxResults` (type: `integer`):

Maximum number of jobs in the dataset across all keywords. 0 = no limit.

## Actor input object example

```json
{
  "searchQueries": [
    "python"
  ],
  "includeDetails": false,
  "maxResultsPerQuery": 50,
  "maxResults": 100
}
```

# Actor output Schema

## `jobs` (type: `string`):

Dataset with the scraped Gupy jobs.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "python"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nanoscrape/gupy-jobs-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "searchQueries": ["python"] }

# Run the Actor and wait for it to finish
run = client.actor("nanoscrape/gupy-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "python"
  ]
}' |
apify call nanoscrape/gupy-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nanoscrape/gupy-jobs-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/PgqIGbgtWIugJ18da/builds/HCSZyAukMu78zVZiY/openapi.json
