# Sólides Vagas Brazil Jobs Scraper (`jpopendata/brazil-jobs-solides`) Actor

Brazil job postings from Sólides Vagas (vagas.solides.com.br): title, employer, city/state, salary in BRL when shown, CLT/PJ/internship, on-site/remote/hybrid, seniority, area, posted date. Keyword and location filters. No personal data. Unofficial; not affiliated with Sólides.

- **URL**: https://apify.com/jpopendata/brazil-jobs-solides.md
- **Developed by:** [JP Open Data](https://apify.com/jpopendata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 per results

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

## Sólides Vagas Brazil Jobs Scraper

**Job postings from Sólides Vagas (vagas.solides.com.br), one of Brazil's large job boards with about 70,000 open positions: title, employer, city and state, salary in BRL when the company shows it, contract type (CLT, PJ, estágio...), on-site/remote/hybrid, seniority, area and posted date, in a clean English schema.**

Search by job title in Portuguese, optionally narrowed by state or city, workplace type, contract type, seniority and occupation area. Every filter is the site's own search filter, so you get exactly what a visitor sees on vagas.solides.com.br.

> **Unofficial tool. Not affiliated with or endorsed by Sólides.** It reads public list pages only (no login), politely (strictly serial requests at least 1.5 s apart, a hard per-run request budget, no block evasion), requests only URLs the site's robots.txt allows, and extracts **job facts only — no personal data** (see "Privacy" below). You are responsible for making sure your use of the data complies with the site's terms and applicable law (including the LGPD).

***

### Quick start — verified input

This input was run successfully on 2026-10-04 (14 jobs per keyword, 2 requests, about 5 seconds, no proxy). Running with an empty input `{}` uses exactly these values.

```json
{
  "keywords": ["vendedor", "auxiliar administrativo"],
  "maxItemsPerKeyword": 14
}
```

More examples:

```json
{
  "keywords": ["analista de dados", "desenvolvedor", "enfermeiro"],
  "locations": ["SP", "Belo Horizonte - MG"],
  "workplaceType": "hibrido",
  "contractTypes": ["clt"],
  "maxItemsPerKeyword": 50,
  "maxApiRequests": 20
}
```

Typical Portuguese search terms: `vendedor`, `auxiliar administrativo`, `assistente financeiro`, `analista de RH`, `técnico de enfermagem`, `motorista`, `estágio`, `jovem aprendiz`, `operador de caixa`, `recepcionista`. Use `"*"` to list all jobs.

#### Common input mistakes

| Wrong | Right | Why |
|---|---|---|
| `"keywords": ["sales person"]` | `"keywords": ["vendedor"]` | Postings are in Portuguese; English titles match only a few. |
| `"locations": ["Rio Claro"]` | `"locations": ["Rio Claro - SP"]` | Some city names exist in several states; add the UF. |
| `"locations": ["São Paulo"]` meaning the city | `"locations": ["São Paulo - SP"]` | A bare name that is also a state is read as the state (a warning says so). |
| `"contractTypes": ["full-time"]` | `"contractTypes": ["clt"]` | Valid: clt, pj, estagio, aprendiz, temporario, autonomo, freelancer, cooperado. |
| `"maxItemsPerKeyword": 500` with the default budget | also raise `"maxApiRequests"` | One request returns one page of 14 jobs; the budget is shared by all keywords. |

An invalid value stops the run immediately, before any request to the site, with a message that names the field and lists the valid values (for unknown cities it suggests the closest names). Numbers outside their range are clamped with a warning.

#### Empty results?

A search with no matching jobs finishes with 0 items and `complete: true` — not an error. Typical causes: an English or very specific title, a small city combined with several filters, or a contract type the companies there do not use. Try a shorter Portuguese title, a state instead of a city, or fewer filters.

***

### Output

One dataset item per job posting:

```json
{
  "keyword": "vendedor",
  "jobId": "932332",
  "title": "ASSISTENTE DE F&I | RP. 271.26",
  "companyName": "MARHGUS MOTOS LTDA",
  "companyNameWithheld": false,
  "companyConfidential": false,
  "city": "São Luís",
  "state": "MA",
  "stateName": "Maranhão",
  "country": "BR",
  "workplaceType": "onsite",
  "contractTypes": ["CLT"],
  "seniorities": ["Junior", "Pleno"],
  "occupationAreas": ["Administrativo"],
  "shifts": ["Integral"],
  "salaryMinBrl": null,
  "salaryMaxBrl": null,
  "salaryDisclosed": false,
  "benefits": ["Vale refeição", "Vale transporte"],
  "educationLevels": ["Ensino Médio"],
  "languages": [],
  "skills": [],
  "affirmativeGroups": [],
  "pcdOnly": false,
  "openPositions": 1,
  "postedDate": "2026-10-03",
  "applicationDeadline": null,
  "postedVia": "solides",
  "jobUrl": "https://vagas.solides.com.br/vaga/932332",
  "source": "Sólides Vagas (vagas.solides.com.br public job list)",
  "sourceUrl": "https://vagas.solides.com.br/vagas/todas",
  "license": "Publicly available data — unofficial tool; users are responsible for compliance with the source site's terms",
  "retrievedAt": "2026-10-04T09:31:12.420Z"
}
```

- `salaryMinBrl` / `salaryMaxBrl` follow the site's own display rule: filled only when the company chose to show the salary to applicants (a single value appears as min = max). The amounts are as entered by the company, normally monthly gross BRL.
- `applicationDeadline` is filled only when the company shows it.
- `postedVia: "rhgestor"` marks postings syndicated from Sólides' RH Gestor product; for those, `jobUrl` is the employer's careers page on rhgestor.com.br (the same link the site uses), and city/state are often empty.
- A run summary (`RUN_SUMMARY` in the key-value store) lists per keyword the upstream total, pages read, items stored, `complete` and a `stopReason`.

### Privacy (GDPR / LGPD)

Only company facts about the job are returned. The Actor never outputs street addresses, neighbourhoods, ZIP codes or coordinates (only city and state), recruiter or contact names, e-mail addresses, phone or WhatsApp numbers, screening questions or the long description text. E-mail addresses and phone numbers inside titles are replaced with `[contact removed]`. When the employer is a sole trader whose legal name is a person's name (Brazilian MEI names are "CNPJ root + owner's name"), `companyName` is `null` and `companyNameWithheld` is `true`.

### Honest limits

- **List-page facts only.** No full job description, no application. Fields the company left empty are `null` or `[]`.
- **14 jobs per request, site order.** Results come in the site's relevance order; deep pulls need many requests (1.5 s apart).
- **Snapshot.** Postings close and change; `retrievedAt` tells you when the data was read.
- **Filters are the site's.** If the site ever stops applying a filter, the Actor notices (the page echoes the applied filters) and stores nothing for that keyword rather than unfiltered jobs.
- **No proxy by default.** vagas.solides.com.br answered Apify datacenter IPs directly when this Actor was built (2026-10-04). If that changes, set `proxyConfiguration` (e.g. RESIDENTIAL, country BR); one sticky IP is used per run.
- **The site can refuse requests.** If it blocks mid-run, the run keeps what it has and ends successfully with `complete: false`. If the very first request is blocked, the run fails visibly instead of retrying aggressively.

### Pricing

Pay per result: you are charged for each job stored in the dataset. A run stops by itself when it reaches the maximum charge you set for the run.

# Actor input Schema

## `keywords` (type: `array`):

Job titles or words to search for, one per line, in Portuguese, e.g. "vendedor", "auxiliar administrativo", "analista de dados", "enfermeiro". Each keyword is searched separately and every record carries its keyword. Use "\*" for all jobs. Up to 50 per run. If empty, the Quick start keywords are used.

## `locations` (type: `array`):

Optional. Brazilian states or cities, one per line: a state code ("SP", "MG"), a state name ("Minas Gerais") or a city as "City - UF" ("Campinas - SP", "Rio de Janeiro - RJ"). Accents are optional. A bare name that is both a state and a city ("São Paulo") means the state. Leave empty for all of Brazil.

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

any (default), presencial (on-site), remoto (remote) or hibrido (hybrid). English words work too.

## `contractTypes` (type: `array`):

Optional, any of: clt (permanent employee), pj (contractor), estagio (internship), aprendiz (apprentice), temporario (temporary), autonomo (self-employed), freelancer, cooperado. Leave empty for all.

## `seniorities` (type: `array`):

Optional, any of: estagio (intern), junior, pleno (mid-level), senior, especialista (specialist), principal. Leave empty for all.

## `occupationArea` (type: `string`):

Optional, one of the site's areas: administrativo, agronegocio, comercial, compras, comunicacao, design, educacao, engenharia, financeiro, juridico, logistica, marketing, primeiro-emprego, producao, recursos-humanos, saude, tecnologia, turismo. English names work too (sales, finance, technology, health, HR). Leave empty for all areas.

## `maxItemsPerKeyword` (type: `integer`):

Job records kept per keyword (1..5000; values outside are clamped). One request returns one page of 14 jobs, so 140 jobs need 10 requests per keyword.

## `maxApiRequests` (type: `integer`):

Hard budget of page requests to vagas.solides.com.br for the whole run (1..500; clamped), shared fairly between keywords. Requests are strictly serial with at least 1.5 s spacing. If the budget runs out, the run still SUCCEEDS with the jobs found so far and complete=false in RUN_SUMMARY (key-value store).

## `proxyConfiguration` (type: `object`):

Optional. vagas.solides.com.br answers Apify datacenter IPs directly (tested 2026-10-04), so no proxy is used by default. If you set one (e.g. RESIDENTIAL, country BR), the run keeps a single sticky session (one IP) — never rotation.

## Actor input object example

```json
{
  "keywords": [
    "vendedor",
    "auxiliar administrativo"
  ],
  "locations": [],
  "workplaceType": "any",
  "contractTypes": [],
  "seniorities": [],
  "occupationArea": "",
  "maxItemsPerKeyword": 14,
  "maxApiRequests": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `records` (type: `string`):

Sólides Vagas job postings with source attribution (source, sourceUrl, license, retrievedAt) on every item.

# 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 = {
    "keywords": [
        "vendedor",
        "auxiliar administrativo"
    ],
    "locations": [],
    "workplaceType": "any",
    "contractTypes": [],
    "seniorities": [],
    "occupationArea": "",
    "maxItemsPerKeyword": 14,
    "maxApiRequests": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("jpopendata/brazil-jobs-solides").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 = {
    "keywords": [
        "vendedor",
        "auxiliar administrativo",
    ],
    "locations": [],
    "workplaceType": "any",
    "contractTypes": [],
    "seniorities": [],
    "occupationArea": "",
    "maxItemsPerKeyword": 14,
    "maxApiRequests": 10,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("jpopendata/brazil-jobs-solides").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 '{
  "keywords": [
    "vendedor",
    "auxiliar administrativo"
  ],
  "locations": [],
  "workplaceType": "any",
  "contractTypes": [],
  "seniorities": [],
  "occupationArea": "",
  "maxItemsPerKeyword": 14,
  "maxApiRequests": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call jpopendata/brazil-jobs-solides --silent --output-dataset

```

## MCP server setup

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

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/ta1Hjp2BZsQKlOOXJ/builds/BgJnTpm4n8V9N0rTE/openapi.json
