Bidlo API docs

Bidlo opens its heavy civil market data to agents through an MCP server and a REST API. Both read and write the same collections your team sees in the app, as far as the key allows.
OpenAPI specGet an API key
Contents

What can I ask it?

Ask in plain words: an agent turns the question into a query and reads back what matches.

“Show me upcoming asphalt jobs in Bexar County.”
Request
MCP tool: documents_query

{
  "collection_id": "Project",
  "filters": [
    { "field_id": "County", "operator": "contains",
      "value": ["<document_id of Bexar County>"] },
    { "field_id": "Bid Date", "operator": "gte", "value": "today" },
    { "field_id": "Type of Work", "operator": "contains",
      "value": "asphalt" }
  ],
  "sorts": [{ "field_id": "Bid Date", "direction": "asc" }],
  "fields": ["Bid Date"]
}
Response
{
  "data": [
    {
      "id": "<document_id>",
      "name": "<job name>",
      "collection_id": "<collection_id>",
      "created_at": "<timestamp>",
      "updated_at": "<timestamp>",
      "fields": {
        "<field_id>": {
          "value": "<bid date>",
          "field_name": "Bid Date",
          "field_type": "date",
          "field_id": "<field_id>",
          "field_value_id": "<field_value_id>"
        }
      }
    }
  ],
  "meta": {
    "page": 1,
    "limit": 100,
    "collection_id": "<collection_id>",
    "next_cursor": null
  }
}
“Show me a list of qualified striping subs.”
Request
MCP tool: documents_query

{
  "collection_id": "Contractor",
  "filters": [
    { "field_id": "Primary Trade", "operator": "contains",
      "value": ["<document_id of the striping trade>"] },
    { "field_id": "Status", "operator": "contains",
      "value": ["<document_id of the Prequalified option>"] }
  ],
  "fields": ["Phone", "Website"]
}
Response
{
  "data": [
    {
      "id": "<document_id>",
      "name": "<company name>",
      "collection_id": "<collection_id>",
      "created_at": "<timestamp>",
      "updated_at": "<timestamp>",
      "fields": {
        "<field_id>": {
          "value": "<phone>",
          "field_name": "Phone",
          "field_type": "text",
          "field_id": "<field_id>",
          "field_value_id": "<field_value_id>"
        },
        "<field_id>": {
          "value": "<website>",
          "field_name": "Website",
          "field_type": "text",
          "field_id": "<field_id>",
          "field_value_id": "<field_value_id>"
        }
      }
    }
  ],
  "meta": {
    "page": 1,
    "limit": 100,
    "collection_id": "<collection_id>",
    "next_cursor": null
  }
}
“Which soils within 10 miles of this point have a plasticity index over 35?”
Request
MCP tool: documents_query

{
  "collection_id": "Soil Layer",
  "filters": [
    { "field_id": "Plasticity Index", "operator": "gt", "value": 35 },
    { "field_id": "Location", "operator": "within_distance",
      "value": { "lat": 29.4241, "lng": -98.4936,
                 "radiusInMeters": 16093 } }
  ],
  "fields": ["Plasticity Index"]
}
Response
{
  "data": [
    {
      "id": "<document_id>",
      "name": "<soil layer name>",
      "collection_id": "<collection_id>",
      "created_at": "<timestamp>",
      "updated_at": "<timestamp>",
      "fields": {
        "<field_id>": {
          "value": <plasticity index>,
          "field_name": "Plasticity Index",
          "field_type": "number",
          "field_id": "<field_id>",
          "field_value_id": "<field_value_id>"
        }
      }
    }
  ],
  "meta": {
    "page": 1,
    "limit": 100,
    "collection_id": "<collection_id>",
    "next_cursor": null
  }
}
“Who’s on the estimating team at Vantel Heavy Civil?”
Request
MCP tool: documents_query

{
  "collection_id": "People",
  "filters": [
    { "field_id": "Company", "operator": "contains",
      "value": ["<document_id of Vantel Heavy Civil>"] },
    { "field_id": "Dept", "operator": "contains",
      "value": ["<document_id of the Estimating option>"] }
  ],
  "fields": ["Email", "Phone"]
}
Response
{
  "data": [
    {
      "id": "<document_id>",
      "name": "<person’s name>",
      "collection_id": "<collection_id>",
      "created_at": "<timestamp>",
      "updated_at": "<timestamp>",
      "fields": {
        "<field_id>": {
          "value": "<email>",
          "field_name": "Email",
          "field_type": "text",
          "field_id": "<field_id>",
          "field_value_id": "<field_value_id>"
        },
        "<field_id>": {
          "value": "<phone>",
          "field_name": "Phone",
          "field_type": "text",
          "field_id": "<field_id>",
          "field_value_id": "<field_value_id>"
        }
      }
    }
  ],
  "meta": {
    "page": 1,
    "limit": 100,
    "collection_id": "<collection_id>",
    "next_cursor": null
  }
}
“Give me every bid price on item 3076 in the San Antonio district over the last three years.”
Request
MCP tool: documents_query, three calls

