# Government Bids & RFP Scraper: State Procurement (`morehavoc/state-procurement-bids`) Actor

Government bids, RFPs, RFQs and tenders from 7 official US state procurement portals: Texas, North Carolina, Oregon, Massachusetts, Illinois, Nevada and New Jersey. Public procurement opportunities in one feed: solicitation number, title, agency, due date, link, buyer contact. $0.003 per bid.

- **URL**: https://apify.com/morehavoc/state-procurement-bids.md
- **Developed by:** [Christopher Moravec](https://apify.com/morehavoc) (community)
- **Categories:** Lead generation, Business
- **Security:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 solicitation returneds

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?

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

## State Procurement Bids & RFPs

Open bids, RFPs, RFQs and ITBs read **directly from official US state procurement portals**, normalised into one clean
feed: solicitation number, title, agency, type, posted and due dates (with time zone), the detail link and the buyer's
published contact. No aggregator in the middle, no account, no login: the same public listings any vendor can browse,
read fresh on every run.

**States:** Texas (ESBD), North Carolina (eVP), Oregon (OregonBuys), Massachusetts (COMMBUYS), Illinois (BidBuy),
Nevada (NevadaEPro) and New Jersey (NJSTART). About 2,000 open solicitations across the seven on a typical day.

### Who it's for

- **Government contractors and small businesses** who want every new state bid in their line of work in one
  spreadsheet, every morning, instead of checking seven portals.
- **Bid-matching and lead services** that need a consistent, current feed of state solicitations to match against
  their customers' capabilities.
- **Sales teams selling to the public sector** tracking which agencies are buying what, and who the buyer is.

### What you get per state

| State | Portal | Open on 2026-10-30 | Posted date | Description | Buyer contact |
|---|---|---|---|---|---|
| TX | [Electronic State Business Daily](https://www.txsmartbuy.gov/esbd) | ~520 | yes | with *Include details* | name, email, phone with *Include details* |
| NC | [electronic Vendor Portal](https://evp.nc.gov/solicitations/) | ~280 | yes | yes | name with *Include details* |
| OR | [OregonBuys](https://oregonbuys.gov/bso/view/search/external/advancedSearchBid.xhtml?openBids=true) | ~165 | no | no | buyer name |
| MA | [COMMBUYS](https://www.commbuys.com/bso/view/search/external/advancedSearchBid.xhtml?openBids=true) | ~860 | no | no | buyer name |
| IL | [BidBuy](https://www.bidbuy.illinois.gov/bso/view/search/external/advancedSearchBid.xhtml?openBids=true) | ~170 | no | no | buyer name |
| NV | [NevadaEPro](https://nevadaepro.com/bso/view/search/external/advancedSearchBid.xhtml?openBids=true) | ~25 | no | no | buyer name |
| NJ | [NJSTART](https://www.njstart.gov/bso/view/search/external/advancedSearchBid.xhtml?openBids=true) | ~25 | no | no | buyer name |

Every row has the solicitation number, title, agency, status, due date and a link to the official detail page, where
the solicitation documents are. The portals cover state agencies and, in most states, the universities, school
districts, cities and counties that post on them too.

OR, MA, IL, NV and NJ run on the same platform (Periscope BSO) and their public lists carry no posted date or
description. Their detail pages answer automated reading with a bot check after a few dozen pages, so the actor does not
open them: those fields stay `null` rather than half-filled.

### Input

| Field | Meaning |
|---|---|
| `states` | Any of `TX`, `NC`, `OR`, `MA`, `IL`, `NV`, `NJ`. Empty = all. |
| `keywords` | Optional list. A row is kept when **any** keyword appears in its title or description, case-insensitive, from the start of a word (`roof` matches *roofing*, not *waterproof*). Keywords of 3 letters or fewer must be a whole word, and written in capitals they match as an acronym (`IT` matches *IT services*, not *it* or *with*). OR, MA, IL, NV and NJ are matched on title only. |
| `postedWithinDays` | Optional. Only solicitations posted in the last N days. Only TX and NC publish a posted date in their lists, so the other states return nothing with this set: a row with an unknown posted date is never assumed recent. |
| `openOnly` | Default `true`: still open for responses (open status and due date not passed). Off: TX and NC also return closed, awarded and cancelled solicitations, newest first. The Periscope states list open bids only. |
| `includeDetails` | Default `false`. Opens each TX and NC detail page for the description, buyer name, email and phone (TX) and type and commodity (NC). One extra request per row. |
| `maxItems` | Default 500. Shared fairly across the chosen states. Each row is one charge. |

Example: IT and software opportunities in Texas and North Carolina posted in the last 14 days, with buyer contacts.

```json
{
  "states": ["TX", "NC"],
  "keywords": ["software", "IT services", "cloud", "cybersecurity", "network"],
  "postedWithinDays": 14,
  "openOnly": true,
  "includeDetails": true,
  "maxItems": 200
}
```

### Output

One row per solicitation. **A field the portal did not publish is `null`, never guessed.**

```json
{
  "state": "TX",
  "solicitationId": "701-27-001-R",
  "title": "Audio - Visual Services for TCDD Council Meetings",
  "description": "The Texas Education Agency (TEA), on behalf of the Texas Council for Developmental Disabilities (TCDD), is seeking responses from qualified audio-visual service providers ...",
  "agency": "Texas Education Agency - 701",
  "categories": ["91507-Audio Recording", "91509-Audio/Video Production Services Complete", "91582-Video Production"],
  "type": null,
  "status": "Addendum Posted",
  "postedDate": "2026-10-22",
  "dueDate": "2026-11-05T14:00:00-06:00",
  "url": "https://www.txsmartbuy.gov/esbd/701-27-001-R",
  "attachmentsUrl": null,
  "contactName": "Demetreus Jones",
  "contactEmail": "deedee.jones@tea.texas.gov",
  "contactPhone": "(512) 936-5867",
  "sourceUrl": "https://www.txsmartbuy.gov/esbd"
}
```

| Field | Description |
|---|---|
| `state` | Two-letter state code |
| `solicitationId` | The portal's solicitation / bid number |
| `title` | Solicitation title |
| `description` | Short description (plain text, up to 1,000 characters): NC always; TX with *Include details*; `null` for the Periscope states |
| `agency` | Buying agency or entity as the portal names it |
| `categories` | Commodity codes as published: NIGP code and name (TX), primary commodity (NC, with *Include details*) |
| `type` | RFP, RFQ, IFB, ITB, RFI, RFCSP, RFQual, ...: NC's own type field (with *Include details*), otherwise only when the solicitation number or title carries the token explicitly. `null` when not stated. |
| `status` | The portal's own status word (`Posted`, `Addendum Posted`, `Open`, `Sent`, ...) |
| `postedDate` | `YYYY-MM-DD` (TX, NC) |
| `dueDate` | Response due / bid opening date, ISO 8601 with the state's UTC offset on that day (`2026-11-19T11:00:00-05:00`). Date only when the portal gave no time. |
| `url` | Official detail page for the solicitation (documents, addenda, how to respond) |
| `attachmentsUrl` | TX only: the external documents link the agency posted, when it gave one |
| `contactName`, `contactEmail`, `contactPhone` | The buyer as officially published: TX name/email/phone and NC name with *Include details*; buyer name on the Periscope states |
| `sourceUrl` | The public list the row came from |

**Public business data only.** Government buyers' names, emails and phones appear only as the portals publish them as
official contacts for the solicitation. No data about private individuals is collected. Titles are kept exactly as
published, including the occasional garbled character a portal itself serves.

The run's key-value store has an `OUTPUT` record with a per-state summary (ok / partial / failed, rows listed and
returned, detail pages read, error), and the log prints the same. **One state failing never stops the others.**

### Pricing

Pay per result: **$0.003 per solicitation** returned ($3 per 1,000). No monthly fee. Set *Max solicitations* or a
maximum charge on the run to cap the cost; a daily run of all seven states with the default 500 rows costs $1.50.

### Tips

- **Daily new-bid alerts:** schedule a run with `postedWithinDays: 1` on TX and NC (with your keywords), and a run of the
  other states with keywords, then compare `solicitationId`s with yesterday's dataset.
- **Speed:** a full run of all states takes about 2 minutes. *Include details* adds 1-2 seconds per TX/NC row.
- **Massachusetts** has the largest list (850+ open bids); use keywords to narrow it.

### How it reads the portals

- **Texas:** the public JSON service the ESBD page itself calls, 24 rows a page, plus its detail service.
- **North Carolina:** the eVP page's own grid service, with the anonymous request token and view settings the page gives
  every visitor, fetched fresh each run.
- **OR, MA, IL, NV, NJ:** the public "open bids" list, paged through the page's own paging call.

Politely: one request at a time per portal with a pause between requests, a user agent that says what it is
(`state-procurement-bids/0.1; public records`), no login, no CAPTCHA solving. A portal that answers with a bot check is
stopped for the rest of the run and reported, not retried.

### Limits

- A portal changing its page layout or service will make that state fail (reported in the summary) until the actor is
  updated; the others keep running.
- The Periscope states (OR, MA, IL, NV, NJ) have no posted date, description, commodity codes or buyer email/phone.
- `openOnly: false` has no effect on the Periscope states, whose public lists show open bids only.

# Actor input Schema

## `states` (type: `array`):

Portals to read. Empty = all. TX and NC carry posted dates, and with Include details also description and buyer contact. OR, MA, IL, NV and NJ (Periscope portals) return list fields only: solicitation number, title, agency, buyer name, due date, status and link.

## `keywords` (type: `array`):

Optional. A row is kept when ANY keyword appears in its title or description, case-insensitive, from the start of a word ("roof" matches "roofing", not "waterproof"). Keywords of 3 letters or fewer must match a whole word, and in capitals they match as an acronym ("IT" matches "IT services", not "it" or "with"). OR, MA, IL, NV and NJ have no description, so only their titles are searched.

## `postedWithinDays` (type: `integer`):

Optional. Only solicitations posted in the last N days. 0 or empty = any posting date. Only TX and NC publish a posted date in their lists, so with this set the other states return nothing (a row with an unknown posted date is never assumed recent).

## `openOnly` (type: `boolean`):

Keep only solicitations still open for responses (the portal's open status and a due date not yet passed). Off: TX and NC also return closed, awarded and cancelled ones, newest first (use Posted within or Max items to bound it). The Periscope states publish only open bids either way.

## `includeDetails` (type: `boolean`):

Opens each TX and NC solicitation's public detail page for the description, buyer name, email and phone (TX), and type and commodity (NC). One extra request per row, about 1-2 seconds each. No effect on OR, MA, IL, NV, NJ.

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

Stop after this many rows in total (each row is one charge). Shared fairly across the chosen states, so no state crowds out the others.

## Actor input object example

```json
{
  "states": [
    "TX",
    "NC"
  ],
  "keywords": [],
  "openOnly": true,
  "includeDetails": false,
  "maxItems": 100
}
```

# Actor output Schema

## `solicitations` (type: `string`):

One row per open bid, RFP, RFQ or ITB: state, solicitation number, title, agency, type, dates, links and buyer contact.

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

For each state: ok, partial or failed, rows listed and returned, detail pages read, and the error if any.

# 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 = {
    "states": [
        "TX",
        "NC"
    ],
    "keywords": [],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("morehavoc/state-procurement-bids").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 = {
    "states": [
        "TX",
        "NC",
    ],
    "keywords": [],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("morehavoc/state-procurement-bids").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 '{
  "states": [
    "TX",
    "NC"
  ],
  "keywords": [],
  "maxItems": 100
}' |
apify call morehavoc/state-procurement-bids --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,morehavoc/state-procurement-bids"
        }
    }
}
```

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/kgHHauDsPBg6549Nb/builds/2TTYJeJ9jVAApn2CT/openapi.json
