# Spain Public Tenders API (PLACSP) Radar (`oatpage/spain-public-tenders-placsp`) Actor

Spanish public tenders (licitaciones) from PLACSP open data: only new since your last run, still open for bids, filtered by CPV code, region and budget, plus award statistics (bids, discount, top winners).

- **URL**: https://apify.com/oatpage/spain-public-tenders-placsp.md
- **Developed by:** [Oatpage](https://apify.com/oatpage) (community)
- **Categories:** Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 tenders

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

## Spain Public Tenders API (PLACSP) Radar

Want to find public contracts in Spain for your sector? Get new and open tenders from the **Plataforma de Contratación del Sector Público (PLACSP)** filtered by CPV code, keywords, province and budget, ranked by deadline or relevance, with the official notice link. Awards come with bids received, discount against budget and top winners. No login, no proxies, no API key.

**Search terms this Actor answers:** Spain public tenders, PLACSP API, licitaciones públicas, Spanish public procurement, government contracts Spain, CPV code search, tender alerts, contract awards Spain, B2G leads.

> Independent tool. Not affiliated with, endorsed by or operated by the Spanish Ministry of Finance or the PLACSP. Source: Plataforma de Contratacion del Sector Publico, Ministerio de Hacienda (open data, reused under Ley 37/2007). Data is shown as published, without altering its meaning; each item includes the `updated` date of the source and the official notice link (`url`). Always check the official notice before bidding. Not legal or procurement advice.

### What you can do

- Daily alerts for tenders in your sector: filter by **CPV code**, keywords, province/region, budget range and status.
- Competitor and market monitoring: who wins which contracts, for how much, with how many bids.
- Lead lists of public buyers by CPV and region.

### Radar mode

Most readers of this feed just filter it. This one is built to follow a market:

- **Only new**: with `onlyNewSinceLastRun` each run returns only tenders new or changed since the previous run of the same `watchName`. Schedule it daily and you get a clean alert list. If more tenders match than `maxItems`, the position is not advanced, so nothing is lost.
- **Still open**: `onlyOpenForBids` keeps tenders whose deadline is today or later; every tender carries `daysToDeadline` and `deadlineStatus`. `sortBy: "deadline"` puts the ones closing soonest first.
- **Relevance**: with `keywords` and/or `cpvPrefixes`, each tender gets a `score` (0-100: keywords in the title count most, then the contracting body, then the CPV match) and `matchedKeywords`. `sortBy: "score"` ranks them.
- **Discount**: awarded tenders carry `discountPct` = (budget without VAT - awarded amount) / budget without VAT, when both figures exist.
- **Market statistics**: one `market` record per run, computed from the awarded tenders that match your search. It is not billed.

Example input:

```json
{"keywords":["limpieza","mantenimiento"],"provinces":["Málaga"],"onlyNewSinceLastRun":true,"watchName":"cleaning-malaga","onlyOpenForBids":true,"sortBy":"deadline","maxPages":10}
```

Real `market` record (3 pages of the feed, all awarded tenders in them, 11-10-2026; 2 of the 10 winners shown):

```json
{"type":"market","awardedTenders":536,"avgBidsReceived":5.54,"discountSample":510,"avgDiscountPct":23.05,"medianDiscountPct":8.73,"topWinners":[{"nif":"A33912445","name":"ASTURSERVICIOS LA PRODUCTORA S.A.","awards":35,"totalAwardAmountEur":1233175},{"nif":"A39008826","name":"Alvarez Forestal, S.A.","awards":10,"totalAwardAmountEur":41620}],"note":"Companies only; natural persons are never listed. ..."}
```

Prefer the **median** discount: a few lots with odd budgets pull the average up (here 23 % average against 8.7 % median). Statistics describe the tenders in the pages you read (`maxPages`), not the whole country. Winners that are natural persons are never listed (their tax id and name are hidden, as in the rest of the output).

### Input

| Field | Meaning |
|---|---|
| `keywords` | Words in title or authority name (accent and case insensitive). Any match keeps the tender. |
| `cpvPrefixes` | CPV prefixes, e.g. `72` IT services, `45` works, `33` medical. |
| `provinces` | Province or region contains, e.g. `Madrid`, `Galicia`. |
| `authorityContains` | Text in authority name or hierarchy. |
| `statuses` | `PUB` open, `EV` evaluation, `ADJ` awarded, `RES` resolved, `ANUL` cancelled. |
| `contractTypes` | `Supplies`, `Services`, `Works`... |
| `minAmount`, `maxAmount` | Budget range in EUR without VAT. |
| `updatedSince` | ISO date; stops reading older pages. Ideal for scheduled daily runs. |
| `maxPages` | Feed pages to read (about 500 updates each). Default 3. |
| `onlyNewSinceLastRun`, `watchName` | Radar: only what is new for this watch | `false`, `default` |
| `onlyOpenForBids` | Only tenders with deadline today or later | `false` |
| `sortBy` | `updated`, `deadline`, `score` or `amount` | `updated` |
| `includeMarketStats` | Add the `market` record (not billed) | `true` |
| `maxItems` | Output cap (and spending cap). Default 100. |

Empty input works: it returns the latest 100 updates.

### Output (example)

```json
{
  "id": "https://contrataciondelestado.es/sindicacion/licitacionesPerfilContratante/19534051",
  "expedient": "2026/C003/000004",
  "title": "Aglomerado VP DP5604",
  "updated": "2026-10-05T19:55:09.195+02:00",
  "status": "ADJ",
  "statusLabel": "Awarded",
  "contractType": "Works",
  "contractingAuthority": "Junta de Gobierno Local del Concello de Negreira",
  "authorityNif": "P1505700C",
  "authorityHierarchy": ["Negreira", "Ayuntamientos", "A Coruna", "Galicia", "ENTIDADES LOCALES", "Sector Publico"],
  "province": "A Coruna",
  "estimatedValueEur": 87357.9,
  "budgetWithoutVatEur": 87357.9,
  "budgetWithVatEur": 105703.06,
  "cpvCodes": ["45233140"],
  "submissionDeadline": "2026-05-11 23:59:00",
  "winners": [{ "nif": "B70134143", "name": "EXCAVACIONES YONI TUNAS S.L.", "naturalPerson": false, "awardDate": "2026-10-01", "bidsReceived": 3, "awardAmount": 83863.58 }],
  "url": "https://contrataciondelestado.es/wps/poc?uri=deeplink:detalle_licitacion&idEvl=..."
}
```

### Ready-to-use input examples

Daily alert, IT services tenders still open in Madrid (schedule it once a day):

```json
{ "cpvPrefixes": ["72"], "provinces": ["Madrid"], "statuses": ["PUB"], "updatedSince": "2026-10-05", "maxItems": 100 }
```

Who is winning construction contracts in Galicia above 100,000 EUR:

```json
{ "cpvPrefixes": ["45"], "provinces": ["Galicia"], "statuses": ["ADJ"], "minAmount": 100000, "maxPages": 10 }
```

Everything a ministry publishes about "software":

```json
{ "authorityContains": "Ministerio", "keywords": ["software", "licencias"], "maxPages": 5 }
```

### Frequently asked questions

**Is this the official PLACSP API?** No. It reads the public open-data feed and returns clean JSON. Independent tool.
**Can I get the full history?** No: the feed holds recent updates only. Schedule the actor daily with `updatedSince` and keep your own archive.
**Which language are the fields in?** Field names and status labels are in English; titles and authority names are copied as published (Spanish).
**How do I get alerts in Slack or e-mail?** Schedule the actor and connect an Apify integration (Slack, Zapier, Make, webhook).
**What does a run cost?** 0.002 USD per tender; set `maxItems` to cap it.

### Pricing

Pay per event: **0.002 USD per tender** (start is free). 1,000 tenders = 2 USD. Example: a daily alert for "software" in Madrid typically returns a few dozen tenders = a few cents per run. Set `maxItems` to cap spending.

### Privacy

Only public-sector procurement data. If the winner is a natural person (individual NIF/NIE/DNI), neither the name nor the ID number is output: only the flag `naturalPerson: true` and the award data (date, amount, bids). No contact emails or phones of officials are output. Do not try to re-identify natural persons by combining this output with other sources. If you process personal data you are responsible, as controller, for complying with GDPR. To request removal of any item, contact us through the Apify actor page.

### Limits

- The feed shows recent updates only (each page about a few hours of activity); it is not a full historical archive. Increase `maxPages` for more depth.
- A tender may appear several times in the feed with different versions; the newest is returned once per run.
- Covers contracts published on PLACSP (central, regional and local bodies that use it); some regional platforms are not included. Minor contracts are out of this feed.
- Source availability depends on the PLACSP servers.
- If the official server certificate chain cannot be validated in the run environment, the actor warns in the log and retries that single public read-only URL without verification.

# Actor input Schema

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

Keep tenders whose title or contracting authority contains at least one keyword (case and accent insensitive). Empty = no filter.

## `cpvPrefixes` (type: `array`):

Keep tenders with a CPV code starting with any of these, e.g. 72 (IT services), 45 (works), 33 (medical). Empty = no filter.

## `provinces` (type: `array`):

Province or region name contains, e.g. Madrid, Galicia, Valencia. Empty = all Spain.

## `authorityContains` (type: `string`):

Text in the authority name or its hierarchy, e.g. 'Ministerio', 'Diputacion'.

## `statuses` (type: `array`):

PUB = open for bids, EV = under evaluation, ADJ = awarded, RES = resolved, ANUL = cancelled. Empty = all.

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

Supplies, Services, Works, etc. Empty = all.

## `minAmount` (type: `integer`):

Only tenders with at least this budget.

## `maxAmount` (type: `integer`):

Only tenders with at most this budget.

## `updatedSince` (type: `string`):

ISO date or datetime (e.g. 2026-10-05). Stops reading older feed pages. Use it for daily alerts.

## `maxPages` (type: `integer`):

Each page holds about 500 recent updates (roughly the last few hours). Raise it to go further back.

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

Maximum tenders to output (and be charged for).

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

Remembers the newest entry it has seen (per 'Watch name') and returns only tenders new or changed after it. If more matches exist than 'Max items', the position is not advanced so nothing is lost.

## `watchName` (type: `string`):

Name of this radar. Use a different name for each search you follow (e.g. 'cleaning-andalucia') so each keeps its own position.

## `onlyOpenForBids` (type: `boolean`):

Keeps tenders whose submission deadline is today or later. Tenders without a readable deadline are dropped.

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

Order of the results. 'Relevance' needs keywords or CPV prefixes.

## `includeMarketStats` (type: `boolean`):

Adds one record (type 'market') computed from the awarded tenders that match your search: average bids received, discount versus budget (average and median) and the companies that win most. Not billed.

## Actor input object example

```json
{
  "keywords": [],
  "cpvPrefixes": [],
  "provinces": [],
  "statuses": [],
  "contractTypes": [],
  "maxPages": 3,
  "maxItems": 100,
  "onlyNewSinceLastRun": false,
  "watchName": "default",
  "onlyOpenForBids": false,
  "sortBy": "updated",
  "includeMarketStats": true
}
```

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("oatpage/spain-public-tenders-placsp").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("oatpage/spain-public-tenders-placsp").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 '{}' |
apify call oatpage/spain-public-tenders-placsp --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,oatpage/spain-public-tenders-placsp"
        }
    }
}
```

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/l77cpkDePXoQlJJut/builds/kIdhvvOjfMoikNO6r/openapi.json
