# MyFloridaMarketPlace Bids Scraper & Monitor (`stefano_seggio/florida-tenders-monitor`) Actor

Scrape and monitor Florida state-agency solicitations on MyFloridaMarketPlace (MFMP VBS): ITB, RFP, ITN, RFI, RSQ, single source and intent-to-award notices with UNSPSC codes, contacts, documents. Server-side filters, delta mode for new, amended and closed ads, normalised dates. Pay per result.

- **URL**: https://apify.com/stefano\_seggio/florida-tenders-monitor.md
- **Developed by:** [Stefano Seggio](https://apify.com/stefano_seggio) (community)
- **Categories:** Business, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 record (full detail)s

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

## MyFloridaMarketPlace Bids Scraper & Monitor — Apify Store Overview

**Actor ID:** `afSZyXLVcgnLpucyo` · **Store URL:** https://apify.com/stefano\_seggio/florida-tenders-monitor · **Version:** 2.0

***

### Executive Summary & Business Use Case

The MyFloridaMarketPlace Bids Scraper & Monitor extracts every advertisement posted on the **Vendor Information Portal of MyFloridaMarketPlace (MFMP VBS, vendor.myfloridamarketplace.com)** — the statutory posting site, operated by Florida's Department of Management Services, where every State of Florida agency is required by section 287.042(3)(b) of the Florida Statutes and Rule 60A-1 F.A.C. to advertise its competitive solicitations and intended awards. The Actor talks directly to the same unauthenticated JSON endpoints (`/mfmp/pub/search/bids`, `/count`, `/detail`) that the portal's own Angular front end calls, so it needs no login, no CAPTCHA and no proxy. It covers all ten advertisement types the portal recognises — Invitation to Bid (ITB), Request for Proposals (RFP), Invitation to Negotiate (ITN), Request for Information (RFI), Request for Statement of Qualifications (RSQ), Single Source notices, Agency Decisions (intent to award), Grant Opportunities, Public Meeting Notices and Informational Notices — across all four statuses the portal tracks (OPEN, CLOSED, WITHDRAWN, PREVIEW).

What makes this more than a one-shot scrape is the delta engine: MFMP keeps an advertisement's original `publishDate` unchanged even when an agency posts an addendum, extends a deadline, or answers a Q\&A, so any monitor keyed on publish date is blind to that activity. This Actor instead keys change detection on the portal's own amendment `version` counter and on `status`, so a schedule run with `onlyNew` on reliably surfaces new postings (`NEW_LISTING`), edits to known postings (`UPDATED`), and status flips such as OPEN → CLOSED (`STATUS_CHANGE`) — including the addenda that publish-date-based alerts miss entirely.

Three concrete business use cases follow directly from the fields the Actor actually returns. First, **Florida government contractors and SMEs** (IT, construction, health and social services) use `type`, `commodityCodeIds`/`commodityCodesText`, `closeDateLocal`, `daysUntilClose` and `contactEmail` to build a bid/no-bid pipeline and a proposal calendar without checking the portal by hand. Second, **capture teams and incumbents working an active solicitation** watch for `event_type: UPDATED` together with `version`/`previousVersion`, `lastUpdateDateUtc` and a fresh entry in `documents[]` (an addendum or Q\&A) so they never miss a close-date extension. Third, **bid-protest counsel and competitive-intelligence teams** filter `types: ["1"]` (Agency Decision) with `onlyNew` on a tight schedule to catch intent-to-award notices the moment `isAwardNotice: true` appears, using `linkedAdNumber`/`linkedAdUrl` to trace the notice back to the original solicitation inside the 72-hour protest window under s. 120.57(3) F.S. Because MFMP has no structured contract-value field, the data does not support a "largest active contract value" style use case out of the box — `amountsUsd`/`maxAmountUsd` are figures mined from free-text descriptions and are documented as sparse (mostly present on single-source and award notices, rarely on solicitations).

### Technical Features & V2 Architecture Highlights

**Named, per-filter-set delta memory.** When `onlyNew` is enabled, the Actor writes a private, named key-value store called `florida-tenders-monitor-state-<deltaStateName>` that remembers, for every advertisement it has delivered, its `advertisementId`, `version` and `status`. `deltaStateName` defaults to a fingerprint of the run's own filters, so unrelated schedules never collide; setting the same name on two tasks makes them share one memory on purpose. `resetState: true` forgets everything for that name and re-baselines. The store is documented to hold up to 50,000 advertisements — larger than the entire CLOSED register (13,027 records as of the README's 8 September 2026 count).

