# VETS-4212 Public Filing Verification (`automation-lab/vets-4212-contractor-records`) Actor

Search official DOL VETS-4212 public filing lists by company, year, state, ZIP or NAICS. Export employer and hiring-location identities with source provenance for supplier checks; no veteran counts or legal compliance conclusions.

- **URL**: https://apify.com/automation-lab/vets-4212-contractor-records.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.58 / 1,000 item extracteds

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

## VETS-4212 Public Filing Verification

Search official DOL **vets-4212 filings** lists to check whether a supplier name appears in a year-specific public workbook. Export employer and hiring-location identities, public addresses, NAICS codes and reproducible source provenance.

This Actor reports **public filing-list presence**, not a legal compliance conclusion. It does not access private submissions, veteran counts or employment totals. A missing match is not proof that an employer failed to file.

### Who is this for?

Procurement analysts can cross-check supplier rosters against a public government source. Supplier-onboarding teams can preserve the workbook year, row and hash behind a match. Researchers can export a filtered list without repeatedly opening a large spreadsheet.

### Why use this Actor?

- Fetch the official public workbook afresh on every run.
- Combine company, state, ZIP and NAICS filters rather than exporting unrelated rows.
- Retain source filename, worksheet row and SHA-256 for an audit trail.
- Keep year-specific rows distinct and eliminate exact duplicate public records within each year.

No DOL account, cookies or proxy configuration is required. This is a standalone public-list lookup, not a filing service or government endorsement.

### Getting started

1. Enter a company substring, such as `1 Accord`.
2. Leave `years` empty to search the newest worksheet available from DOL.
3. Optionally narrow the search with a state, ZIP or NAICS prefix.
4. Set a global `maxItems`, then run the Actor.
5. Open the default dataset or download JSON, CSV, Excel or another Apify-supported export.

Example input:

```json
{"company":"1 Accord","years":[],"maxItems":5}
```

The public workbook can be large. Even a narrow query downloads the entire archive and reads its shared-string table. `maxItems` limits returned rows, not downloaded bytes.

### Searching vets-4212 filings

