# OCC Mundial Jobs Scraper (`hexadatalabs/occ-mundial-jobs-scraper`) Actor

Scrape job listings from OCC Mundial (occ.com.mx), Mexico's largest job board: title, company, location, salary range, posting date, category, contract type, work mode, benefits and full description. Pay per result.

- **URL**: https://apify.com/hexadatalabs/occ-mundial-jobs-scraper.md
- **Developed by:** [Hexadata](https://apify.com/hexadatalabs) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 job listing (result)s

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

## OCC Mundial Jobs Scraper

Extract job listings from **OCC Mundial (occ.com.mx)**, Mexico's largest job board, as clean structured JSON: title, company (with verified badge), location, salary range as numbers in MXN, posting date, category and subcategory, contract type, schedule, work mode (on-site / remote / hybrid), benefits and the full description.

Give it one or more keywords and an optional Mexican state. **Pay per result**: you only pay for the jobs you receive, and `maxResults` caps the cost.

### What you can use it for

- **Salary and labor-market research** for Mexico: ranges by role, state and sector.
- **B2B lead generation**: companies hiring for a given profile right now.
- **Hiring-trend monitoring and internal alerts** for your own team: run it on a schedule and compare with the previous run by `id`.
- **Recruiting intelligence**: watch competitors' openings and offered salaries.
- **AI agents**: query the Mexican job market in natural language through MCP (see below).

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `searchQueries` | string\[] | – (required unless `startUrls` is set) | Keywords or job titles. Each one is a separate search. |
| `location` | string | – | Mexican state, e.g. `nuevo leon`, `ciudad de mexico`, `jalisco`, `estado de mexico` (`cdmx` and `edomex` also work). OCC filters by state only: a city name or a typo makes the run fail with a clear message instead of returning (and billing) nationwide results. |
| `startUrls` | array | – | Custom OCC listing URLs (any results page, including the site's own filters; pagination continues from that page). |
| `maxResults` | integer | `100` | Total cap across all queries. |
| `scrapeDetails` | boolean | `true` | Open each job page for description, category, education, contract type, schedule, work mode and exact posting date. One extra request per job. |
| `postedWithinDays` | integer | – | Skip listings older than N days. |
| `onlyNewSinceLastRun` | boolean | `false` | For scheduled runs: only save jobs posted after the previous run started (first run saves everything). |
| `includeIndividualEmployers` | boolean | `false` | Keep the name of employers that look like a private individual (sole proprietors posting under their own name). By default `company` is `null` for them and `companyIsIndividual` is `true`. See Legal notice. |
| `proxyConfiguration` | object | Apify proxy | Datacenter proxies work for listing pages; job pages need residential proxies (see Limits). |
| `maxConcurrency` | integer | `3` | 1–5 parallel requests; the site is shared with real job seekers. |
| `maxRequestsPerMinute` | integer | `60` | Cap on requests per minute to the site (max 120). |

Example:

```json
{
    "searchQueries": ["data engineer", "desarrollador python"],
    "location": "jalisco",
    "maxResults": 200,
    "scrapeDetails": true,
    "postedWithinDays": 14
}
```

### Output

One dataset item per job. Example (shortened):

```json
{
    "id": "21386467",
    "title": "Contador",
    "company": "GRUPO ACROS",
    "companyConfidential": false,
    "companyIsIndividual": false,
    "companyVerified": true,
    "companyLogo": "https://cdn-h4.occ.com.mx/images/logos/300x300/....jpg",
    "location": "Torreón, Coahuila",
    "city": "Torreón",
    "region": "Coahuila",
    "salary": "$ 18,000 - $ 20,000 Mensual",
    "salaryMin": 18000,
    "salaryMax": 20000,
    "salaryPeriod": "monthly",
    "currency": "MXN",
    "postedText": "Hace 3 días",
    "postedAt": "2026-10-23T18:47:28.126Z",
    "datePosted": "2026-10-23",
    "validThrough": "2026-12-22",
    "isRecommended": true,
    "isPremium": true,
    "externalApplication": false,
    "benefits": ["Prestaciones de ley", "Fondo de ahorro"],
    "category": "Administrativo",
    "subcategory": "Administración de oficina",
    "education": "Licenciatura",
    "employmentType": "Tiempo completo",
    "schedule": "Jornada completa",
    "workMode": "Presencial",
    "description": "VACANTE: CONTADOR (A)\nLugar de trabajo: Torreón Jardín, Torreón, Coahuila.\nRequisitos ...",
    "url": "https://www.occ.com.mx/empleo/oferta/21386467-contador/",
    "searchQuery": "contador",
    "scrapedAt": "2026-10-26T18:47:29.151Z"
}
```

| Field | Description |
| --- | --- |
| `id` | OCC job id (stable, use it to deduplicate between runs) |
| `title`, `company`, `companyConfidential`, `companyVerified`, `companyLogo` | Employer info as published; confidential employers appear as "Empresa confidencial" |
| `companyIsIndividual` | `true` when the employer name looks like a private person rather than a company; `company` is then `null` unless `includeIndividualEmployers` is set |
| `location`, `city`, `region` | Listing location and the structured city / state from the job page |
| `salary`, `salaryMin`, `salaryMax`, `salaryPeriod`, `currency` | Published range parsed to numbers; all `null` when the employer hides the salary |
| `postedText`, `postedAt`, `datePosted`, `validThrough` | Relative date on the listing, its ISO conversion, and the exact dates from the job page |
| `isRecommended`, `isPremium`, `externalApplication`, `benefits` | Listing badges and benefit tags |
| `category`, `subcategory`, `education`, `employmentType`, `schedule`, `workMode` | "Sobre el empleo" attributes from the job page |
| `description` | Full plain-text description from the job page |
| `searchQuery`, `scrapedAt` | Provenance |

Fields not published for a job are `null`. With `scrapeDetails: false` only listing fields are filled, which is about twice as fast and half the requests.

### Pricing & cost examples

**$1.50 per 1,000 jobs** (pay per event, charged only for items saved to the dataset; duplicates and errors are never billed). `maxResults` is a hard cost cap. Examples: 1,000 jobs ≈ $1.50 · a daily run with `maxResults: 200` ≈ $0.30/day (≈ $9/month) · a one-off market study of 5,000 jobs ≈ $7.50. Apify platform usage is included in the price.

### Use it from an AI agent (MCP)

Every Apify Actor is available as a tool through the [Apify MCP server](https://mcp.apify.com). Add `https://mcp.apify.com/?tools=hexadatalabs/occ-mundial-jobs-scraper` as an MCP server in Claude, Cursor, VS Code or any MCP client (with your Apify token) and ask, for example:

> "What are companies in Monterrey paying for accountants this month?"

The agent calls this Actor with `{"searchQueries":["contador"],"location":"nuevo leon","postedWithinDays":30}` and reasons over the returned salaries.

From code, with the Apify API:

```bash
curl -X POST "https://api.apify.com/v2/acts/hexadatalabs~occ-mundial-jobs-scraper/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries":["analista de datos"],"location":"ciudad de mexico","maxResults":50}'
```

```python
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("hexadatalabs/occ-mundial-jobs-scraper").call(run_input={
    "searchQueries": ["marketing digital"], "maxResults": 100, "postedWithinDays": 7,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["company"], job["salaryMin"], job["salaryMax"])
```

### Limits and notes

- **Job pages and datacenter IPs.** OCC serves the listing pages to Apify's datacenter proxies, but in our platform tests every job page (the detail request) answered HTTP 403 from datacenter IPs, while the same pages load from residential IPs. When a job page cannot be fetched the job is still saved with its listing fields (title, company, location, salary, posted date, badges) and the detail fields (`description`, `category`, `education`, `employmentType`, `schedule`, `workMode`, `datePosted`) stay `null`; the run's status message tells you how many jobs were saved from listing data only. For full details choose Apify **residential** proxies in `proxyConfiguration` (`{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}`), or set `scrapeDetails: false` to get listing data at half the requests.

- Results come from the public search pages in the site's own order, about 20 per page.

- `location` accepts Mexican states only (OCC has no city-level search URL). The run also checks the page it gets back and stops if the site ignored the state filter, so you are never billed for nationwide results you did not ask for.

- Many employers hide the salary ("Sueldo no mostrado por la empresa"); those jobs have `salary: null`.

- Split broad queries by state or keyword to get beyond the site's per-search result ceiling.

- If the site changes its HTML the run fails with a clear `SITE_LAYOUT_CHANGED` error instead of returning empty data. Please report it and it will be fixed quickly.

- If a single job page cannot be loaded after 3 retries, the job is still saved with its listing fields and the detail fields (`description`, `category`, ...) `null`; the run log and status message say how many.

- The Actor sends at most 60 requests per minute to the site by default (`maxRequestsPerMinute`, max 120) with 1–5 parallel requests, uses a session pool and retries transient errors up to 3 times.

### Legal notice

This Actor collects **publicly available job advertisements published by companies**. It does not collect candidate or recruiter data, does not log in, and does not bypass access controls. Names of individual employers (sole proprietors posting under their own name) are omitted by default; set `includeIndividualEmployers` only if you have a legal basis to process personal data. OCC's Terms (section 12, https://h3.occ.com.mx/Ayuda/Terminos_Condiciones) do not allow republishing or redistributing listing content for public or commercial use. Use the output for internal analysis, research and your own job search; do not republish job texts without OCC's permission. You are responsible for complying with OCC's terms of use and the data-protection laws that apply to you.

### Changelog

- **0.1** (2026-10-27) — First public release.

### Support

Found a problem or need another field? Open an issue in the **Issues** tab of this Actor; we answer within 24 hours and ship confirmed fixes within 48 hours.

***

#### En español

Extrae vacantes de OCC Mundial (occ.com.mx) con título, empresa, ubicación, rango salarial numérico en pesos, fecha, categoría, tipo de contratación, horario, modalidad (presencial / remoto / híbrido), prestaciones y descripción completa. Indica las palabras clave y opcionalmente el estado (OCC solo filtra por estado, no por ciudad). Se cobra por resultado; usa `maxResults` para limitar el costo. Los nombres de empleadores que son personas físicas se omiten por defecto. Los datos son para análisis interno e investigación: los términos de OCC no permiten redistribuir el contenido de las vacantes. Funciona también como herramienta para agentes de IA mediante el servidor MCP de Apify.

# Actor input Schema

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

Job titles or keywords to search for, one per line (for example "contador", "desarrollador python", "ventas"). Each query becomes one search. Required unless startUrls is given.

## `location` (type: `string`):

Mexican state to filter by, for example "nuevo leon", "ciudad de mexico", "jalisco", "estado de mexico" (also accepts cdmx, edomex). OCC filters by state only: city names are not supported and the run stops with an error instead of returning nationwide results. Leave empty for all of Mexico.

## `startUrls` (type: `array`):

Full OCC listing URLs to scrape instead of (or in addition to) the search queries, for example a URL built with the site's filters: https://www.occ.com.mx/empleos/de-contador/en-nuevo-leon/

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

Stop after this many job listings in total (across all queries). You are charged per result, so this caps the cost.

## `scrapeDetails` (type: `boolean`):

Open each job page to get the full description, category, contract type, schedule, work mode, education and exact posting date. One extra request per job.

## `postedWithinDays` (type: `integer`):

Skip listings older than this many days, using the posting date shown on the listing. Leave empty to keep everything.

## `onlyNewSinceLastRun` (type: `boolean`):

Use this when you schedule the Actor (daily/weekly): it remembers when the previous run started and only saves jobs posted after it, so you pay only for new vacancies. The first run saves everything.

## `includeIndividualEmployers` (type: `boolean`):

Sole proprietors can post vacancies on OCC under their own name. By default such names are replaced by null (the job is still saved, with companyIsIndividual: true). Enable only if you have a legal basis to process personal data.

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

Proxy to use. Listing pages work through Apify datacenter proxies; OCC job pages (scrapeDetails) are blocked from datacenter IPs and need Apify residential proxies (group RESIDENTIAL). Without them jobs are still saved from listing data.

## `maxConcurrency` (type: `integer`):

Maximum parallel requests. Keep it low (2–5) to stay polite with the site, which is shared with real job seekers.

## `maxRequestsPerMinute` (type: `integer`):

Upper bound on requests sent to the site per minute (listing and job pages together).

## Actor input object example

```json
{
  "searchQueries": [
    "contador"
  ],
  "startUrls": [],
  "maxResults": 100,
  "scrapeDetails": true,
  "onlyNewSinceLastRun": false,
  "includeIndividualEmployers": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 3,
  "maxRequestsPerMinute": 60
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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": [
        "contador"
    ],
    "location": "",
    "startUrls": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("hexadatalabs/occ-mundial-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": ["contador"],
    "location": "",
    "startUrls": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("hexadatalabs/occ-mundial-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": [
    "contador"
  ],
  "location": "",
  "startUrls": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call hexadatalabs/occ-mundial-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hexadatalabs/occ-mundial-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/0QfnFNxwz2pP1SsdD/builds/hQRTSnRLjJTd3ZnbU/openapi.json