**Why `version`, not `publishDate`.** The delta engine's change key is the portal's own amendment `version` counter, specifically because MFMP does not move `publishDate` when an agency edits a live advertisement. The CHANGELOG's own worked example: grant opportunity `GO-14963` published 23 January 2026, edited seven times with six addenda through September, publish date never moved — invisible to a date-keyed monitor, but a same-day `UPDATED` event on a version-keyed one.

**Real event-type set — three values, not the generic fleet default.** `event_type` on this actor is `NEW_LISTING`, `UPDATED`, or `STATUS_CHANGE` (per `dataset_schema.json`'s own enum and the `eventTypes` input's enum). There is no separate `CLOSED` event type here: a solicitation closing is represented as `STATUS_CHANGE` with `status` moving from `OPEN` to `CLOSED` (visible only if `CLOSED` is also included in the `statuses` filter, since an advertisement that leaves the walked statuses simply disappears from the walk).

**`onlyNew` — exact behavior per this actor's own input schema.** Per its own description text: *"Remembers every advertisement it has delivered (id, version, status - per filter set, in a named key-value store) and returns only what is new, amended or status-changed since. The portal has no timestamp sort, so every listing page is re-read on each run (cheap: OPEN is 2 pages, fetched in parallel); detail is fetched only for records that will be delivered. The first run is the baseline: with a small 'Max records' it delivers the most recently published advertisements and treats everything older as history."* In practice: a quiet daily run against OPEN costs three listing requests plus the start fee, because detail is never fetched for a row that will not be delivered.

**Crash-safe delta writes.** The CHANGELOG documents a v1 bug that this v2 fixes directly: the seen-set used to be persisted *before* records were pushed to the dataset, so a spending-limit stop, timeout, or platform migration mid-run could mark an undelivered advertisement as "seen" forever. In v2, state is written only for records actually stored in the dataset, delivery happens oldest-first within a run (so a gap always sits at the point the next walk resumes from), and state is flushed periodically (every 50 records) plus on `migrating`/`aborting` events and in a `finally` block.

**Standard integration envelope, not a documented "18-field" baseline.** Every record carries six envelope fields — `record_id`, `event_type`, `scraped_at`, `is_new`, `source_url`, `data_source` — described in the README as identical across this developer's public-register Actors, so one webhook parser serves all of them. Beyond the envelope, `dataset_schema.json` documents roughly 60 advertisement-specific fields (identity, agency, three date families, amendment tracking, detail-only fields, commodity codes, documents, and contact), and the README states the total as "70+ fields." Neither the schema file nor the README states an "18 base field" figure for this actor, so that number is not used here.

**Concurrency and server-side filtering.** `maxConcurrency` (default 5, max 10) governs in-flight listing-page and detail requests; the README and input schema both note the listing endpoint starts returning HTTP 429 above roughly 8 concurrent requests, so the Actor backs off and retries with jittered, `Retry-After`-aware backoff rather than failing. Nearly every filter (`statuses`, `types`, `agencyIds`, `commodityCodes`, `adNumber`, `agencyAdNumberContains`, `openBefore`, `closesAfter`, a one-day `dateFrom`/`dateTo`) is pushed to MFMP's own search endpoint server-side; only `agencyNameContains` and wider date windows are applied client-side after the listing has already been walked (which the README notes is "cheap - the listing is walked in full anyway").

### Input Schema & JSON Configuration Example

All fields below are copied verbatim from `.actor/input_schema.json`.