1. The jobs
{
  "collection_id": "Project",
  "filters": [
    { "field_id": "District", "operator": "contains",
      "value": ["<document_id of the San Antonio district>"] },
    { "field_id": "Bid Date", "operator": "relative_range",
      "value": { "period": "past", "timeframe": "year", "number": 3 } }
  ]
}

2. Item 3076 on those jobs
{
  "collection_id": "Project Items",
  "filters": [
    { "field_id": "Project", "operator": "contains",
      "value": ["<document_id of each job>"] },
    { "field_id": "Code", "operator": "starts_with", "value": "3076-" }
  ]
}

3. Every bid on those items
{
  "collection_id": "Bid Items",
  "filters": [
    { "field_id": "Project Item", "operator": "contains",
      "value": ["<document_id of each item>"] }
  ],
  "fields": ["Price", "Quantity"]
}
Response
{
  "data": [
    {
      "id": "<document_id>",
      "name": "<bid item name>",
      "collection_id": "<collection_id>",
      "created_at": "<timestamp>",
      "updated_at": "<timestamp>",
      "fields": {
        "<field_id>": {
          "value": <unit price>,
          "field_name": "Price",
          "field_type": "number",
          "field_id": "<field_id>",
          "field_value_id": "<field_value_id>"
        },
        "<field_id>": {
          "value": <quantity>,
          "field_name": "Quantity",
          "field_type": "number",
          "field_id": "<field_id>",
          "field_value_id": "<field_value_id>"
        }
      }
    }
  ],
  "meta": {
    "page": 1,
    "limit": 100,
    "collection_id": "<collection_id>",
    "next_cursor": null
  }
}

What data is in it?

Bidlo holds public heavy civil lettings from the day a job is advertised to the day it is awarded, with the bid sheets, the unit prices as bid, the contractors, the plants and the agencies behind them. Counted on 2026-09-08, the three largest collections held 2,000,000+ bid items, 87,000+ jobs and 4,800+ agencies. Both surfaces reach every collection below and any other your team can see, except the app's own pages, chats and views.

CategoryCollectionWhat it holds
ProjectsProjectJobs from advertisement to award: owner, county, letting date, value.
Project ItemsThe bid sheet: code, description, unit, quantity.
Bid ItemsUnit prices as bid, item by item, by contractor.
Pre-Bid Q&AQuestions before a letting, with the owner’s answers.
Change OrdersLine items paid on change orders after award.
ScopesThe trades a company works and an item calls for.
FilesPlans, specs, bid sheets and addenda.
ContractorsContractorsEvery company in a market: primes, subs, vendors, suppliers.
FacilitiesWhere those companies operate: plants, pits, yards, offices, with coordinates.
PermitsPermits pulled for the work, by county.
GeologySoil AreaMapped soil units: classification, acreage, drainage, slope.
Soil SurveyComponents of each soil area: profile depth, water depth, obstructions.
Soil LayerLayers of a soil profile: depth range, texture, plasticity.
MarketAgencyThe owners that let work, from state DOTs to cities and districts.
CountiesHow a market is cut geographically.
MaterialsStandard categories that line items up across agencies and spec books.

And many more. Anything your team attaches to a job, a contractor or a facility reads through the same key.

How does an agent connect?

An agent connects over MCP: Bidlo runs a streamable HTTP server at https://app.bidlo.ai/api/mcp, and a client needs that URL and one Authorization header carrying a Bidlo API key.

Claude Desktop, Claude Code and Cursor all take the config below.

Client config
{
  "mcpServers": {
    "bidlo": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://app.bidlo.ai/api/mcp",
        "--header",
        "Authorization: Bearer YOUR_API_KEY"
      ]
    }
  }
}

The tools follow the key: a read-only key sees 12 read tools, and Full access opens all 34. MCP server has every tool and client config. Querying data has the filters and sorts.

Is there a REST API?

Yes: https://app.bidlo.ai/api/v1 reads and writes documents, collections, fields, files and comments, and reads field history, with the same key and permissions the MCP server takes. REST API has the routes and examples. /openapi.json is the same API as an OpenAPI 3.1 spec.

How do I get access?

Bidlo data needs a Bidlo account, and anyone can get one: book a call, Bidlo sets up your team, and a team owner or admin then mints the key under Settings → Features → API. Both surfaces take that one key. Access and keys covers permissions, expiry and revoking a key.

Book a call

What can I build with it?

Reports, alerts and price checks with a key that reads, and synced job and contractor lists with one that can also edit data.

With a key that reads:

  • A county market report. Filter jobs by county and letting date, then total what was let by owner over the last year.
  • A letting watch. Read the jobs advertised since the last run and post the new ones where your estimators read them.
  • Subs and suppliers near a job. Filter facilities with within_distance on the job's own coordinates to find the plants, pits and yards that can reach it.
  • A unit price check before bid day. Pull the bid items on jobs like yours and compare what they went for against the number on your sheet.

With a key that can also edit data:

  • Your own bid results. After each letting, push your numbers from your estimating system into your team's own collection, next to the public tabs.
  • Job updates. Write status, dates and notes onto your jobs from the system your project managers already use.
  • A contractor list kept in sync. Upsert your subs and suppliers on a name or a license number each night. Matches are updated and new ones added; run it dry first.