# InfoJobs Jobs Scraper | Spanish Job Data (`datascraperes/infojobs-jobs-scraper`) Actor

Search public InfoJobs Spain listings with friendly keyword, location, remote-work, contract, schedule, date, and sorting filters. Get unique titles, descriptions, companies, cities, publication dates, listing URLs, and promotion fields with optional detail-page enrichment.

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

## Pricing

from $0.75 / 1,000 infojobs job 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/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

Search public InfoJobs Spain listings and save **unique structured job listings** to the default Apify Dataset. Enter a keyword and optional location or job filters, set the maximum number of jobs, and optionally request best-effort detail-page enrichment.

### What this Actor does

This Actor reads public InfoJobs search-result pages without an external jobs API. It translates the values you enter into the public InfoJobs search, follows result pagination, preserves the generated search URL that produced each item, extracts the server-rendered job listing data, and removes duplicate offers across the collected pages.

Each Dataset item contains the job title, full public description when available, city, job URL, contract type, workday, teleworking mode, publication date, company information, promotion/state fields, and the retrieval timestamp. Detail enrichment is optional and uses the public JSON detail contract declared by InfoJobs' frontend; the core listing remains usable if that contract or the HTML fallback is unavailable.

### Use cases

- Build a current dataset of Spanish job listings for recruiting or market research.
- Narrow results by city or province, remote-work mode, contract type, schedule, publication date, and sort order.
- Track public job titles, companies, locations, contract types, working conditions, and publication dates.
- Enrich selected runs with additional public detail-page fields when those pages are accessible.

### How to use

1. Open the Actor in Apify Console.
2. Enter a **Keyword or job title**, such as `python` or `nurse`.
3. Optionally choose a city or province and the filters you need.
4. Set **Maximum unique jobs** and optionally enable **Fetch detail-page enrichment**.
5. Click **Start** and open the Dataset when the run finishes.

### Input

`keyword` is required and accepts a search phrase from 1 to 200 characters. The Actor creates the public InfoJobs search URL internally, so users do not need to construct or paste URLs.

`location` is optional and accepts a city or province name. Known province names are mapped to the current InfoJobs province filter; other values are sent as a city search term. Leave it empty to search across Spain.

`remote`, `contractType`, `workday`, `publishedWithinDays`, and `sortBy` are optional select fields. Their defaults are `any`, `any`, `any`, `any`, and `relevance`. The available choices correspond to filters currently exposed by the public InfoJobs search.

`maxItems` defaults to 30 and accepts values from 1 to 1,000. It is an upper bound, not a guarantee that InfoJobs has that many matching public offers.

`includeDetails` defaults to `false`. When enabled, the Actor first requests the public first-party JSON detail endpoint for each delivered offer, using the listing's public `code` and `newBOId`; it falls back to the detail HTML when necessary. A challenge or unavailable detail response does not remove the successfully collected listing.

Network transport and fallback behavior are managed internally by the Actor and
are not part of the public input contract.

Example:

```json
{
  "keyword": "python",
  "location": "Madrid",
  "remote": "any",
  "contractType": "any",
  "workday": "any",
  "publishedWithinDays": "any",
  "sortBy": "relevance",
  "maxItems": 30,
  "includeDetails": false
}
```

### Output

The default Dataset contains one item per unique public offer. Offers are deduplicated globally by their public offer code, or by their listing URL when a code is unavailable. The `searchUrl` field identifies the generated public search that delivered the item.

The `offer` object contains the public list fields `code`, `title`, `description`, `city`, `link`, `contractType`, `workday`, `teleworking`, `publishedAt`, `companyName`, `companyLogo`, `companyLink`, `states`, `upsellings`, `executive`, and `newBOId`. When detail enrichment succeeds, `offer.detail` contains additional public detail fields such as salary, experience, studies, skills, category, requirements, company ratings, applications, and social benefits when supplied by InfoJobs.

This is the complete Dataset item from a real successful run:

```json
{
  "searchUrl": "https://www.infojobs.net/ofertas-trabajo/espana?keyword=python&page=1&sortBy=RELEVANCE&onlyForeignCountry=false&countryIds=17&sinceDate=ANY&provinceIds=33",
  "scrapedAt": "2026-09-24T09:11:02Z",
  "offer": {
    "code": "694a872eb84b0ba839fa86d91335d9",
    "title": "Desarrollador Senior Python (Híbrido)",
    "description": "Como Desarrollador Senior Python, formarás parte de un equipo ágil centrado en la construcción y evolución de servicios backend escalables y robustos. Colaborarás con DevOps y Product para definir y entregar soluciones basadas en contenedores que se desplieguen en entornos de producción con alta disponibilidad, contribuyendo a la estabilidad y mejora continua del ecosistema tecnológico de la empresa.\nPython: Es el lenguaje principal de desarrollo; necesitamos que domines su ecosistema y buenas prácticas para construir código mantenible y de alto rendimiento.\nFastAPI: Debes tener experiencia en su uso para desarrollar APIs modernas, rápidas y bien documentadas, aprovechando sus capacidades asíncronas y validación de datos.\nAPIs REST y/o GraphQL: Es esencial que hayas diseñado e implementado APIs que se integren con múltiples sistemas, priorizando la claridad, seguridad y escalabilidad.\nContenedores: Debes entender cómo empaquetar y ejecutar aplicaciones en entornos aislados, asegurando consistencia entre desarrollo, prueba y producción.\nLa modalidad de trabajo es híbrida (2 - 3 días en las oficinas del cliente) en la zona de IFEMA.",
    "city": "Madrid",
    "link": "https://www.infojobs.net/madrid/desarrollador-senior-python-hibrido/of-i694a872eb84b0ba839fa86d91335d9?applicationOrigin=search-new&page=1&sortBy=RELEVANCE",
    "contractType": "Contrato indefinido",
    "workday": "Jornada completa",
    "teleworking": "Híbrido",
    "publishedAt": "2026-09-17T08:16:28Z",
    "companyName": "DEVOTEAM",
    "companyLogo": "https://multimedia-logos.infojobs.net/image/upload/c8/c8ec5db1-715e-42b0-88b0-fc43822fc2eb",
    "companyLink": "https://devoteam.ofertas-trabajo.infojobs.net",
    "states": [],
    "upsellings": [
      "PROMOTED"
    ],
    "executive": false,
    "newBOId": "ab33f2ae-c707-4945-a191-e4fbd970d48c"
  }
}
```

The run also writes a safe `SUMMARY` record to the default Key-Value Store with counters, search completion status, duplicate counts, and detail-page skips. If a search cannot be validated or completed, the run reports failure rather than presenting an incomplete search as a successful collection; Dataset items already written remain available for inspection.

When a run is resumed after an interruption, the Actor reconciles its persisted Dataset identities and pending billing intent before continuing, so recovered listings are not written or charged twice.

### Pricing

This Actor uses Apify pay-per-event pricing. You are charged once for each unique job listing successfully delivered to the default Dataset. Empty runs, failed requests, duplicate offers, and detail-page fields that are skipped do not create an `infojobs-job-result` charge. The actual charge is per delivered job result; the 1,000-result figures below are comparison equivalents.

| Apify tier | Price per job result (USD) | Equivalent per 1,000 job results (USD) |
|---|---:|---:|
| FREE | $0.00100 | $1.00 |
| BRONZE | $0.00090 | $0.90 |
| SILVER | $0.00080 | $0.80 |
| GOLD | $0.00075 | $0.75 |
| PLATINUM | $0.00075 | $0.75 |
| DIAMOND | $0.00075 | $0.75 |

Apify account-level compute, storage, and other platform usage charges, if applicable to your account, are separate from the Actor event price. Check the Actor's **Pricing** tab for the live contract before running it.

### Related Actors