| Field | Type | Default | Description |
|---|---|---|---|
| `statuses` | array (string enum: `OPEN`, `CLOSED`, `WITHDRAWN`, `PREVIEW`) | `["OPEN"]` | Which advertisement statuses to walk. OPEN is small and cheap (2 pages). Add CLOSED to track awards and to receive STATUS\_CHANGE events when an open solicitation closes (131 pages, fetched in parallel; only new or changed records cost anything). |
| `types` | array (string enum `"1"`–`"10"`) | `[]` | The portal's 10 advertisement types. Select several to get their union. Agency Decision = intent-to-award / award notice (starts the 72-hour protest clock); Single Source = non-competitive purchase notice. |
| `agencyIds` | array of string (pattern `^\d{4,12}$`) | `[]` | MFMP organization ids, e.g. 30000021 (FDOT), 30000023 (DCF), 30000029 (AHCA), 30000032 (DMS), 30000026 (DOH), 30000031 (FDLE), 30000018 (DOE), 30000011 (DEP), 30000015 (FDACS), 30000012 (DOC), 30000036 (FWC), 30000039 (DJJ), 30000034 (REV), 30000019 (FSDB), 30000030 (FDC), 30000020 (FDVA), 30000025 (DMA), 30000013 (DLA), 30000033 (DOAH). Every record carries its `organizationId`, so run once without this filter to discover the rest (the portal has no public agency list). |
| `agencyNameContains` | string | — | Case-insensitive substring on the agency name or short name, e.g. 'Transportation' or 'AHCA'. Applied client-side after the listing is read (cheap - the listing is walked in full anyway). |
| `titleContains` | string | — | Case-insensitive substring on the advertisement title, e.g. 'software' or 'construction'. |
| `commodityCodes` | array of string (pattern `^\d{8}$`) | `[]` | Exact 8-digit UNSPSC codes, e.g. 83112200 (Enhanced telecommunications services), 43230000 (Software). Several codes = union. The portal matches exact codes only - a prefix such as '43' returns nothing. |
| `adNumber` | string (pattern `^\s*([A-Za-z]{2,3}-?)?\d+\s*$`) | — | One specific advertisement, e.g. 16672 or AD-16672 (the numeric part is used). |
| `agencyAdNumberContains` | string | — | Substring on the agency's own reference, e.g. 'DOT-ITB-27' or 'RFP 2526'. |
| `dateFrom` | string (absolute or relative date) | — | Only advertisements first published on or after this UTC calendar day: absolute (2026-09-01) or relative ('7 days', '3 months' back from today). A one-day window (from = to) is pushed to the portal; wider windows are applied client-side on the listing. Note: amendments keep the original publish date - use delta mode to catch them. |
| `dateTo` | string (absolute or relative date) | — | Upper bound of the publish window (inclusive). Leave empty for 'today'. |
| `openBefore` | string (absolute or relative date) | — | Server-side: only advertisements whose open date is on or before this day (absolute or relative). Useful to skip solicitations that have not started accepting responses yet. |
| `closesAfter` | string (absolute or relative date) | — | Server-side: only advertisements whose close date is on or after this day (absolute, or relative such as '0 days' for 'still open today'). Great for 'give me only what I can still bid on'. |
| `eventTypes` | array (string enum: `NEW_LISTING`, `UPDATED`, `STATUS_CHANGE`) | `["NEW_LISTING", "UPDATED", "STATUS_CHANGE"]` | NEW\_LISTING = an advertisement never delivered before; UPDATED = a known advertisement whose version counter rose (addendum, Q\&A, close-date extension, new document); STATUS\_CHANGE = a known advertisement whose status changed (e.g. OPEN -> CLOSED; only visible if that status is in 'Statuses'). |
| `onlyNew` | boolean | `false` (prefill `true`) | Remembers every advertisement it has delivered (id, version, status - per filter set, in a named key-value store) and returns only what is new, amended or status-changed since. The portal has no timestamp sort, so every listing page is re-read on each run (cheap: OPEN is 2 pages, fetched in parallel); detail is fetched only for records that will be delivered. The first run is the baseline: with a small 'Max records' it delivers the most recently published advertisements and treats everything older as history. |
| `deltaStateName` | string (pattern `^[A-Za-z0-9-]{1,30}$`) | — | Optional label for the memory of a monitoring task. Defaults to a fingerprint of your filters, so different schedules never interfere. Set the same name on two tasks to make them share one memory. |
| `resetState` | boolean | `false` | Forget every previously delivered advertisement for this delta state and re-baseline (the run then returns up to 'Max records' of the most recently published advertisements). |
| `maxItems` | integer (1–50000) | `100` | Hard cap on delivered records (and therefore on cost: you pay per record). Newest-published first; amendments and status changes to known records always come first. In delta mode anything beyond the cap stays unseen and is delivered by the next run. |
| `fetchDetail` | boolean | `true` | One extra request per delivered record for the description (HTML + plain text), UNSPSC commodity codes, documents with download links, response contact, response due date, last-update timestamp, linked solicitation and indicator flags. Off = listing-only summary records (cheaper 'result-summary' price). |
| `maxConcurrency` | integer (1–10) | `5` | How many listing pages / detail requests are in flight at once. Detail pages tolerate 10; the listing endpoint starts returning HTTP 429 above about 8 in flight (the Actor backs off and retries), so keep 5 for large backfills. |
| `dateRange` | string (pattern `^(24h\|7d\|30d)$`, hidden editor) | — | Kept for tasks created with version 1: '24h', '7d' or '30d' are interpreted as 'Published from'. |

