# elempleo Job Scraper — Full Descriptions & COP Salaries (`corvuslab/elempleo-scraper`) Actor

Scrape every live job on elempleo.com (Colombia): full job description, numeric COP salary min/max, expiry date, city/region and area/profession/sector. Filter by keyword, city, industry, contract and work mode; monitor changes and export to JSON, CSV or Excel.

- **URL**: https://apify.com/corvuslab/elempleo-scraper.md
- **Developed by:** [Corvuslab](https://apify.com/corvuslab) (community)
- **Categories:** Jobs, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## elempleo Scraper

> **Turn every live elempleo.com job into a clean, structured record — full description, numeric COP salary and all — in seconds.**

Scrape jobs and salaries from **elempleo.com**, Colombia's major job board (~20,000 live vacancies). Every record carries the **full job description** (as text, HTML and Markdown), the **company**, **city/region**, the **formatted salary** plus **numeric COP salary min/max**, **contract type**, **work mode**, **posted** and **expiry** dates, and the job's **área / profession / sector**. Search by keyword, narrow with rich filters, and **monitor changes on a schedule** — all no-code, with output to **JSON, CSV, Excel, API** or straight into your **AI agent**.

**Why this scraper**

- ⚡ **Fast & low-cost** — runs on cheap direct requests, no login and no browser, so large runs stay cheap.
- 🧾 **Rich, typed records** — 40+ structured fields per job, not raw HTML.
- 🧠 **Full description on every job** — the complete offer text ships even on the fastest, cheapest runs — the field most job feeds and scrapers leave out.
- 💰 **Parsed COP salary** — not just the formatted band the site shows, but numeric `salaryMin` / `salaryMax` in COP, ready for analysis without regex or cleanup.
- ♻️ **Cheap to monitor** — incremental mode re-scrapes only what changed (see below).
- 🔔 **Notifications built in** — Telegram, Slack, Discord or any webhook.
- 🤖 **AI- & API-ready** — compact output, MCP-friendly, one-click integrations.

***

### ✨ Key features

- 🧾 **Full job description — always** — the complete offer text is included as **plain text, HTML and Markdown** on *every* run, even with detail enrichment turned off. This is the field most job feeds leave out.
- 💰 **Structured COP salary** — not just the formatted band the site shows, but **numeric `salaryMin` / `salaryMax` in COP**, plus period and currency — ready for salary-benchmarking and analysis.
- 🔬 **Detail enrichment** — opt in for **expiry date**, exact **region / country**, **employment type**, and the job's **área, profession, sector and experience level**.
- 🔎 **Keyword search or direct URLs** — run one or many keyword searches, or paste specific elempleo job URLs to scrape those offers directly.
- 🎚️ **Rich filters, applied on the site** — 40 Colombian **cities**, 38 **industries/sectors**, **contract type**, **work mode** (on-site / remote / hybrid), **position level**, **experience** and **posted-within** recency, plus sort order.
- 📇 **Contacts & lead-gen signals** — emails, phones, URLs and social profiles pulled from each description, with an optional `requireContact` filter to keep only jobs that expose a contact.
- ♻️ **Incremental monitoring** — schedule it and get only what changed (NEW / UPDATED / EXPIRED); unchanged jobs are skipped *before* their page is even fetched.
- 🔔 **Notifications** — Telegram, Slack, Discord or any webhook (n8n / Make / Zapier).
- 🤖 **AI-ready** — compact and drop-empty output modes keep payloads small for LLMs and MCP.

***

### ⚙️ Input & output

#### Input

Configure it in the visual editor — no code needed — or pass JSON via the API.

| Field | What it does |
|---|---|
| `keywords` | One or more Spanish keyword searches (e.g. `contador`, `desarrollador`). |
| `startUrls` | Scrape specific elempleo job-detail URLs directly. |
| `cities` | Limit to any of 40 Colombian cities. |
| `industry` | Limit to one of 38 industries / sectors. |
| `contractType`, `workModality`, `positionLevel`, `experience`, `publishDate` | Site filters: contract, work mode, seniority, experience, posted-within recency. |
| `includeDetails` | Fetch each job's page for salary min/max, expiry, location and área/profession/sector. |
| `requireContact` | Keep only jobs that expose an email and/or phone. |
| `incrementalMode` | Emit only what changed since the last run. |
| `maxResults` | Cap the number of jobs (0 = unlimited). |
| `proxyConfiguration` | Optional — runs fine without a proxy at typical volumes. |

…and **30 inputs** in total — the table shows the essentials; the rest cover description format, notification channels, output/AI modes and advanced tuning, all in the visual editor.

**Example inputs**

```json
{ "keywords": ["contador", "auxiliar contable"], "maxResults": 100 }
```

```json
{ "keywords": ["desarrollador"], "cities": ["34"], "workModality": "101", "publishDate": "7", "includeDetails": true }
```

```json
{ "keywords": ["ingeniero"], "incrementalMode": true, "telegramToken": "…", "telegramChatId": "@yourchannel" }
```

#### Output

Most job scrapers hand you a title, company and a link back to the site. This scraper delivers the **full job description** (plain text, HTML and Markdown) and **parsed COP salary min/max** on every record — no post-processing, no second request, no data left behind on the page.

**Example output**

```json
{
  "id": "1886738223",
  "title": "Contador público",
  "url": "https://www.elempleo.com/co/ofertas-trabajo/contador-publico-1886738223",
  "company": "ASESORIA Y GESTION INTEGRAL SAS",
  "companyId": 2108643298,
  "companyLogo": "https://elempleo.blob.core.windows.net/empresasprd/logo_pordefecto.webp",
  "isConfidential": false,
  "city": "Bogotá",
  "region": "Bogotá",
  "country": "CO",
  "salary": "$2 a $2,5 millones",
  "hasSalary": true,
  "salaryMinMillions": 2.0,
  "salaryMaxMillions": 2.5,
  "salaryMin": 2000001.0,
  "salaryMax": 2500000.0,
  "salaryPeriod": "MONTH",
  "salaryCurrency": "COP",
  "contractType": "Indefinido",
  "employmentType": "FULL_TIME",
  "workMode": "Presencial",
  "isRemote": false,
  "isHybrid": false,
  "area": "Administrativa y Financiera",
  "profession": "Contaduría",
  "sector": "Servicios",
  "experienceLevel": "Más de 1 año",
  "positions": "Contador",
  "keywords": ["contador", "contadores"],
  "publishedDate": "2026-07-10T00:00:00Z",
  "publishedText": "Hace 1 mes",
  "expirationDate": "2026-08-24",
  "description": "Empresa de Juegos de suerte y azar, ubicada en Soacha, requiere Contador Público con sólidos conocimientos y experiencia en todo el ciclo contable y tributario. - Horario de Lunes a Jueves de 7.30 am a 5pm...",
  "source": "elempleo.com",
  "searchKeyword": "contador",
  "scrapedAt": "2026-08-18T09:10:29Z",
  "detailFetched": true
}
```

The description also ships as `descriptionHtml` and `descriptionMarkdown`. Every field is nullable — missing values come back as `null`. **Compact mode** returns the core fields only, for lean AI/MCP payloads.

***

### 📚 What data can you extract from elempleo?

- **Core** — `id`, `title`, `url`, `company`, `companyId`, `companyLogo`, `isConfidential`, `city`, `salary`, `hasSalary`, `salaryMinMillions` / `salaryMaxMillions`, `currency`, `contractType`, `workMode`, `isRemote` / `isHybrid`, `positions`, `keywords`, `publishedDate` / `publishedText`.
- **Full description** — `description` (plain text), `descriptionHtml`, `descriptionMarkdown` — included on every run.
- **Details** (with `includeDetails`) — `region`, `country`, `salaryMin` / `salaryMax`, `salaryPeriod`, `salaryCurrency`, `employmentType`, `expirationDate`, `area`, `profession`, `sector`, `experienceLevel`.
- **Contacts & signals** — `extractedEmails`, `extractedPhones`, `extractedUrls`, `socialProfiles`.
- **Change tracking** — `changeType` (NEW / UPDATED / UNCHANGED / EXPIRED), `contentHash`, `isRepost`, plus `source`, `searchKeyword`, `scrapedAt`, `detailFetched`.

Every field is present in standard mode (missing values are `null`); **compact mode** returns the core fields only, for lean AI/MCP payloads.

***

### 💡 What can you do with elempleo data?

- **Labor-market & salary research** — track live vacancies and numeric COP salary bands across cities, industries and roles.
- **Recruiting & sourcing** — build a fresh, filtered feed of open roles with full descriptions and company details.
- **HR-tech ingestion** — feed clean, typed job records into your own product, ATS or data warehouse.
- **Sales prospecting** — spot hiring signals and pull contact details from descriptions for lead lists.
- **Talent-market intelligence** — monitor demand by área, profession, sector and work mode over time.
- **AI agents & pipelines** — compact output plugs straight into LLM/MCP workflows.

***

### ♻️ Incremental monitoring — pay for changes, not repeats

Schedule the actor and turn on **incremental mode**: each run compares against the last and emits only **NEW / UPDATED / EXPIRED** records — unchanged jobs are skipped *before* their detail page is fetched, so a daily watch costs a fraction of a full re-scrape.

| Daily churn | of 1,000 tracked | billable records | you save |
|---|---|---|---|
| 5 % | 1,000 | 50 | **95 %** |
| 15 % | 1,000 | 150 | **85 %** |
| 30 % | 1,000 | 300 | **70 %** |

The first run seeds the baseline and bills in full; every run after that bills only the delta.

***

### 🚀 How to scrape elempleo

1. Open the actor and enter one or more **search keywords** (e.g. `contador`) and/or pick filters — or paste an elempleo job URL.
2. Set **Max results** and choose whether to **fetch full details**.
3. (Optional) Turn on **incremental mode** and a **notification** channel, then **Schedule** it.
4. Click **Start**.
5. Download the data as **JSON, CSV or Excel**, or pull it from the **API**.

New to Apify? Create a free account — it comes with monthly credit, no credit card required.

***

### 🔌 Integrations & export

Export to **JSON, CSV, Excel** or an HTML table, or pull from the **REST API** and the **JavaScript / Python** clients. Runs on a **schedule**, connects to **Google Sheets, Slack, Make, Zapier and n8n**, and works as an **MCP tool** for AI agents — compact mode keeps token usage small.

***

### ❓ FAQ

**Do I need a proxy or login?** No — it runs out of the box with no account or proxy; Apify Proxy is available under Advanced for very high-volume runs.

**Do I always get the full job description?** Yes — the complete description ships on every run, even with detail enrichment turned off, as plain text, HTML and Markdown.

**What extra fields does "fetch full details" add?** Numeric COP salary min/max, expiry date, exact region/country, employment type, and the job's área, profession, sector and experience level.

**Can I get only new jobs on a schedule?** Yes — turn on incremental mode and schedule it; each run emits only what changed and can notify your channel.

**What formats can I export?** JSON, CSV, Excel, HTML table, or via the API.

**Is it good for AI agents?** Yes — enable compact mode; the output is MCP-friendly.

**How many jobs can I get?** As many as the search returns — set `maxResults` (0 = unlimited).

**What makes this scraper different from other job scrapers?** Most job scrapers give you a title, company and a link — you have to visit the page yourself for the description and parse the salary string for numbers. This scraper delivers the **full description** in three formats and **parsed COP salary min/max** on every record, plus contact extraction, 40+ structured fields, and incremental monitoring out of the box.

**Is scraping this legal?** The actor collects only **publicly available** data. You are responsible for how you use it, including any personal data and GDPR-style obligations.

***

### ⚖️ Is it legal to scrape elempleo?

This actor accesses only publicly available data on elempleo.com. You are responsible for how you use the extracted data — in particular any personal information — and for complying with the site's terms and applicable law (including the GDPR where it applies). Not affiliated with, endorsed by, or sponsored by elempleo.com or its operators.

***

**Keywords:** elempleo scraper · elempleo.com scraper · elempleo api · elempleo job scraper · Colombia job scraper · Colombia jobs api · empleos Colombia · ofertas de trabajo Colombia · job board scraper · salary data scraper · COP salary data · labor market research · recruitment data · hiring signals · lead generation · job monitoring · export to CSV Excel JSON · no-code scraper · MCP tool for AI agents

# Actor input Schema

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

Job search terms, e.g. "desarrollador", "contador", "conductor". Add several to run several searches.

## `keyword` (type: `string`):

Alternative single search term. Combined with the list above.

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

Paste elempleo job-detail URLs (…/co/ofertas-trabajo/…) to scrape those offers directly.

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

Maximum number of jobs to return across all searches. Set 0 for unlimited (bounded by how many the search has).

## `cities` (type: `array`):

Limit to one or more Colombian cities. Leave empty for the whole country.

## `industry` (type: `string`):

Limit to a company industry.

## `contractType` (type: `string`):

Filter by contract type.

## `workModality` (type: `string`):

Filter by on-site / remote / hybrid.

## `positionLevel` (type: `string`):

Filter by seniority.

## `experience` (type: `string`):

Filter by required experience.

## `publishDate` (type: `string`):

Only jobs published within the last N days.

## `sortExpression` (type: `string`):

Order of the returned jobs.

## `requireContact` (type: `string`):

off = keep everything; email / phone = require that channel; either = at least one; both = email and phone. Screened at emit time, so it also trims what you're billed for.

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

Fetch each job's detail page for numeric salary, expiry date, exact location and area / profession / sector. Turn off for the fastest, cheapest runs (you still get the description).

## `descriptionFormat` (type: `string`):

Which representation(s) of the job description to include.

## `compact` (type: `boolean`):

Emit only the core fields. Ideal for AI agents and MCP clients.

## `excludeEmptyFields` (type: `boolean`):

Remove null, empty-string and empty-array fields from each record.

## `incrementalMode` (type: `boolean`):

Track state between runs and tag every job with a changeType (NEW / UPDATED / UNCHANGED / EXPIRED).

## `stateKey` (type: `string`):

Stable name for the tracked search. Leave empty to derive one from your search settings.

## `emitUnchanged` (type: `boolean`):

Also emit jobs that have not changed since the previous run.

## `emitExpired` (type: `boolean`):

Emit jobs present last run but gone now.

## `telegramToken` (type: `string`):

Bot token from @BotFather.

## `telegramChatId` (type: `string`):

Chat or channel ID, e.g. "-100123456789" or "@yourchannel".

## `slackWebhookUrl` (type: `string`):

Slack incoming-webhook URL.

## `discordWebhookUrl` (type: `string`):

Discord incoming-webhook URL.

## `webhookUrl` (type: `string`):

Any HTTPS endpoint. Receives a JSON POST with the matched jobs — works with n8n, Make and Zapier.

## `webhookHeaders` (type: `object`):

Extra headers for the webhook request, e.g. {"Authorization": "Bearer xyz"}.

## `notificationLimit` (type: `integer`):

How many jobs to include in each notification message.

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

Optional. elempleo works direct (no proxy) — leave this off for the cheapest runs. Enable a proxy only if you hit rate limits on very large runs.

## `maxRequestRetries` (type: `integer`):

How many times to retry a failed request before giving up on it.

## Actor input object example

```json
{
  "keywords": [
    "contador",
    "auxiliar contable"
  ],
  "maxResults": 50,
  "industry": "",
  "contractType": "",
  "workModality": "",
  "positionLevel": "",
  "experience": "",
  "publishDate": "0",
  "sortExpression": "PublishDate_Desc",
  "requireContact": "off",
  "includeDetails": true,
  "descriptionFormat": "all",
  "compact": false,
  "excludeEmptyFields": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "notificationLimit": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxRequestRetries": 3
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `allItems` (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 = {
    "keywords": [
        "desarrollador"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("corvuslab/elempleo-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 = { "keywords": ["desarrollador"] }

# Run the Actor and wait for it to finish
run = client.actor("corvuslab/elempleo-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 '{
  "keywords": [
    "desarrollador"
  ]
}' |
apify call corvuslab/elempleo-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,corvuslab/elempleo-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/iDKvp2vJOGK3VzRkA/builds/ucigpc0Ugd5E9vSTN/openapi.json
