# Finnish Public Tenders Scraper (Hilma) (`pohjastudio/finnish-public-tenders-hilma-scraper`) Actor

Scrape Finnish public procurement notices from Hilma: titles, CPV codes, buyers, deadlines, estimated and awarded values, winners.

- **URL**: https://apify.com/pohjastudio/finnish-public-tenders-hilma-scraper.md
- **Developed by:** [Pohja Studio](https://apify.com/pohjastudio) (community)
- **Categories:** Business
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Finnish Public Tenders Scraper (Hilma)

Scrape Finnish public procurement notices from **Hilma** (`hankintailmoitukset.fi`), the official notification channel for Finnish public sector tenders. Every Finnish contracting authority — municipalities, hospital districts, parishes, state agencies, universities — is legally required to publish here, which makes it the single complete source of Finnish public demand.

No API key, no account, no login. Point the Actor at a sector and get structured notices.

### What you get

Each notice comes back as a flat record:

| Field | Description |
|---|---|
| `title`, `description` | Notice title and full description |
| `buyerName`, `buyerBusinessId`, `buyerAddress`, `buyerType` | The contracting authority |
| `cpvCodes` | CPV classification (45 = construction, 71 = engineering, 33 = medical, …) |
| `nutsCodes` | NUTS region (FI1B1 = Helsinki-Uusimaa, …) |
| `procurementType` | `works`, `services` or `supplies` |
| `datePublished`, `deadline` | Publication date and tender deadline |
| `estimatedValue` | Estimated contract value, when the buyer discloses it |
| `awardedValue`, `currency`, `winners` | On award notices: what the contract closed at and who won it |
| `isEuProcurement`, `isFrameworkAgreement`, `isCancelled`, `isCorrigendum` | Notice flags |
| `url` | Direct link to the notice on Hilma |
| `lots` | Per-lot breakdown (optional) |
| `raw` | Untouched source record (optional) |

### Use cases

- **Construction and trade companies** — find tenders in your discipline and region before the deadline, filtered by CPV instead of reading everything.
- **Competitive intelligence** — award notices carry the winner and the contract value, so you can see who is winning what, at what price.
- **Suppliers and manufacturers** — track which authorities are buying your product category.
- **Sales teams selling to the public sector** — a live feed of buying intent with the buyer's business ID attached.
- **Market research and analytics** — historical volumes and values by sector, region and buyer.

### Input

| Option | Description |
|---|---|
| `searchTerms` | Finnish keywords. Each term is a separate query returning up to 50 notices, so several terms give wider coverage. Leave as `*` to take everything matching the filters. |
| `cpvPrefix` | Keep only notices whose CPV starts with this, e.g. `45` for construction work. |
| `nutsPrefix` | Keep only notices in a region, e.g. `FI1B` for Helsinki-Uusimaa. |
| `procurementType` | `works`, `services`, `supplies`, or any. |
| `publishedAfter` / `publishedBefore` | Publication date window. |
| `deadlineAfter` | Only notices whose deadline falls on or after this date. |
| `onlyOpenForBids` | Drop cancelled notices and anything past its deadline. |
| `maxItems` | Stop after this many notices. |
| `includeLots`, `includeRawFields` | Attach the lot breakdown / the raw source record. |

#### Example: open construction tenders in Helsinki-Uusimaa

```json
{
  "searchTerms": ["julkisivu", "vesikatto", "maalaus", "peruskorjaus", "saneeraus"],
  "cpvPrefix": "45",
  "nutsPrefix": "FI1B",
  "procurementType": "works",
  "onlyOpenForBids": true,
  "maxItems": 200
}
```

#### Example: who won Finnish construction contracts this year

```json
{
  "searchTerms": ["urakka"],
  "cpvPrefix": "45",
  "publishedAfter": "2026-01-01T00:00:00Z"
}
```

### How coverage works

Hilma's search backend returns at most **50 records per query** and does not support paging. This Actor therefore widens coverage by running one query per search term and merging the results, dropping duplicates.

If a run returns fewer notices than you expect, add more specific Finnish keywords rather than raising `maxItems`. Terms that work well for construction: `julkisivu`, `vesikatto`, `maalaus`, `peruskorjaus`, `saneeraus`, `urakka`, `korjaus`, `rakentaminen`, `purku`, `piha`.

The backend also caches responses per search term, so the same term asked twice in quick succession can return the earlier response even if you changed the filters. Every record is re-checked locally against your criteria before it is written, so the output never contains records that do not match what you asked for — a cached response simply yields fewer rows.

### Pricing

This Actor is billed per notice delivered. Records that are filtered out or that duplicate an earlier record are not charged.

### Notes

Hilma notices are official public documents published under Finnish procurement law, and the data is used here as published. Some fields are only present on certain notice types — `awardedValue` and `winners` exist on contract award notices, `deadline` and `estimatedValue` on calls for tenders — so nulls in those columns are expected rather than a scraping failure.

# Actor input Schema

## `searchTerms` (type: `array`):

Finnish keywords to search for. Each term is a separate query returning up to 50 notices, so several terms give wider coverage. Leave empty to fetch everything matching the filters below.

## `cpvPrefix` (type: `string`):

Keep only notices whose CPV code starts with this. Examples: 45 = construction work, 71 = engineering services, 09 = energy, 33 = medical.

## `nutsPrefix` (type: `string`):

Keep only notices in this region. Examples: FI1B = Helsinki-Uusimaa, FI19 = Western Finland, FI1D = Northern and Eastern Finland.

## `procurementType` (type: `string`):

Restrict to works (construction), services, or supplies.

## `publishedAfter` (type: `string`):

Only notices published on or after this date.

## `publishedBefore` (type: `string`):

Only notices published before this date.

## `deadlineAfter` (type: `string`):

Only notices whose tender deadline falls on or after this date.

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

Drop cancelled notices and anything whose deadline has already passed.

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

Stop after this many notices. You are charged per notice delivered.

## `includeLots` (type: `boolean`):

Add the per-lot breakdown (title, description, CPV, region) to each notice.

## `includeRawFields` (type: `boolean`):

Attach the untouched Hilma record under `raw`. Useful when you need a field this Actor does not map yet.

## Actor input object example

```json
{
  "searchTerms": [
    "julkisivu",
    "vesikatto",
    "maalaus"
  ],
  "cpvPrefix": "45",
  "nutsPrefix": "FI1B",
  "procurementType": "any",
  "onlyOpenForBids": false,
  "maxItems": 1000,
  "includeLots": false,
  "includeRawFields": false
}
```

# Actor output Schema

## `notices` (type: `string`):

Tender notices with buyer, CPV codes, deadline, estimated and awarded values, and the winner where the notice is a contract award.

## `noticesInConsole` (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 = {
    "searchTerms": [
        "*"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pohjastudio/finnish-public-tenders-hilma-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 = { "searchTerms": ["*"] }

# Run the Actor and wait for it to finish
run = client.actor("pohjastudio/finnish-public-tenders-hilma-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 '{
  "searchTerms": [
    "*"
  ]
}' |
apify call pohjastudio/finnish-public-tenders-hilma-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pohjastudio/finnish-public-tenders-hilma-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/6fMfYkF1ubHvYN4Yd/builds/w157ecqcKHI92qge0/openapi.json