#### Example configuration — monitor RFPs and ITNs, delta mode on

This configuration is valid against the schema above: `types` uses two of the ten allowed enum values, `statuses` and `eventTypes` use only enum members, `agencyIds` matches the required digit pattern, and `maxItems` is within `[1, 50000]`.

```json
{
  "statuses": ["OPEN", "CLOSED"],
  "types": ["5", "6"],
  "agencyIds": ["30000021"],
  "titleContains": "software",
  "eventTypes": ["NEW_LISTING", "UPDATED", "STATUS_CHANGE"],
  "onlyNew": true,
  "deltaStateName": "fdot-software-rfps",
  "maxItems": 200,
  "fetchDetail": true,
  "maxConcurrency": 5
}
```

### Output Dataset Sample & Data Dictionary

All field names and descriptions below are copied from `.actor/dataset_schema.json`.

**Integration envelope**

| Field | Type | Description |
|---|---|---|
| `record_id` | string | `advertisementId` as a string - stable across runs (e.g. "16672"). |
| `event_type` | string (enum: `NEW_LISTING`, `UPDATED`, `STATUS_CHANGE`) | NEW\_LISTING, UPDATED (version counter rose: addendum, extension, new document) or STATUS\_CHANGE (e.g. OPEN -> CLOSED). |
| `scraped_at` | string | ISO-8601 UTC timestamp of this extraction. |
| `is_new` | boolean | true when this advertisement was never delivered by a previous run of this delta memory. |
| `source_url` | string | The advertisement's page on the portal. |
| `data_source` | string | Attribution string. |

**Advertisement fields**

