# Kununu Company Data Fetcher (`webdesigndetail/kununu-company-data-fetcher`) Actor

Fetch public Kununu company profile data for a selected German city.

- **URL**: https://apify.com/webdesigndetail/kununu-company-data-fetcher.md
- **Developed by:** [Webdesign Detail](https://apify.com/webdesigndetail) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 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.

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

## Kununu Company Data Fetcher

Fetch public Kununu company data for a German city. The Actor accepts a city such as `Berlin`, resolves Kununu's opaque location token, follows source-provided pagination, visits each discovered company profile, and can enrich it from the employee review, salary, jobs, and culture sections.

The input city controls company discovery. Section summaries and detail records are company-wide Kununu data and are not restricted to that city. Use the Actor only for workloads for which you have authorization and in accordance with applicable law and platform terms. Kununu is a trademark of its respective owner; this project is not affiliated with Kununu.

### Why the Actor uses both HTTP and Playwright

Kununu's search, profile, and enrichment data is available in rendered HTML, so the main crawl uses Crawlee's `BeautifulSoupCrawler` for speed and lower cost. Playwright resolves a free-text city and completes Kununu's browser challenge. Challenged browser identities are retired explicitly. After a successful bootstrap, both crawlers share the same Crawlee session, cookies, Chromium user agent and TLS fingerprint, sticky proxy session, and mandatory Apify Proxy configuration. Playwright remains available as a bounded fallback.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `city` | string | `Berlin` | German city to resolve. |
| `locationToken` | string | — | Optional known Kununu `city-UUID` token; skips city resolution. |
| `maxCompanies` | integer | `2` | Maximum unique company profiles, up to 500. |
| `maxPages` | integer | `20` | Maximum search pages. |
| `includeExtendedProfile` | boolean | `true` | Include profile sections, factors, and benefits. |
| `enrichmentMode` | enum | `summary` | `none`, `summary`, `detailed`, or `full`. |
| `sections` | string array | all four | Any of `reviews`, `salary`, `jobs`, and `culture`. |
| `maxReviewsPerCompany` | integer | `10` | Review rows in Detailed or Full mode, up to 100. |
| `maxSalaryRolesPerCompany` | integer | `20` | Salary-role rows, up to 300. Full mode opens each accepted role page. |
| `maxJobsPerCompany` | integer | `30` | Job rows, up to 300. |
| `maxJobDetailsPerCompany` | integer | `0` | Individual job pages opened in Full mode, up to 50. |
| `enableBrowserFallback` | boolean | `true` | Retry exhausted HTTP requests in Playwright. |
| `maxConcurrency` | integer | `3` | Maximum HTTP request concurrency. |
| `proxyConfiguration` | object | Residential, Germany | Required Apify Proxy settings. |
| `debug` | boolean | `false` | Enable verbose diagnostics. Raw HTML is never stored. |

Example:

```json
{
  "city": "Berlin",
  "maxCompanies": 25,
  "maxPages": 20,
  "includeExtendedProfile": true,
  "enrichmentMode": "detailed",
  "sections": ["reviews", "salary", "jobs", "culture"],
  "maxReviewsPerCompany": 10,
  "maxSalaryRolesPerCompany": 20,
  "maxJobsPerCompany": 30,
  "maxJobDetailsPerCompany": 0,
  "enableBrowserFallback": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "DE"
  }
}
```

Live cloud verification showed that Kununu rejects Apify's automatic proxy pool with HTTP 403 responses. The Actor therefore defaults to the `RESIDENTIAL` group in Germany. Your Apify account must have access to this group, which has usage-based proxy costs. You can supply another available group through the standard `proxyConfiguration` editor; explicitly supplied settings are preserved. The Actor rejects `useApifyProxy: false` by design.

The Actor defaults to 1 GB of memory and 2 companies. This Store-safe prefill is sufficient for a representative result while keeping the memory-based start event plus result events within the current platform run-cost guard. Increase `maxCompanies` for production collection and set a matching maximum run charge in Apify Console or through the API. Increase memory only for deliberately larger browser-heavy runs and review the resulting start charge first.

### Enrichment modes

- `none`: fetch the base company profile only.
- `summary`: fetch one page from each selected section and add company-wide `reviewsSummary`, `salarySummary`, `jobsSummary`, and `cultureSummary` objects to the company row. This is the recommended cost-controlled default.
- `detailed`: also follow exact section pagination URLs until the configured per-company item limits and write review, salary-role, and job rows to their own datasets.
- `full`: additionally open every accepted salary-role page and up to `maxJobDetailsPerCompany` accepted job pages. Each row reports `detailStatus` as `succeeded`, `failed`, or `notRequested`.

### Dataset output

The Actor defines four run-scoped datasets:

| Alias | Contents |
| --- | --- |
| `default` | One finalized row per successfully fetched company profile, including enrichment summaries and per-section status. |
| `reviews` | Public employee review records in Detailed and Full modes. |
| `salary_roles` | Salary-role rows; Full mode adds range, comparison, monthly, and experience-band fields from role pages. |
| `jobs` | Job rows; Full mode can add plain-text and source-HTML descriptions, responsibilities, requirements, benefits, compensation highlights, apply URLs, dates, and structured salary information from job pages. |

Core company fields include:

- search context: `cityInput`, `resolvedCity`, `locationToken`, `searchPage`, and `searchRank`;
- identity: `companyName`, `companySlug`, `companyUrl`, logo and cover URLs;
- profile status: `verifiedProfile`, `activeProfile`, and `topCompany`;
- company data: locations, country, industry, company website, and social links;
- reputation data: Kununu score, review count, recommendation rate, score trend, followers, factor ratings, benefit names, and benefit percentages where exposed;
- optional extended text: company information, recruiting, locations, employee, career, salary, culture, FAQ, and award summaries;
- enrichment data: company-wide section summaries and `enrichment.sections` status, page, item, cap, and deep-detail counters;
- provenance: `sourceSearchUrl`, `scrapedAt`, schema version, and `missingFields`.

Fields absent on a source profile are omitted and listed in `missingFields` where they are part of the core profile. The dataset schema provides Overview and Extended data views in Apify Console.

### Pagination and completeness

The crawler does not manufacture pagination URLs. It follows the exact next-page `href` exposed by Kununu for search results, reviews, salary roles, and jobs. It deduplicates canonical company and record URLs and stops each collection at the first applicable condition:

- the source no longer exposes a next link;
- `maxCompanies` is reached;
- `maxPages` is reached; or
- a page fails after both HTTP and browser retry policies; or
- the applicable per-company record limit is reached.

Kununu can report more matching employers than it exposes through its search result window. When the final accessible range is lower than the reported total, `RUN_SUMMARY.isResultSetCapped` is `true`. Limits and failures also produce explicit partial-result reasons; already written valid company items are retained.

The default key-value store record `RUN_SUMMARY` contains search/profile counts, the resolved city and token, per-section company/page/item/failure counters, cap flags, browser-fallback diagnostics, timestamps, a bounded final-failure URL list, and `isPartial`. Completed company rows are emitted progressively. On Apify, the Actor also reserves 45 seconds before `ACTOR_TIMEOUT_AT` to stop accepting work, flush remaining partial rows, and finish cleanly with `TIME_BUDGET_REACHED` telemetry instead of being killed.

### Local development

Requirements:

- Python 3.12 or newer;
- Docker for the production-equivalent image;
- Apify CLI 1.9 or newer;
- an Apify account/token with access to Apify Proxy for a real target smoke test.

Create a virtual environment and run focused checks:

```bash
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements-dev.txt
.venv/bin/ruff check .
.venv/bin/pytest -q
apify validate-schema
docker build --platform linux/amd64 -t kununu-company-data-fetcher .
```

Run through the Apify CLI with an inline input:

```bash
apify run --purge --input '{"city":"Berlin","maxCompanies":3,"locationToken":"city-2fbd1016-893c-4ef0-bea2-7e709129d312","enrichmentMode":"summary","proxyConfiguration":{"useApifyProxy":true,"apifyProxyGroups":["RESIDENTIAL"],"apifyProxyCountry":"DE"}}'
```

The known token in this diagnostic skips city resolution; remove it to test the full resolver. A genuine Apify Proxy run is best verified in Apify Cloud because local proxy access depends on account credentials and plan settings.

### Docker

The image uses Apify's Python Playwright base with the browser revision matched to `playwright==1.61.0`. The Apify base is a Linux/AMD64 image, matching Apify Cloud; ARM-based development Macs should therefore keep the explicit `--platform linux/amd64` build flag shown above. The image installs runtime dependencies, compiles the package during build, and starts the Actor with `python3 -m kununu_actor`.

### GitHub CI/CD

The CI workflow lints, compiles, tests, validates all Apify schemas, and builds the Docker image. A successful push to `main` deploys the `beta` build tag. Production is separate: the `Release latest Actor build` workflow is manually dispatched and targets the protected `apify-production` environment.

Configure these GitHub Actions secrets:

- `APIFY_TOKEN`: Apify API token allowed to update the Actor;
- `APIFY_ACTOR_ID`: existing Actor ID or `username/actor-name`.

Create the `apify-beta` and `apify-production` GitHub environments. Add required reviewers to `apify-production` before the first release.

### Publication checklist

Before making the Actor public in Apify Store:

1. Run CI and build the Docker image successfully.
2. Deploy `beta` and run a small proxy-backed Berlin smoke test.
3. Confirm the default company dataset, all selected summaries, aliased detail datasets for Detailed/Full mode, pagination, and `RUN_SUMMARY` in Apify Console.
4. Test a city without a supplied token to exercise the resolver and test one invalid city for a clear failure.
5. Review proxy cost, memory, timeout, privacy, legal, pricing, support, and Store metadata settings.
6. Trigger the protected `latest` workflow and verify the live build and sample run before publishing.

Selector drift remains possible on any third-party site. Sparse results are surfaced through `missingFields`; structural failures are retried and then reported instead of being silently treated as complete.

# Actor input Schema

## `city` (type: `string`):

German city to resolve through Kununu's location suggestions, for example Berlin or Hamburg.

## `locationToken` (type: `string`):

Optional advanced override. Supply an existing city-UUID token to skip browser-based city resolution.

## `maxCompanies` (type: `integer`):

Maximum number of unique company profiles to write to the dataset.

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

Maximum number of source-provided search result pages to follow. Kununu currently exposes at most 20 pages for this search flow.

## `includeExtendedProfile` (type: `boolean`):

Include factor ratings, benefits, company information, recruiting details, locations, salary and culture summaries where present.

## `enrichmentMode` (type: `string`):

Summary reads each selected section once. Detailed also writes bounded review, salary-role, and job records. Full additionally opens salary-role pages and a bounded number of job detail pages.

## `sections` (type: `array`):

Select the company sections to fetch. Ignored when enrichment mode is None.

## `maxReviewsPerCompany` (type: `integer`):

Maximum employee-review records written in Detailed or Full mode.

## `maxSalaryRolesPerCompany` (type: `integer`):

Maximum salary-role records written. Full mode also opens each accepted role page.

## `maxJobsPerCompany` (type: `integer`):

Maximum job records written in Detailed or Full mode.

## `maxJobDetailsPerCompany` (type: `integer`):

Maximum accepted jobs whose individual detail pages are opened in Full mode. Zero keeps job-card fields only.

## `enableBrowserFallback` (type: `boolean`):

Retry pages with Playwright when all faster HTTP attempts fail. Recommended for reliability.

## `maxConcurrency` (type: `integer`):

Maximum simultaneous HTTP profile requests. Browser fallback is capped lower automatically.

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

Apify Proxy is mandatory. Select proxy groups and country settings available in your Apify account.

## `debug` (type: `boolean`):

Enable verbose logs for troubleshooting. No raw page HTML is stored.

## Actor input object example

```json
{
  "city": "Berlin",
  "maxCompanies": 2,
  "maxPages": 20,
  "includeExtendedProfile": true,
  "enrichmentMode": "summary",
  "sections": [
    "reviews",
    "salary",
    "jobs",
    "culture"
  ],
  "maxReviewsPerCompany": 10,
  "maxSalaryRolesPerCompany": 20,
  "maxJobsPerCompany": 30,
  "maxJobDetailsPerCompany": 0,
  "enableBrowserFallback": true,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DE"
  },
  "debug": false
}
```

# Actor output Schema

## `companies` (type: `string`):

Structured company profile items.

## `reviews` (type: `string`):

Review records written in Detailed and Full modes.

## `salaryRoles` (type: `string`):

Salary-role records; Full mode enriches them from individual role pages.

## `jobs` (type: `string`):

Job records; Full mode can enrich a bounded subset from job detail pages.

## `runSummary` (type: `string`):

Completeness, pagination, browser fallback and failure telemetry.

# 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 = {
    "city": "Berlin",
    "maxCompanies": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("webdesigndetail/kununu-company-data-fetcher").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 = {
    "city": "Berlin",
    "maxCompanies": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("webdesigndetail/kununu-company-data-fetcher").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 '{
  "city": "Berlin",
  "maxCompanies": 2
}' |
apify call webdesigndetail/kununu-company-data-fetcher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,webdesigndetail/kununu-company-data-fetcher"
        }
    }
}

```

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/YLNEbXOc2H5ReYr9o/builds/H0pvI1pHPeHQauvDA/openapi.json
