# simap.ch Switzerland Tender Scraper (`publicmoney/simap-switzerland-scraper`) Actor

Extract Swiss public procurement publications from simap.ch: tenders, awards, direct awards, advance notices and competitions from federal, cantonal and communal buyers, with the full publication detail.

- **URL**: https://apify.com/publicmoney/simap-switzerland-scraper.md
- **Developed by:** [Public Money](https://apify.com/publicmoney) (Apify)
- **Categories:** Business
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.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.
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

This Actor reads Swiss public procurement publications from simap.ch, the joint platform of the Confederation, the cantons and the communes. It returns one record per project with that project's newest publication in your window: tenders, awards, direct awards, advance notices, requests for information, competitions, revocations and abandonments, each with the full publication as simap.ch serves it.

### What it does

- Reads the same public API the simap.ch search screen calls. No account, no API key.
- Filters at the source: date window, keyword, publication type, order type, procedure, CPV, BKP, canton and the Switzerland-only flag all go to simap.ch.
- Walks simap.ch's cursor pagination (20 per page) to the end of the window, or until **Max items**.
- With **Include publication details** on (default), adds the full publication (buyer address and contacts, description, CPV/BKP/NPK codes, deadlines, Q\&A dates, terms, criteria, lots, award decision with winning vendors, prices and number of offers) and the project's earlier publications.

### Input

| Field | Type | Default | What it controls |
| --- | --- | --- | --- |
| `publishedWithinDays` | integer | `7` | Rolling window, in days back from the run |
| `publishedFrom` | string | empty | Newest publication on or after this date (YYYY-MM-DD); overrides the rolling window |
| `publishedTo` | string | empty | Newest publication on or before this date |
| `search` | string | empty | simap.ch full-text search, matched in `language` |
| `language` | string | `de` | `de`, `fr`, `it` or `en`: the keyword's match language, and the language of `titleText`, `procOfficeNameText`, `cpvLabel` and `projectUrl` |
| `publicationTypes` | array | empty | `tender`, `award_tender`, `direct_award`, `advance_notice`, `request_for_information`, `competition`, `award_competition`, `study_contract`, `award_study_contract`, `participant_selection`, `selective_offering_phase`, `revocation`, `abandonment` |
| `projectSubTypes` | array | empty | `construction`, `service`, `supply`, `project_competition`, `idea_competition`, `overall_performance_competition`, `project_study`, `idea_study`, `overall_performance_study`, `request_for_information` |
| `processTypes` | array | empty | `open`, `selective`, `invitation`, `direct`, `no_process` |
| `cpvCodes` | array | empty | 8-digit CPV codes; a code also matches the codes beneath it |
| `bkpCodes` | array | empty | Swiss BKP building cost codes |
| `cantons` | array | empty | Canton of the place of performance (`ZH`, `BE`, …) |
| `onlySwitzerland` | boolean | `false` | simap.ch's own "place of performance in Switzerland" filter |
| `includeDetails` | boolean | `true` | Adds `publicationDetails`, `pastPublications` and per-lot detail; two extra requests per record |
| `maxItems` | integer | `1000` | Caps records returned; `0` returns the whole window |
| `schemaOrg` | boolean | `false` | Appends a schema.org `schemaOrg` block to each record |

Multiple values in one filter are ORed by simap.ch. An unknown value is refused by simap.ch with `400 Invalid parameter` and the run fails with that message.

### Output

One record per project, keyed on `publicationId`:

`projectId`, `projectNumber`, `publicationId`, `publicationNumber`, `publicationDate`, `pubType`, `corrected`, `projectType`, `projectSubType`, `processType`, `lotsType`, `titleText`, `title` (de/en/fr/it), `procOfficeNameText`, `procOfficeName` (de/en/fr/it), `orderAddressOnlyDescription`, `countryId`, `cantonId`, `orderAddress`, `lots`, `offerDeadline`, `cpvCode`, `cpvLabel`, `projectUrl`, `publicationDetails`, `pastPublications`, `scrapedAt`.

`publicationDetails` is the publication object exactly as simap.ch serves it, under its own key names (`project-info`, `procurement`, `dates`, `terms`, `criteria`, `lots`, `decision`, `base`, `ted`, …); which keys appear depends on the publication type.

### Notes

- simap.ch's search starts on 2024-07-01, when the current platform went live. Earlier dates return nothing.
- The search returns each project once, with its newest publication. A project's earlier publications are listed in `pastPublications` (per lot, under `lots`, for lot projects).
- `offerDeadline` is published only on tenders, competitions, requests for information and some advance notices; awards, direct awards, revocations and abandonments carry none. Revocations and abandonments publish no CPV code.
- The keyword is matched in the chosen `language`: `Schule` over 2026-09-01 to 2026-10-03 matched 330 projects with `de` and 5 with `en`.
- `projectUrl` is `https://www.simap.ch/<language>/project-detail/<projectId>`, the form simap.ch's own help text gives for a project page.

# Actor input Schema

## `publishedWithinDays` (type: `integer`):

Return projects whose newest publication is from the last N days. Ignored when Published from is set. Examples: 1, 7, 30. Default is 7.

## `publishedFrom` (type: `string`):

Date in YYYY-MM-DD form. Overrides Published within. simap.ch's search starts at 2024-07-01. Examples: '2026-09-01'.

## `publishedTo` (type: `string`):

Date in YYYY-MM-DD form. Leave empty for today. Examples: '2026-09-30'.

## `search` (type: `string`):

simap.ch's own full-text search, matched in the chosen Language. Examples: 'Schule', 'école', 'Software'.

## `language` (type: `string`):

Language simap.ch matches the keyword in, the language of titleText, procOfficeNameText and cpvLabel (falling back to de, fr, it, en when the publication has no text in it), and of projectUrl. Default is de.

## `publicationTypes` (type: `array`):

Type of the project's newest publication. Leave empty for all.

## `projectSubTypes` (type: `array`):

Construction, service, supply, competitions and studies. Leave empty for all.

## `processTypes` (type: `array`):

Procurement procedure. Leave empty for all.

## `cpvCodes` (type: `array`):

8-digit CPV codes; a code also matches the codes under it. Examples: '45000000' (construction work), '72000000' (IT services).

## `bkpCodes` (type: `array`):

Swiss building cost plan (BKP) codes. Examples: '275' (lock systems), '211'.

## `cantons` (type: `array`):

Canton of the place of performance. Leave empty for all.

## `onlySwitzerland` (type: `boolean`):

Keep only publications simap.ch marks as performed in Switzerland (its own 'Switzerland only' filter). Off by default.

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

Adds the full publication (buyer address and contacts, description, CPV/BKP codes, deadlines, lots, award decision with winning vendors and prices, criteria) and the project's earlier publications. Two extra requests per record. On by default.

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

Maximum number of publications to return. 0 returns everything in the window. Examples: 10, 1000. Default is 1000.

## `schemaOrg` (type: `boolean`):

Appends a `schemaOrg` object to the end of every record, mapping it to schema.org (Demand for tenders, MonetaryGrant for grants, Invoice for payments). The record's own fields are not changed. Off by default. Charged once per run, not per record.

## Actor input object example

```json
{
  "publishedWithinDays": 7,
  "language": "de",
  "publicationTypes": [],
  "projectSubTypes": [],
  "processTypes": [],
  "cpvCodes": [],
  "bkpCodes": [],
  "cantons": [],
  "onlySwitzerland": false,
  "includeDetails": true,
  "maxItems": 1000,
  "schemaOrg": false
}
```

# Actor output Schema

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

Publications matched by the run, one item per record, in the default dataset.

# 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 = {
    "publishedWithinDays": 7,
    "maxItems": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("publicmoney/simap-switzerland-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 = {
    "publishedWithinDays": 7,
    "maxItems": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("publicmoney/simap-switzerland-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 '{
  "publishedWithinDays": 7,
  "maxItems": 1000
}' |
apify call publicmoney/simap-switzerland-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,publicmoney/simap-switzerland-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/YpXFOCTVHPAambYv6/builds/WGd9edKTtxrwQOb5O/openapi.json