| Field | Type | Description |
|---|---|---|
| `advertisementId` | integer | Example: 16672. |
| `uniqueName` | string | Display ID, e.g. AD-16672, GO-14963. |
| `agencyAdNumber` | string | null | The agency's own reference. |
| `title` | string | Advertisement title. |
| `type` | string | Agency Decision, Grant Opportunities, Informational Notice, Invitation to Bid, Invitation to Negotiate, Request for Proposals, Public Meeting Notice, Request for Information, Request for Statement of Qualifications, Single Source. |
| `typeId` | string | The `types` filter value ("1"–"10"). |
| `status` | string (enum: `OPEN`, `CLOSED`, `WITHDRAWN`, `PREVIEW`) | Current advertisement status. |
| `agency` | string | Agency name. |
| `organizationId` | integer | null | Stable agency id - the `agencyIds` filter value. |
| `organizationShortName` | string | null | Example: FDOT. |
| `organizationEntity` | string | null | Example: 550000. |
| `openDate` | string | Open date (raw), e.g. "2026-09-03T20:30:00.000+00:00". |
| `closeDate` | string | Close date (raw). |
| `publishDate` | string | Publish date (raw). |
| `publishDateUtc` | string | null | Canonical ISO-8601 Z form. |
| `openDateUtc` | string | null | Opens (UTC). |
| `closeDateUtc` | string | null | Closes (UTC). |
| `publishDateLocal` | string | null | Published (Florida time), e.g. "2026-08-13 13:39 EDT". |
| `openDateLocal` | string | null | Opens (Florida time). |
| `closeDateLocal` | string | null | What the vendor reads on the portal: Eastern time with EST/EDT. |
| `publishDay` | string | null | YYYY-MM-DD (UTC) - the granularity of the portal's own date filter. |
| `closeDay` | string | null | Close day. |
| `responseWindowDays` | integer | null | Whole days between open and close. |
| `daysUntilClose` | integer | null | Negative once closed. |
| `isOpenForResponses` | boolean | status OPEN and close date still in the future at scrape time. |
| `version` | integer | The portal's amendment counter - rises on every agency edit. The delta engine's change key. |
| `isAmended` | boolean | version > 1. |
| `previousVersion` | integer | null | Version at the previous delivery (UPDATED / STATUS\_CHANGE events). |
| `previousStatus` | string | null | Previous status. |
| `isAwardNotice` | boolean | type = Agency Decision (intent to award / agency decision - starts the 72-hour protest window). |
| `isSingleSource` | boolean | Single source flag. |
| `detailFetched` | boolean | false when fetchDetail was off or the detail could not be retrieved (see detailError). |
| `detailError` | string | null | NOT\_FOUND when the portal has no detail for the id; otherwise the failure message. |
| `description` | string | null | Raw HTML from the portal. |
| `descriptionText` | string | null | Plain-text rendering with line breaks - ready for search, summarisation or e-mail. |
| `amountsUsd` | array of number | Every $ figure found in the description (the portal has no structured value field). |
| `maxAmountUsd` | number | null | Largest amount (USD). |
| `currency` | string | Example: USD. |
| `lastUpdateDate` | string | null | Last updated (raw). |
| `lastUpdateDateUtc` | string | null | When the agency last edited the advertisement - amendments do NOT change publishDate. |
| `responseDate` | string | null | Response due (raw). |
| `responseDateUtc` | string | null | Proposal / response due date - can differ from closeDate (e.g. a grant RFA closing months after the application deadline). |
| `responseDateLocal` | string | null | Response due (Florida time). |
| `linkedAdNumber` | string | null | For an Agency Decision: the advertisementId of the original solicitation being awarded. |
| `linkedAdUrl` | string | null | Linked advertisement URL. |
| `publishOption` | string | null | Example: "Start Immediately". |
| `withdrawn` | boolean | null | The portal's own flag (observed false even on WITHDRAWN records - prefer `status`). |
| `timeRemainingMs` | integer | null | Milliseconds until closeDate as computed by the portal at fetch time (negative once closed). |
| `indicators` | object | null | minorityEncouraged, preSolicitationConference, disabilitiesAct, rightToReject, agencyContactPeriod. |
| `minorityEncouraged` | boolean | null | Minority participation encouraged. |
| `preSolicitationConference` | boolean | null | Pre-solicitation conference. |
| `disabilitiesAct` | boolean | null | ADA notice. |
| `rightToReject` | boolean | null | Right to reject. |
| `agencyContactPeriod` | boolean | null | Agency contact period. |
| `commodityCodes` | array of object (`{id, value}`) | UNSPSC codes with labels. |
| `commodityCodeIds` | array of string | Commodity code IDs. |
| `commodityCodesText` | string | null | Example: "83112200 Enhanced telecommunications services; 83112403 Point to point digital telecommunications circuit". |
| `documents` | array of object (`fileName`, `downloadUrl`, `attachmentId`, `description`, `date`, `dateUtc`, `version`, `docFor`) | Attachments with direct download links (the endpoint needs an Accept: application/json header) and posting dates - addenda appear here with their title. |
| `documentCount` | integer | null | Document count. |
| `latestDocumentDateUtc` | string | null | When the most recent attachment (typically the latest addendum) was posted. |
| `responseContact` | object | null | Raw: responseContact, email, ph, address1, address2, city, state, zip. |
| `contactName` | string | null | Contact name. |
| `contactEmail` | string | null | Contact e-mail. |
| `contactPhone` | string | null | Contact phone. |
| `contactAddress` | string | null | Contact address. |
| `contactCity` | string | null | Contact city. |
| `contactState` | string | null | Contact state. |
| `contactZip` | string | null | Contact ZIP. |

#### Sample dataset record

Field names below are all real (`dataset_schema.json`); the description text is trimmed for length, matching the README's own convention.