The source is [DOL's VETS-4212 information page](https://www.dol.gov/agencies/vets/programs/vets4212) and its linked [public validation archive](https://www.dol.gov/sites/dolgov/files/VETS/files/vets4212-validations.zip).

At implementation time the workbook contained sheets `2025` and `2026`. These are **sheet labels**, not independently inferred reporting periods. Availability changes when DOL replaces its workbook. The Actor does not promise historical years absent from the current public download.

DOL describes filing confirmation emails as submission verification. This public-list search does not replace those confirmations or any contracting officer's process.

### Input parameters

| Parameter | Meaning |
| --- | --- |
| `company` | Case-insensitive literal substring of **Company Name**, with input leading/trailing whitespace removed. Not hiring-location name, token matching or whole-field equality. Omit for all companies. |
| `years` | Array of four-digit sheet-year strings. Empty or omitted selects newest available. Multiple values use ANY, processed newest first. An unavailable year fails explicitly. |
| `state` | Exact two-letter state/territory code, case-insensitive. |
| `zip` | Five digits match the first five source ZIP characters; ZIP+4 matches the full source value exactly. |
| `naics` | Literal 2–6 digit prefix of source NAICS. No inferred industry names. |
| `maxItems` | Global maximum of 1–10000 unique matching rows across selected sheets. Default 100. Zero/unlimited is unsupported. |

All nonempty filter fields combine with **AND**. For example, `company="Acme"` and `state="VA"` require both a company-name substring match and exact state match. Multiple years alone use ANY. Empty strings disable their corresponding filter.

### Output fields

| Field | Description |
| --- | --- |
| `recordId` | SHA-256 of the sheet year and public row values; useful for comparisons. |
| `reportType` | Source report type, currently `VETS-4212`. |
| `companyName` | Listed employer name. |
| `hiringLocationName` | Listed hiring-location name, or null. |
| `address`, `city`, `state`, `zip` | Public source address, including PO boxes where supplied. |
| `naics` | Source industry code as text, or null. |
| `sheetYear` | Literal workbook sheet label. |
| `filingListPresence` | Always true for a returned row; means present in this public workbook only. |
| `sourceRow` | One-based worksheet row number. |
| `sourceUrl`, `sourcePageUrl` | Official archive and information-page URLs. |
| `sourceWorkbook` | Original downloaded workbook filename. |
| `sourceSha256` | Hash of the XLSX bytes for reproducibility. |
| `retrievedAt` | UTC retrieval time. |

Missing optional source values are null. Source ZIP and NAICS values are retained as text, without guessed corrections. The default dataset contains one record type only: matching public filing-list rows.

### Output example

This shortened example is based on a current public-source local result; full dataset records also include the URLs, filename and hash described above.

```json
{
  "companyName":"1 Accord Consulting LLC",
  "hiringLocationName":null,
  "address":"8014C Hammock Lane",
  "city":"Pasadena",
  "state":"MD",
  "zip":"21122",
  "naics":"541330",
  "sheetYear":"2026",
  "filingListPresence":true,
  "sourceRow":2
}
```

The key-value store's search summary lists selected/available years, scanned and returned rows, archive size and whether the row limit was reached. It is included with the run. Source row counts are not employee or veteran counts.

### How much does it cost to verify VETS-4212 public filing presence?

The Actor uses pay-per-event pricing: one `start` event per valid run and one `item` event per produced public filing-list row. Rejected rows, duplicates and empty search results have no item charge. A valid no-match search still incurs the start fee because it downloads and scans the public source.

The run-start event costs **$0.005**. Per public filing-list row:

| Spend tier | Price per row |
| --- | ---: |
| FREE | $0.010686 |
| BRONZE | $0.009292 |
| SILVER | $0.0072478 |
| GOLD | $0.0055752 |
| PLATINUM | $0.0055752 |
| DIAMOND | $0.0055752 |

For example, at BRONZE the event fees are $0.014292 for one row, $0.05146 for five rows, and $0.9342 for 100 rows, including one start event. A valid no-match search costs $0.005 in Actor event fees. Spend tiers follow your qualifying aggregate monthly Store spend, not a per-Actor volume schedule.

The displayed Apify pricing panel is authoritative. Pricing varies by subscription tier; platform usage and any separately applicable Apify charges are distinct from the Actor's event fees.

### Limits and interpretation

- No veteran counts, employment totals, private filing forms or legal opinions.
- A match is a public row, not an authenticated employer identity or proof of all obligations.
- A no-match result can reflect a name variation, unsupported year, source delay or incomplete source coverage.
- No fuzzy entity resolution, company deduplication across locations, or enrichment from other sources.
- Exact duplicate rows within a year are collapsed; the same company in different years remains separate.
- Output traverses newest selected sheet first and retains its source order. A capped result is not a full inventory.
- Billing limits, run timeouts and upstream failures can stop a run before `maxItems`.
- The supported archive bound is 200 MiB. Unexpected workbook headers or structure fail rather than return a misleading empty success.

### Reliability and troubleshooting

Transient network failures, HTTP 429 and temporary 5xx responses receive at most three total download attempts with bounded backoff. Permanent 4xx responses are not blindly retried. There is no automatic paid-proxy fallback or stale cached workbook.

**No results?** Try a shorter literal company substring and remove geographic/industry filters. Check the newest available sheet before interpreting absence.

**Unavailable year?** Select a year present in the current source workbook, or leave `years` empty. The Actor will not silently substitute a different explicit year.

**Source changed or download failed?** Check the official DOL links and run log. Failed runs must not be treated as completed negative supplier checks. Any dataset rows from a failed run are partial.

### Integrations

Export the default dataset to a supplier-review spreadsheet or use Apify dataset APIs in an internal procurement pipeline. Join results to your own supplier roster outside the Actor and review ambiguous names manually.

For recurring checks, create an Apify schedule with a saved input and compare `recordId`, `sheetYear` and `sourceSha256` across completed runs. This Actor does not maintain a cross-run history or send change alerts itself. Keep full exports in storage rather than placing an entire workbook in an LLM context.

### API usage

Set your own `APIFY_TOKEN` securely. The examples below start one run and return run metadata; they do not return dataset rows immediately.

```bash
curl -X POST -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  'https://api.apify.com/v2/acts/automation-lab~vets-4212-contractor-records/runs' \
  -d '{"company":"1 Accord","maxItems":5}'
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/vets-4212-contractor-records')
    .call({ company: '1 Accord', maxItems: 5 });
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 5 });
console.log(items);
```

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/vets-4212-contractor-records').call(
    run_input={'company': '1 Accord', 'maxItems': 5})
print(client.dataset(run['defaultDatasetId']).list_items(limit=5).items)
```

### MCP setup

Use a stable Actor-specific subset:

```bash
claude mcp add --transport http apify \
  'https://mcp.apify.com?tools=automation-lab/vets-4212-contractor-records'
