# GSA Labor Rate Benchmarks (CALC+) (`maxbeuvelet/gsa-labor-rate-benchmarks`) Actor

Benchmark GSA CALC+ ceiling labor rates for many labor categories at once, export rate records, and track rate changes for vendors or contracts. Public GSA data.

- **URL**: https://apify.com/maxbeuvelet/gsa-labor-rate-benchmarks.md
- **Developed by:** [Max Beuvelet](https://apify.com/maxbeuvelet) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 rate records

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

## GSA Labor Rate Benchmarks (CALC+)

Benchmark GSA schedule ceiling labor rates for a whole list of labor categories in one run, export the
underlying rate records, and track rate changes for the vendors, contracts or labor categories you watch.

Data comes from GSA's public CALC+ Quick Rate API. This Actor is not affiliated with or endorsed by the U.S. General Services Administration.

### What can this Actor do?

- **Benchmark:** paste up to 200 labor category titles. Get one row per title with the number of matching
  rates, the minimum, 10th, 25th, 50th, 75th and 90th percentile, maximum, mean and standard deviation of
  current-year ceiling rates, and the labor categories that matched.
- **Search and export:** vendor names match regardless of capitalization (spell them as on the GSA schedule). Up to 10,000 rate records per run with vendor, contract number, SIN, education,
  experience, worksite, business size and current, next-year and second-year rates.
- **Watch:** track up to 20 vendors, contracts or labor categories. Each scheduled run reports only rates that
  were added, removed or re-priced since the previous run.

### Who is it for?

Pricing and capture teams at federal contractors, consultants who prepare price-to-win analyses, and data
teams who want GSA labor rate data in a spreadsheet, database or AI agent.

### How do I benchmark GSA labor rates for many labor categories?

1. Set **Mode** to *Benchmark labor categories*.
2. Paste your labor categories, one per line. Optional filters (education, experience, worksite, business
   size, clearance, SIN, price range) apply to every row. The Actor checks that GSA applied each filter;
   a row whose filter GSA did not apply is not delivered or charged.
3. Click **Start**, then download CSV, Excel or JSON, or send the results to Google Sheets.

### How do I track competitors' GSA rate changes?

Set **Mode** to *Watch for rate changes*, add vendors (names match regardless of capitalization; spell them as on the GSA schedule) or contract numbers, save the input as a task and add a
daily schedule. GSA refreshes the data once a night, so one run a day is enough. If your maximum cost per run
cannot cover all changes of a target, that target is skipped whole and reported again next run, so no change
is lost or charged twice. A watched target whose GSA record was skipped is not compared or charged that run.

### How much does it cost?

| Event | Price on the Free plan | Charged when |
|---|---|---|
| Rate record | $1.50 per 1,000 | a rate record is written to your dataset |
| Benchmark row | $0.02 | a labor category with at least one matching rate is benchmarked |
| Watch check | $0.002 | a watched target is compared against a newer GSA data refresh |
| Rate change | $0.003 | an added, removed or re-priced rate is reported |
| Actor start | Apify's default | the run starts |

Paid Apify plans get lower event prices. You do not pay for platform usage. Benchmark rows with no matching
rates and watch checks with no new GSA data are free. Examples: 20 rate records ≈ $0.03; a benchmark of 40
labor categories = $0.80; a daily watch of 5 vendors for 30 days = $0.30 plus $0.003 per change.

### Input example

```json
{
  "mode": "benchmark",
  "benchmarkLaborCategories": [
    "Project Manager"
  ]
}
```

### Output example

Excerpt of a recorded fixture run; the `sourceUrl` field is left out here.

```json
[
  {
    "type": "benchmark",
    "retrievedAt": "2026-10-22T12:30:02.452Z",
    "laborCategoryQuery": "Project Manager",
    "match": "contains",
    "filtersApplied": {},
    "sampleSize": 15235,
    "currentPrice": {
      "min": 27.5,
      "p10": 99.46,
      "p25": 124.5,
      "median": 152.95,
      "p75": 184.24,
      "p90": 222.58,
      "max": 458.39,
      "mean": 158.26,
      "stdDev": 51.75
    },
    "topMatchedCategories": [
      {
        "name": "Project Manager",
        "count": 2974
      },
      {
        "name": "Project Manager II",
        "count": 921
      },
      {
        "name": "Project Manager I",
        "count": 848
      },
      {
        "name": "Project Manager III",
        "count": 624
      },
      {
        "name": "Senior Project Manager",
        "count": 469
      }
    ],
    "businessSizeMix": {
      "small": 11288,
      "otherThanSmall": 3947
    },
    "worksiteMix": {
      "customer": 7991,
      "contractor": 6411,
      "virtual": 833
    },
    "percentilesApproximate": true,
    "sourceRefreshedAt": "2026-10-22T02:00:02Z"
  }
]
```

### Use it from the API, Google Sheets or an AI agent

Agents that use the Apify MCP server can find and run this Actor; each run is charged by the events above.
Heavy users can add their own free api.data.gov key under Advanced to get their own GSA rate limit.

### Data source, freshness and limits

- Source: GSA CALC+ Quick Rate API, ceiling rates endpoint, refreshed every night by GSA; every record shows
  the refresh it came from (`sourceRefreshedAt`).
- Rates are awarded ceiling hourly rates on GSA schedule contracts: the most a contractor may charge under the contract, not the price an agency paid on a particular order.
- GSA's API returns at most 10,000 records per query; add filters to narrow a query.
- Percentiles are computed by GSA's API and can be approximate for large samples.
- Contains matching is GSA's keyword search, which also matches vendor names and contract numbers. Every
  benchmark row lists the labor categories that matched; use exact matching when you need it.
- No AI is used to produce the data. Results are data, not pricing or procurement advice.

### Privacy

Your input is sent only to GSA's API to run your query. The Actor does not log your input or send it anywhere else.
Watch snapshots are stored in a key-value store named `gsa-labor-rates-watch` in your own Apify account;
delete it under Storage to reset your watches. Results contain company names and contract numbers only.
If you open an issue, the maintainer keeps its title and link in a private log for up to 180 days.

### FAQ

- **Is this official GSA data?** It is GSA's public data, unchanged except for field names and formatting.
  This Actor is not a GSA product.
- **Can I reuse the data?** U.S. federal government works generally have no copyright; GSA's website
  policies give details.
- **Why did my run stop early?** You reached your maximum cost per run or GSA's rate limit. Everything
  delivered is in the dataset and you are charged only for delivered items.
- **How do I report a problem?** Open an issue on this page.

### Changelog

Version history is on the Changelog tab of this Actor's page.

# Changelog

This Actor's version history is a separate document: https://apify.com/maxbeuvelet/gsa-labor-rate-benchmarks/changelog.md

# Actor input Schema

## `mode` (type: `string`):

Search exports matching rate records. Benchmark summarizes the rates for each labor category you list. Watch compares today's rates for your vendors, contracts or labor categories with your previous run.

## `keyword` (type: `string`):

Words to look for in labor category, vendor or contract number (partial matches count). Ignored when you fill in one of the exact fields below.

## `laborCategoryExact` (type: `string`):

Find rate records whose labor category is exactly this text. Leave empty to use the keyword instead.

## `vendorExact` (type: `string`):

Find rate records for this vendor, spelled as it appears on the GSA schedule; capitalization does not matter. Leave empty to use the keyword instead.

## `contractNumber` (type: `string`):

Find rate records for one contract, for example GS-35F-0000X. Leave empty to use the keyword instead.

## `maxResults` (type: `integer`):

The most rate records the run returns. You are charged only for records returned, so a lower number costs less.

## `sort` (type: `string`):

Return the cheapest rates first or the most expensive rates first.

## `benchmarkLaborCategories` (type: `array`):

Job titles to summarize, one per line, for example Project Manager. Each gets one summary with minimum, median, average and maximum hourly rates.

## `benchmarkMatch` (type: `string`):

Contains also counts longer titles such as Senior Project Manager. Exact counts only records with exactly that title.

## `watchVendors` (type: `array`):

Vendor names to watch, one per line, spelled as they appear on the GSA schedule; capitalization does not matter. Between vendors, contracts and labor categories you can list 1 to 20 targets in total.

## `watchContracts` (type: `array`):

Contract numbers to watch, one per line, for example GS-35F-0000X.

## `watchLaborCategories` (type: `array`):

Labor category names to watch, one per line.

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

A label for this watch list: 2 to 40 lowercase letters, digits or hyphens. Runs with the same name are compared with each other; use a new name for a separate list.

## `maxRecordsPerTarget` (type: `integer`):

The most rate records checked for each watched target. Targets with more records than this are cut off and reported as incomplete.

## `includeBaselineRecords` (type: `boolean`):

On the first run for a watch name, also return every rate record found. Off by default, so the first run only saves a baseline.

## `educationLevels` (type: `array`):

Keep only records for these minimum education levels. Leave empty for all levels.

## `minYearsExperience` (type: `integer`):

Keep only records that ask for at least this many years of experience (0 to 40).

## `maxYearsExperience` (type: `integer`):

Keep only records that ask for at most this many years of experience (0 to 40). Must not be below the minimum.

## `priceMin` (type: `number`):

Keep only records priced at or above this many US dollars per hour (0 to 1,000).

## `priceMax` (type: `number`):

Keep only records priced at or below this many US dollars per hour (0 to 1,000). Must not be below the minimum.

## `worksite` (type: `string`):

Where the work is done: at the customer's site, at the contractor's site, or virtually. Any means no filter.

## `businessSize` (type: `string`):

Keep only small businesses or only other-than-small businesses. Any means no filter.

## `securityClearance` (type: `string`):

Keep only records where a security clearance is required, or only where it is not. Any means no filter.

## `sin` (type: `string`):

Keep only records under this one GSA Special Item Number, for example 54151S. Enter a single value without commas or vertical bars.

## `apiDataGovKey` (type: `string`):

Your own free api.data.gov key. It is sent only to the GSA service as a request header and is never logged or stored. Leave empty to use the shared limit.

## Actor input object example

```json
{
  "mode": "search",
  "keyword": "Project Manager",
  "maxResults": 20,
  "sort": "price_asc",
  "benchmarkLaborCategories": [
    "Project Manager",
    "Systems Engineer",
    "Business Analyst"
  ],
  "benchmarkMatch": "contains",
  "watchName": "default",
  "maxRecordsPerTarget": 2000,
  "includeBaselineRecords": false,
  "worksite": "any",
  "businessSize": "any",
  "securityClearance": "any"
}
```

# Actor output Schema

## `rates` (type: `string`):

GSA CALC+ ceiling-rate records (search mode).

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

Rate statistics per labor category (benchmark mode).

## `changes` (type: `string`):

Detected changes in watched rates (watch mode).

## `summary` (type: `string`):

Counts, warnings and outcome of the run.

# 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 = {
    "mode": "search",
    "keyword": "Project Manager",
    "maxResults": 20,
    "benchmarkLaborCategories": [
        "Project Manager",
        "Systems Engineer",
        "Business Analyst"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maxbeuvelet/gsa-labor-rate-benchmarks").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 = {
    "mode": "search",
    "keyword": "Project Manager",
    "maxResults": 20,
    "benchmarkLaborCategories": [
        "Project Manager",
        "Systems Engineer",
        "Business Analyst",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("maxbeuvelet/gsa-labor-rate-benchmarks").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 '{
  "mode": "search",
  "keyword": "Project Manager",
  "maxResults": 20,
  "benchmarkLaborCategories": [
    "Project Manager",
    "Systems Engineer",
    "Business Analyst"
  ]
}' |
apify call maxbeuvelet/gsa-labor-rate-benchmarks --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maxbeuvelet/gsa-labor-rate-benchmarks"
        }
    }
}
```

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/CwqAn2nuqLWfDY9EN/builds/8e75LcCoSF4eqP4uw/openapi.json