```json
{
  "record_id": "16861",
  "event_type": "NEW_LISTING",
  "scraped_at": "2026-09-08T08:20:11.402Z",
  "is_new": true,
  "source_url": "https://vendor.myfloridamarketplace.com/search/bids/detail/16861",
  "data_source": "MyFloridaMarketPlace Vendor Information Portal (vendor.myfloridamarketplace.com), Florida Department of Management Services - public records under Florida Statutes ch. 119",
  "advertisementId": 16861,
  "uniqueName": "RFP-16861",
  "agencyAdNumber": "DOT-RFP-27-9018-SJ",
  "title": "Commercial Driver's License (CDL) Training and Testing Services",
  "type": "Request for Proposals",
  "typeId": "6",
  "status": "OPEN",
  "agency": "Florida Department of Transportation (FDOT)",
  "organizationId": 30000021,
  "organizationShortName": "FDOT",
  "organizationEntity": "550000",
  "openDate": "2026-09-03T20:06:37.000+00:00",
  "closeDate": "2026-09-21T14:00:00.000+00:00",
  "publishDate": "2026-09-03T20:06:37.000+00:00",
  "publishDateUtc": "2026-09-03T20:06:37.000Z",
  "closeDateUtc": "2026-09-21T14:00:00.000Z",
  "publishDateLocal": "2026-09-03 16:06 EDT",
  "closeDateLocal": "2026-09-21 10:00 EDT",
  "publishDay": "2026-09-03",
  "closeDay": "2026-09-21",
  "responseWindowDays": 17,
  "daysUntilClose": 13,
  "isOpenForResponses": true,
  "version": 1,
  "isAmended": false,
  "previousVersion": null,
  "previousStatus": null,
  "isAwardNotice": false,
  "isSingleSource": false,
  "detailFetched": true,
  "detailError": null,
  "descriptionText": "The Florida Department of Transportation requests competitive sealed bids/proposals/replies for the procurement of: Commercial Driver's License (CDL) Training and Testing Services. All Bidders, Proposers, and Respondents must be registered in the State of Florida's MyFloridaMarketplace system...",
  "amountsUsd": [],
  "maxAmountUsd": null,
  "currency": "USD",
  "lastUpdateDate": "2026-09-03T20:06:37.000+00:00",
  "lastUpdateDateUtc": "2026-09-03T20:06:37.000Z",
  "responseDate": "2026-09-21T14:00:00.000+00:00",
  "responseDateLocal": "2026-09-21 10:00 EDT",
  "linkedAdNumber": null,
  "publishOption": "Start Immediately",
  "withdrawn": false,
  "commodityCodes": [
    { "id": "86101715", "value": "Road or rail transportation vocational training services" },
    { "id": "86131701", "value": "Vehicle driving schools services" }
  ],
  "commodityCodeIds": ["86101715", "86131701"],
  "documents": [
    {
      "fileName": "9018 - Solicitation Document.pdf",
      "downloadUrl": "https://vendor.myfloridamarketplace.com/mfmp/bids/detail/attachment/download?attachmentId=40095",
      "attachmentId": 40095,
      "description": "SOLICITATION DOCUMENT",
      "date": "2026-09-03T20:06:24.000+00:00",
      "dateUtc": "2026-09-03T20:06:24.000Z",
      "version": null,
      "docFor": "advertisementDocuments"
    }
  ],
  "documentCount": 1,
  "latestDocumentDateUtc": "2026-09-03T20:06:24.000Z",
  "contactName": "SHERILL JOHNSON",
  "contactEmail": "CO.Purch@dot.state.fl.us",
  "contactPhone": "(000) 000-0000",
  "contactAddress": "FDOT PROCUREMENT OFFICE, 605 SUWANNEE STREET",
  "contactCity": "TALLAHASSEE",
  "contactState": "FL",
  "contactZip": "32399-0450",
  "minorityEncouraged": false,
  "preSolicitationConference": false
}
```

On a later run, the same advertisement amended by the agency arrives with `"event_type": "UPDATED"`, `"is_new": false`, an incremented `"version"` (e.g. `2`), `"previousVersion": 1`, a fresh `lastUpdateDateUtc`, and a new addendum entry inside `documents[]`.

### Multi-language Integration Snippets

All three snippets call the same endpoint and use only real input field names from this actor's `input_schema.json`.

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/stefano_seggio~florida-tenders-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "types": ["5", "6"],
    "agencyIds": ["30000021"],
    "onlyNew": true,
    "maxItems": 200,
    "fetchDetail": true
  }'
```

#### Python (apify-client)

```python
from apify_client import ApifyClient

client = ApifyClient(token="YOUR_APIFY_TOKEN")