```

Claude Desktop, Cursor and VS Code clients supporting remote HTTP MCP can use this equivalent configuration (authentication follows the client's Apify setup):

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/vets-4212-contractor-records"}}}
```

Discover actual tool names and input schemas with scoped `tools/list`. Actor selection also exposes run/data helpers; discovery alone does not authorize execution or aborts.

Example prompt: “Search the newest public VETS-4212 list for 1 Accord and show at most five rows. Explain that list presence is not a compliance conclusion.”

For an authorized invocation, start once and retain the run ID. If nonterminal, follow that same run with bounded 2/4/8/10-second backoff and a 120-second total deadline. Set the client request timeout above the chosen server wait plus transport margin. On client timeout, check the known run rather than starting again. At the deadline, report pending with the run ID.

After success, read the returned default dataset using explicit `limit`, `offset` and fields. An example consumer budget is pages of 20 source rows, at most 100 rows and 64 KiB of serialized UTF-8 result content admitted to the model, whichever comes first. Enforce the byte budget host-side, track source offsets and disclose truncation. Without response interception, do not claim a hard byte guarantee. These consumer limits do not change Actor collection defaults. Tool discovery/cache behavior depends on the client; no universal token-saving claim is made.

### Legality and privacy

Use public filing-list data for lawful supplier research and follow applicable source terms and data-protection obligations. This Actor is independent of DOL. Do not use it as an authoritative legal compliance determination or infer individual veteran status.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days.

### FAQ

**Does a match prove compliance?** No. It proves only that a matching public row was present in this downloaded workbook.

**Can I get veteran employment counts?** No. This Actor excludes employment totals, veteran counts and private submission contents.

**Does the Actor use AI?** No runtime AI or model enrichment is used; filtering and output are deterministic.

**How do I request help or delete data?** Open an issue on the Actor's Apify page. Delete your datasets, key-value stores and run logs through your Apify account according to your retention requirements. The Actor uses ephemeral download files and removes them at completion; it does not keep a separate public-record history. Sanitized private diagnostic reports expire after 30 days. Apify storage and backup retention follows your account/platform policies, not an Actor-set automatic deletion deadline.

### Related Actors

For separate procurement context, see [SAM.gov Government Contracts Scraper](https://apify.com/automation-lab/samgov-government-contracts-scraper) and [SBA DSBS Small Business Search](https://apify.com/automation-lab/sba-dsbs-small-business-search). These workflows do not establish VETS-4212 filing presence.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/vets-4212-contractor-records/changelog.md

# Actor input Schema

## `company` (type: `string`):

Case-insensitive literal substring of Company Name only, after trimming the input. Not hiring-location name or whole-field equality. Combined with all other filters using AND. Empty means any company.

## `years` (type: `array`):

Year strings matching the public workbook sheet names, not inferred reporting periods. Empty or omitted selects the newest available sheet; explicit unavailable years fail. Multiple years use ANY; results traverse newest year first.

## `state` (type: `string`):

Exact two-letter state/territory code, case-insensitive. AND with other filters; empty means any state.

## `zip` (type: `string`):

Five digits match the first five source ZIP characters; ZIP+4 matches the full source value exactly. Leading zeros are preserved when present in source. Empty means any ZIP.

## `naics` (type: `string`):

Literal 2–6 digit prefix of the public source NAICS value. AND with other filters; empty means any NAICS.

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

Global maximum unique matching rows across all selected years (1–10000, default 100). Zero/unlimited is unsupported. Stops scanning after the accepted-row limit; the complete archive and shared-string table must still download and parse. Billing and run limits may stop earlier.

## Actor input object example

```json
{
  "company": "1 Accord",
  "years": [],
  "maxItems": 5
}
```

# Actor output Schema

## `dataset` (type: `string`):

Public employer/location filing-list rows.

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

Selected years, source bytes and scan/return counters.

# 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 = {
    "company": "1 Accord",
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/vets-4212-contractor-records").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 = {
    "company": "1 Accord",
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/vets-4212-contractor-records").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 '{
  "company": "1 Accord",
  "maxItems": 5
}' |
apify call automation-lab/vets-4212-contractor-records --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/vets-4212-contractor-records"
        }
    }
}
```

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/rLwXwUi8abe4XQHYf/builds/zCNZWaxqognbOuCjw/openapi.json
