# Korea Bid Price Benchmark (낙찰률 분포) — Where Winning Bids Land (`atomo-studio/korea-g2b-bid-benchmark`) Actor

Award-rate distributions for Korean public tenders: p10/median/p90 plus a target band, grouped by category, agency, region or contract size. Construction awards cluster inside about one percentage point; goods spread over nineteen. Computed from Public Procurement Service (조달청) award data.

- **URL**: https://apify.com/atomo-studio/korea-g2b-bid-benchmark.md
- **Developed by:** [Atomo Studio](https://apify.com/atomo-studio) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $100.00 / 1,000 benchmark groups

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

## Korea Bid Price Benchmark (낙찰률 분포) — Where Winning Bids Actually Land

Every other Korean procurement dataset hands you rows. This one hands you the number
you were going to compute from them anyway: **where the award rate clusters** for your
category, your agency, your contract size.

The award rate (낙찰률) is the winning bid as a percentage of the predetermined price.
Any single award tells you almost nothing. The *distribution* tells you everything —
and it is not remotely uniform:

| Category | Size | n | Target band (p25–p75) | Spread (p10→p90) |
|---|---|---:|---|---:|
| Construction | 200M–1B KRW | 289 | **90.26 – 90.45 %** | **0.56 %p** |
| Construction | 50M–200M KRW | 734 | 90.16 – 90.48 % | 0.99 %p |
| Services | 10M–50M KRW | 565 | 88.08 – 92.72 % | 11.47 %p |
| Goods | under 10M KRW | 342 | 80.61 – 97.02 % | 33.48 %p |

*30-day sample, 5,000 awards, taken 2026-09-04.*

Read that top row again. For a mid-size construction contract, **half of all winners
land inside a 0.2 percentage-point window.** Bid outside it and you are not competing —
you are donating an entry. Meanwhile small goods tenders spread across 33 points, which
is its own useful finding: there is no band to hit, so stop trying to shave basis points
and compete on something else.

You cannot see either fact from a single record.

### What you get

One record per group:

```json
{
  "category": "Construction",
  "amount_band": "200M-1B KRW",
  "sample_size": 289,
  "award_rate_p10": 90.13,
  "award_rate_p25": 90.26,
  "award_rate_median": 90.35,
  "award_rate_p75": 90.45,
  "award_rate_p90": 90.69,
  "award_rate_min": 84.21,
  "award_rate_max": 99.90,
  "award_rate_stdev": 1.12,
  "target_band_pct": { "low": 90.26, "high": 90.45 },
  "band_width_pct_points": 0.56,
  "median_award_krw": 329834500,
  "total_award_krw": 118421563000,
  "median_bidder_count": 21.0,
  "max_bidder_count": 412,
  "period_start": "2026-08-05",
  "period_end": "2026-09-04",
  "source": "조달청 나라장터 (Public Procurement Service, Korea) via data.go.kr"
}
```

`target_band_pct` is the interquartile range — the window half of all winners landed in.
`band_width_pct_points` is p90 − p10, and it is the honesty check: a wide value means
there is no reliable band here and the median should not be trusted as a target.

### Input

| Field | Type | Notes |
|---|---|---|
| `groupBy` | array | `category`, `agency`, `region`, `amount_band` — combine freely |
| `lookbackDays` | integer | Sampling window. Default 30 |
| `endDate` | date | Sample up to this date. Defaults to today |
| `categories` | array | `goods`, `construction`, `service`, `foreign` |
| `agencyKeywords` | array | Restrict to agencies matching these, e.g. `서울특별시` |
| `minSampleSize` | integer | Groups smaller than this are dropped. Default 20 |
| `maxSamples` | integer | Awards to collect before computing. Default 5,000 |

Each dimension you add to `groupBy` splits the sample further, so widen
`lookbackDays` when you cut finely — `["agency", "amount_band"]` over 30 days will
drop most groups for want of a sample. **Groups below `minSampleSize` are discarded
rather than reported**, because a percentile from nine records is a guess wearing a
decimal point.

Awards with no award rate — negotiated contracts, design competitions — are excluded
from every statistic rather than counted as zero.

### Who this is for

- **Bidders** setting a price. Filter to your agency and size band, take the target
  band, and you know the window before you commit
- **Bid-support and estimating tools** that need a defensible reference rate rather
  than a house rule of thumb
- **Analysts** measuring how competitive a market segment actually is — `band_width`
  and `median_bidder_count` together say more than any single tender
- **AI agents** that need a calibrated prior on Korean procurement pricing

Built on the same data as **Korea Tender Winners (낙찰자 현황)** — use that Actor for
the individual awards, this one for the distribution they form.

### Notes

**Source.** [조달청 낙찰정보서비스](https://www.data.go.kr/data/15129394/openapi.do),
published on Korea's national open-data portal under an unrestricted licence
(이용허락범위 제한 없음). Every record carries a `source` field and the exact period
it was computed over.

**This is an API client, not a scraper.** Data comes from the government's own REST
API with a registered key. The g2b.go.kr website is never touched.

**No personal data.** Output is aggregate statistics only — no company is named, and
the underlying records are stripped of CEO names, phone numbers and street addresses
before they are ever counted.

**These are observed distributions, not predictions.** They describe what winning bids
did over the sampled period. Statutory floors, agency practice and formulas change;
re-run rather than caching a band and trusting it next quarter.

***

### Development

```bash
export G2B_SERVICE_KEY='...'   # data.go.kr 인증키 (다른 g2b Actor와 같은 키)
apify push                     # 배포
```

집계 로직은 `src/main.py`에 있다. 원천 필드가 바뀌면 `src/normalize.py`부터 볼 것.

# Actor input Schema

## `lookbackDays` (type: `integer`):

How far back to sample awards. Longer windows give tighter percentiles but take longer to run.

## `endDate` (type: `string`):

Sample up to this date, YYYY-MM-DD (Korea time). Defaults to today.

## `groupBy` (type: `array`):

Dimensions to break the benchmark down by. Each extra split shrinks the sample, and groups below the minimum are dropped.

## `categories` (type: `array`):

Which kinds of tender to sample.

## `agencyKeywords` (type: `array`):

Only sample awards whose demand agency contains one of these. Korean, e.g. 서울특별시, 한국도로공사.

## `minSampleSize` (type: `integer`):

Drop any group with fewer awards than this — percentiles from a handful of records are noise.

## `maxSamples` (type: `integer`):

Stop collecting after this many awards. Roughly 1,200 are registered a day.

## Actor input object example

```json
{
  "lookbackDays": 30,
  "endDate": "",
  "groupBy": [
    "category",
    "amount_band"
  ],
  "categories": [
    "goods",
    "construction",
    "service"
  ],
  "agencyKeywords": [],
  "minSampleSize": 20,
  "maxSamples": 5000
}
```

# Actor output Schema

## `benchmarks` (type: `string`):

One record per group — percentiles, target band, sample size.

## `downloadCsv` (type: `string`):

The same records as a spreadsheet.

# 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("atomo-studio/korea-g2b-bid-benchmark").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("atomo-studio/korea-g2b-bid-benchmark").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 atomo-studio/korea-g2b-bid-benchmark --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,atomo-studio/korea-g2b-bid-benchmark"
        }
    }
}

```

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/dYpfToio7c4dJIqQg/builds/IVfknw2TUitvguW6n/openapi.json