run_input = {
    "types": ["5", "6"],
    "agencyIds": ["30000021"],
    "onlyNew": True,
    "maxItems": 200,
    "fetchDetail": True,
}

run = client.actor("stefano_seggio/florida-tenders-monitor").call(run_input=run_input)

for ad in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(ad["event_type"], ad["uniqueName"], ad["agency"], ad["title"], ad["closeDateLocal"], ad["contactEmail"])
```

#### Node.js (apify-client)

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('stefano_seggio/florida-tenders-monitor').call({
    types: ['5', '6'],
    agencyIds: ['30000021'],
    onlyNew: true,
    maxItems: 200,
    fetchDetail: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems({ desc: true });
for (const ad of items) {
    console.log(ad.event_type, ad.uniqueName, ad.agency, ad.title, ad.closeDateLocal, ad.contactEmail);
}
```

### Pricing Model Explanation

Verified-live pricing for this actor, pay-per-event with platform usage included:

| Event | Price | When it fires on this actor |
|---|---|---|
| `result` | $0.003 per record | A delivered advertisement with full detail attached — one extra request per record fetched the description (HTML + plain text), UNSPSC commodity codes, `documents[]` with download links, response contact, response due date, last-update timestamp, linked solicitation and indicator flags. This is the tier used whenever `fetchDetail: true` (the default) successfully retrieves detail for that record. |
| `result-summary` | $0.001 per record | A delivered advertisement as a listing-only summary — either because `fetchDetail` was set to `false`, so only the fields already present on the search-results row are returned (no description, no documents, no contact), or because a detail request failed for a transient reason after retries: the record is still delivered, `detailFetched` is `false` and `detailError` carries the failure reason, but it is billed at the cheaper summary rate rather than being dropped or double-charged. |
| `apify-actor-start` | $0.00005 | Once per run, regardless of how many (or how few) records it delivers. |

The tier this actor bills at is therefore purely a function of whether full detail was actually attached to that specific record — not a "new vs. previously seen" distinction. In delta mode (`onlyNew: true`), the two tiers apply on top of the delta filter, not instead of it: enabling `onlyNew` means an advertisement that is already known and unchanged (same `version`, same `status` as last delivered) is never delivered at all — it produces no `result` or `result-summary` event and is not billed at $0, it simply never becomes a chargeable event in the first place. Only advertisements that are new, whose `version` rose, or whose `status` changed reach the dataset (and therefore reach a billing event), which is what keeps a quiet daily monitoring run down to the $0.00005 start fee plus, at most, a handful of `result`/`result-summary` charges for whatever actually moved on the portal that day.

# Actor input Schema

## `statuses` (type: `array`):

Which advertisement statuses to walk. OPEN is small and cheap (2 pages). Add CLOSED to track awards and to receive STATUS\_CHANGE events when an open solicitation closes (131 pages, fetched in parallel; only new or changed records cost anything).

## `types` (type: `array`):

The portal's 10 advertisement types. Select several to get their union. Agency Decision = intent-to-award / award notice (starts the 72-hour protest clock); Single Source = non-competitive purchase notice.

## `agencyIds` (type: `array`):

MFMP organization ids, e.g. 30000021 (FDOT), 30000023 (DCF), 30000029 (AHCA), 30000032 (DMS), 30000026 (DOH), 30000031 (FDLE), 30000018 (DOE), 30000011 (DEP), 30000015 (FDACS), 30000012 (DOC), 30000036 (FWC), 30000039 (DJJ), 30000034 (REV), 30000019 (FSDB), 30000030 (FDC), 30000020 (FDVA), 30000025 (DMA), 30000013 (DLA), 30000033 (DOAH). Every record carries its `organizationId`, so run once without this filter to discover the rest (the portal has no public agency list).

## `agencyNameContains` (type: `string`):

Case-insensitive substring on the agency name or short name, e.g. 'Transportation' or 'AHCA'. Applied client-side after the listing is read (cheap - the listing is walked in full anyway).

## `titleContains` (type: `string`):

Case-insensitive substring on the advertisement title, e.g. 'software' or 'construction'.

## `commodityCodes` (type: `array`):

Exact 8-digit UNSPSC codes, e.g. 83112200 (Enhanced telecommunications services), 43230000 (Software). Several codes = union. The portal matches exact codes only - a prefix such as '43' returns nothing.

