# InfoJobs Scraper — Spain Job Offers, Salary & Employers (`logiover/infojobs-scraper`) Actor

Scrape offers from InfoJobs, Spain's largest job board: title, hiring company, city, work model, contract type, working hours, advertised salary with a sortable numeric floor and a description snippet. Keyless, no login. JSON, CSV or Excel.

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

## Pricing

from $3.50 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

## InfoJobs Scraper — Spain Job Offers, Salary & Employers

Export offers from **InfoJobs**, Spain's largest job board — company, city, contract type, work model and advertised salary — to JSON, CSV or Excel. Keyless, no login.

### What does InfoJobs Scraper do?

InfoJobs renders its search results server-side, so this Actor parses the offer cards directly and walks the pagination. No API key, no account, no cookies.

The card is built in a way that punishes lazy parsing: location, work model, posting age, contract type and working hours all sit in **two generic list blocks with identical class names**, and their order changes from ad to ad. Read them by index and "Jornada completa" ends up in a location column. This Actor classifies every value by its own Spanish pattern — a *contrato* word is a contract, a *jornada* word is working hours, *presencial / híbrido / teletrabajo* is the work model, and whatever remains is the city.

Spanish salaries are written with a dot as the thousands separator (`30.000 € - 40.000 € Bruto/año`). Parsed naively that becomes 30. The Actor strips the separator and emits a correct numeric floor alongside the original label.

### Who is it for?

- **Recruitment agencies** tracking who is hiring across Spanish provinces.
- **Salary researchers** building Spanish pay benchmarks by role.
- **B2B sales teams** using hiring activity as a growth signal.
- **Job boards and aggregators** that need a Spanish feed.
- **Market analysts** measuring the split between on-site, hybrid and remote work.

### Use cases

- Compare advertised pay for developers between Madrid, Barcelona and Valencia.
- Measure how many offers are hybrid versus fully on-site, month over month.
- List every company hiring on permanent contracts in a province.
- Filter to offers that publish pay and rank them by floor.
- Feed a niche board with fresh Spanish listings.

### Why use this Actor?

- **Keyless** — nothing to sign up for.
- **18 structured fields**, with contract, working hours and work model never mixed.
- **Correct Spanish number parsing** — `30.000 €` becomes 30000, not 30.
- **Work-model column** so remote and hybrid roles are filterable.
- **Description snippet** included for context without opening each ad.
- **Honest columns** — a value lands in a column only when it matches that field's pattern.

### What data can you extract?

| Field | Type | Description |
|---|---|---|
| `offerId` | string | InfoJobs offer ID |
| `title` | string | Job title |
| `offerUrl` | string | Public offer page |
| `companyName` | string | Hiring company |
| `location` | string | City or town |
| `workModel` | string | Presencial, Híbrido, Teletrabajo |
| `contractType` | string | e.g. Contrato indefinido, temporal |
| `workingHours` | string | e.g. Jornada completa, parcial |
| `salaryLabel` | string | Salary exactly as advertised |
| `salaryFrom` | number | Numeric floor for sorting |
| `currency` | string | EUR when a salary is present |
| `postedLabel` | string | Posting age as displayed |
| `isFeatured` | boolean | Paid featured placement |
| `isRepublished` | boolean | Ad was republished |
| `snippet` | string | Opening of the description |
| `country` | string | Spain |
| `source` | string | Always `infojobs_net` |
| `scrapedAt` | string | Run timestamp (ISO 8601) |

#### Example output

```json
{
  "offerId": "dd425b14884f9a80d940498721c913",
  "title": "Expert Java Backend Engineer",
  "offerUrl": "https://www.infojobs.net/viladecans/expert-java-backend-engineer/of-idd425b14884f9a80d940498721c913",
  "companyName": "SOLUCIONES TECNO-PROFESIONALES",
  "location": "Viladecans",
  "workModel": "Híbrido",
  "contractType": "Contrato indefinido",
  "workingHours": "Jornada completa",
  "salaryLabel": "30.000 € - 40.000 € Bruto/año",
  "salaryFrom": 30000,
  "currency": "EUR",
  "country": "Spain",
  "source": "infojobs_net"
}
```

### How to use it

1. Enter a keyword, or leave it empty to walk the newest offers.
2. Optionally set a province ID to narrow by region.
3. Switch on **Only ads with a salary** when building a benchmark.
4. Set **Maximum results** and run, then export JSON, CSV or Excel.

### Input example

```json
{
  "query": "programador",
  "withSalaryOnly": true,
  "maxResults": 1000
}
```

### Notes and limits

- Roughly **half** of Spanish offers publish pay; the rest leave `salaryLabel` and `salaryFrom` as `null`.
- InfoJobs mixes sponsored placeholder cards into the results. Those carry no offer link and are skipped rather than emitted as empty rows.
- Work model is only present when the employer states it; it stays `null` otherwise.
- This Actor exports offer cards, not full descriptions — that is what keeps bulk runs cheap. Open `offerUrl` for the full text.
- You are responsible for how you use employer data, including under GDPR.

### FAQ

**Do I need an InfoJobs account?**
No. Offers are public and this Actor needs nothing from you.

**Does it handle Spanish number formatting?**
Yes. `30.000 €` is read as thirty thousand, which is the whole point of the dedicated parser.

**Can I filter to remote roles?**
Filter the exported dataset on `workModel` — InfoJobs does not expose a reliable remote-only URL filter.

**Why are some rows missing a work model?**
Because the employer did not state one. The field is left `null` rather than guessed.

**How am I charged?**
Pay per result — you pay for the offers delivered.

# Actor input Schema

## `query` (type: `string`):

Free-text search across InfoJobs, e.g. "programador", "comercial" or "enfermera". Leave empty to walk the newest offers.

## `province` (type: `string`):

Optional InfoJobs province ID, e.g. 28 for Madrid or 8 for Barcelona.

## `withSalaryOnly` (type: `boolean`):

Spanish employers publish pay on roughly half of ads. Switch this on to keep only those.

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

Stop after this many offers. The Actor walks pages until the target is met.

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

InfoJobs serves plain HTML; the default datacenter proxy is enough.

## Actor input object example

```json
{
  "query": "programador",
  "withSalaryOnly": false,
  "maxResults": 500,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `offerId` (type: `string`):

No description

## `title` (type: `string`):

No description

## `offerUrl` (type: `string`):

No description

## `companyName` (type: `string`):

No description

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

No description

## `workModel` (type: `string`):

No description

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

No description

## `workingHours` (type: `string`):

No description

## `salaryLabel` (type: `string`):

No description

## `salaryFrom` (type: `string`):

No description

## `currency` (type: `string`):

No description

## `postedLabel` (type: `string`):

No description

## `isFeatured` (type: `string`):

No description

## `isRepublished` (type: `string`):

No description

## `snippet` (type: `string`):

No description

## `country` (type: `string`):

No description

## `source` (type: `string`):

No description

## `scrapedAt` (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 = {
    "query": "programador",
    "province": "",
    "withSalaryOnly": false,
    "maxResults": 500,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("logiover/infojobs-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 = {
    "query": "programador",
    "province": "",
    "withSalaryOnly": False,
    "maxResults": 500,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("logiover/infojobs-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 '{
  "query": "programador",
  "province": "",
  "withSalaryOnly": false,
  "maxResults": 500,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call logiover/infojobs-scraper --silent --output-dataset

```

## MCP server setup

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