| Actor | Best for |
|---|---|
| [Indeed Jobs Scraper | Worldwide Job Data](https://apify.com/datascraperes/indeed-jobs-scraper) | Compare Spanish search results with worldwide Indeed job listings and country-specific filters. |
| [Glassdoor Jobs Scraper | Global Job Listings](https://apify.com/datascraperes/glassdoor-jobs-scraper) | Compare public job listings with Glassdoor location, date, job-type, remote, and application filters. |
| [Built In Jobs Scraper | Salaries & Skills](https://apify.com/datascraperes/builtin-jobs-scraper) | Research technology jobs where Built In provides published salary and skills fields. |
| [Lever Jobs Scraper | Public ATS Job Data](https://apify.com/datascraperes/lever-jobs-scraper) | Collect public postings from Lever company career boards and compare ATS-specific listings. |

### Limits and data quality

- Coverage is limited to public InfoJobs Spain search pages, the first-party JSON detail contract exposed by the current frontend, and fields available in those responses.
- InfoJobs can return a browser-verification page even with HTTP status `200`. The Actor validates the offer state and does not count such a response as an empty result.
- A run can fail when the requested search cannot be completed. Successful Dataset items remain available, and the `SUMMARY` record identifies the generated search URL.
- Detail enrichment is best effort. Salary, skills, requirements, and other detail fields can be absent, changed, or unavailable when InfoJobs changes the JSON contract or challenges the fallback detail page.
- Search results can change while pages are being collected. Use a bounded `maxItems`, review the `SUMMARY` record, and rerun the same search when you need a refreshed snapshot.
- Empty or smaller-than-requested results are valid when the public search has fewer matching offers or when duplicate offers are encountered.

### Frequently asked questions

#### Does the Actor use an external or authenticated InfoJobs API?

No. It uses the public InfoJobs HTML search form and the public first-party JSON detail route exposed by the frontend. It does not require an API key, login, or third-party jobs API.

#### Why did I receive fewer jobs than `maxItems`?

`maxItems` is a maximum. The search may contain fewer public offers, the source may end the available pagination, or duplicate offers may be encountered while paging.

#### Are duplicate jobs returned across result pages?

No. Offers are deduplicated across the complete run by public offer code, with the listing URL as fallback.

#### What happens when InfoJobs challenges a request?

The affected search is not treated as a successful empty page. The run records the incomplete search in `SUMMARY` and fails if the promised collection could not be completed. A challenge on optional detail enrichment only skips that enrichment and preserves the base listing.

#### Does enabling detail enrichment change the core output?

No. The list fields are collected first. A `detail` object is added only when the first-party JSON contract or the HTML fallback exposes valid public data.

### Responsible use

Use this Actor only for lawful processing of public job information. Respect applicable laws, privacy requirements, InfoJobs terms, and the rights of employers and applicants. Do not use collected data for spam, harassment, discrimination, or unlawful automated decisions.

### Support

For support, open an issue in the Actor's **Issues** tab and include a reproducible input, the run ID, and the relevant `SUMMARY` status. Do not include credentials or internal configuration in an issue.

For public integration examples, see the [InfoJobs Jobs Scraper public repository](https://github.com/datacrawler-edu/infojobs-jobs-scraper-python).

# Changelog

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

# Actor input Schema

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

Required search phrase, such as python, nurse, or project manager.

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

Optional Spain location. Use a city or province name, for example Madrid or Barcelona. Leave empty to search across Spain.

## `remote` (type: `string`):

Filter by the teleworking mode shown by InfoJobs.

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

Optional InfoJobs contract filter.

## `workday` (type: `string`):

Optional InfoJobs workday filter.

## `publishedWithinDays` (type: `string`):

Limit results to jobs published in the selected period. InfoJobs currently exposes 24 hours, 7 days, and 15 days.

## `sortBy` (type: `string`):

Choose the order requested from InfoJobs.

## `maxItems` (type: `integer`):

Maximum number of unique jobs saved for this search. This is a limit, not a guaranteed result count.

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

Best-effort HTTP enrichment through InfoJobs' public detail contract, with HTML fallback. The core listing remains available when detail data is unavailable or challenged.

## Actor input object example

```json
{
  "keyword": "python",
  "location": "Madrid",
  "remote": "any",
  "contractType": "any",
  "workday": "any",
  "publishedWithinDays": "any",
  "sortBy": "relevance",
  "maxItems": 10,
  "includeDetails": false
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "keyword": "python",
    "location": "Madrid",
    "remote": "any",
    "contractType": "any",
    "workday": "any",
    "publishedWithinDays": "any",
    "sortBy": "relevance",
    "maxItems": 10,
    "includeDetails": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("datascraperes/infojobs-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 = {
    "keyword": "python",
    "location": "Madrid",
    "remote": "any",
    "contractType": "any",
    "workday": "any",
    "publishedWithinDays": "any",
    "sortBy": "relevance",
    "maxItems": 10,
    "includeDetails": False,
}

# Run the Actor and wait for it to finish
run = client.actor("datascraperes/infojobs-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 '{
  "keyword": "python",
  "location": "Madrid",
  "remote": "any",
  "contractType": "any",
  "workday": "any",
  "publishedWithinDays": "any",
  "sortBy": "relevance",
  "maxItems": 10,
  "includeDetails": false
}' |
apify call datascraperes/infojobs-jobs-scraper --silent --output-dataset

```

## MCP server setup

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