## `adNumber` (type: `string`):

One specific advertisement, e.g. 16672 or AD-16672 (the numeric part is used).

## `agencyAdNumberContains` (type: `string`):

Substring on the agency's own reference, e.g. 'DOT-ITB-27' or 'RFP 2526'.

## `dateFrom` (type: `string`):

Only advertisements first published on or after this UTC calendar day: absolute (2026-09-01) or relative ('7 days', '3 months' back from today). A one-day window (from = to) is pushed to the portal; wider windows are applied client-side on the listing. Note: amendments keep the original publish date - use delta mode to catch them.

## `dateTo` (type: `string`):

Upper bound of the publish window (inclusive). Leave empty for 'today'.

## `openBefore` (type: `string`):

Server-side: only advertisements whose open date is on or before this day (absolute or relative). Useful to skip solicitations that have not started accepting responses yet.

## `closesAfter` (type: `string`):

Server-side: only advertisements whose close date is on or after this day (absolute, or relative such as '0 days' for 'still open today'). Great for 'give me only what I can still bid on'.

## `eventTypes` (type: `array`):

NEW\_LISTING = an advertisement never delivered before; UPDATED = a known advertisement whose version counter rose (addendum, Q\&A, close-date extension, new document); STATUS\_CHANGE = a known advertisement whose status changed (e.g. OPEN -> CLOSED; only visible if that status is in 'Statuses').

## `onlyNew` (type: `boolean`):

Remembers every advertisement it has delivered (id, version, status - per filter set, in a named key-value store) and returns only what is new, amended or status-changed since. The portal has no timestamp sort, so every listing page is re-read on each run (cheap: OPEN is 2 pages, fetched in parallel); detail is fetched only for records that will be delivered. The first run is the baseline: with a small 'Max records' it delivers the most recently published advertisements and treats everything older as history.

## `deltaStateName` (type: `string`):

Optional label for the memory of a monitoring task. Defaults to a fingerprint of your filters, so different schedules never interfere. Set the same name on two tasks to make them share one memory.

## `resetState` (type: `boolean`):

Forget every previously delivered advertisement for this delta state and re-baseline (the run then returns up to 'Max records' of the most recently published advertisements).

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

Hard cap on delivered records (and therefore on cost: you pay per record). Newest-published first; amendments and status changes to known records always come first. In delta mode anything beyond the cap stays unseen and is delivered by the next run.

## `fetchDetail` (type: `boolean`):

One extra request per delivered record for the description (HTML + plain text), UNSPSC commodity codes, documents with download links, response contact, response due date, last-update timestamp, linked solicitation and indicator flags. Off = listing-only summary records (cheaper 'result-summary' price).

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

How many listing pages / detail requests are in flight at once. Detail pages tolerate 10; the listing endpoint starts returning HTTP 429 above about 8 in flight (the Actor backs off and retries), so keep 5 for large backfills.

## `dateRange` (type: `string`):

Kept for tasks created with version 1: '24h', '7d' or '30d' are interpreted as 'Published from'.

## Actor input object example

```json
{
  "statuses": [
    "OPEN"
  ],
  "types": [],
  "agencyIds": [],
  "commodityCodes": [],
  "eventTypes": [
    "NEW_LISTING",
    "UPDATED",
    "STATUS_CHANGE"
  ],
  "onlyNew": true,
  "resetState": false,
  "maxItems": 100,
  "fetchDetail": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `resultsNewestFirst` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `excel` (type: `string`):

No description

## `overview` (type: `string`):

No description

## `bidPipeline` (type: `string`):

No description

## `amendments` (type: `string`):

No description

## `awards` (type: `string`):

No description

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

No description

## `contacts` (type: `string`):

No description

# 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 = {
    "onlyNew": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("stefano_seggio/florida-tenders-monitor").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 = { "onlyNew": True }

# Run the Actor and wait for it to finish
run = client.actor("stefano_seggio/florida-tenders-monitor").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 '{
  "onlyNew": true
}' |
apify call stefano_seggio/florida-tenders-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,stefano_seggio/florida-tenders-monitor"
        }
    }
}

```

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/afSZyXLVcgnLpucyo/builds/qWOjMwMwLiYnmqA18/openapi.json
