Desarrolladores
What data is in it?
Bidlo tracks public lettings across states and owners, from the day a job is advertised to the day it is awarded. Queried on 2026-09-08, the three largest collections held 2,064,249 bid items, 87,868 jobs and 4,871 agencies. Every collection below is readable through the MCP server; the REST API names three of them.
| Collection | What it holds |
|---|---|
| Bid Items | Unit prices as bid, item by item, with the job and the contractor behind each one. 2,064,249 on 2026-09-08. |
| Project | Jobs from advertisement through award — owner, county, letting date and value. 87,868 on 2026-09-08. |
| Agency | The owners that let the work, from state DOTs down to cities and districts. 4,871 on 2026-09-08. |
| Project Items | The bid sheet for one job: code, description, unit and quantity. |
| Bids | What each contractor bid on a job, and who took it. |
| Contractors | Firms that bid, with the work they have bid and won. |
| Facilities | Plants, pits and yards, with the coordinates to find them near a job. |
| Counties | Counties, which is how a market gets cut geographically. |
| Pre-Bid Q&A | Questions asked before a letting, and the answers the owner gave. |
| Change Order Items | Line items paid on change orders after award. |
How does an agent connect?
Start with MCP. Bidlo runs a streamable HTTP server at https://lite.bidlo.ai/api/mcp, and one header — Authorization: Bearer sk_live_… — is the whole of the setup. There is no OAuth handshake and nothing to register: the discovery probes answer 404 on purpose, so a client falls through to the key it was given.
Any client that reads an mcpServers block takes the config below. Claude Desktop, Claude Code and Cursor all do.
{
"mcpServers": {
"bidlo": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://lite.bidlo.ai/api/mcp",
"--header",
"Authorization: Bearer YOUR_API_KEY"
]
}
}
}Cursor installs from a deeplink instead. The config inside it is base64, so re-encode it with a real key in place of YOUR_API_KEY rather than editing the string.
cursor://anysphere.cursor-deeplink/mcp/install?name=bidlo&config=eyJ1cmwiOiJodHRwczovL2xpdGUuYmlkbG8uYWkvYXBpL21jcCIsImhlYWRlcnMiOnsiQXV0aG9yaXphdGlvbiI6IkJlYXJlciBZT1VSX0FQSV9LRVkifX0=The server registers five read tools and no write tools.
| Tool | What it does |
|---|---|
| get_team_collections | Lists the collections this key can read. |
| get_collection_fields | Lists the fields on one or more collections, with the operators each field will take. |
| resolve_documents_by_name | Turns a name into the id a filter needs. |
| query_database_data | Reads a page of documents: filters, sorts, a limit and a page. |
| query_mentioned_document | Reads one document by id. |
A filter on query_database_data is a field name, an operator and a value. The operators are is_null, is_not_null, eq, ne, contains, not_contains, starts_with, ends_with, gt, lt, gte, lte, between, relative_range, within_distance, outside_distance. limit runs 1 to 100 and defaults to 25; page starts at 1. Call get_collection_fields first — it returns the operators a given field will take.
Coming. Market tools are being added: search_projects, search_contractors, search_facilities, and comparable unit prices for an item in a county. None of them is live today. Compose the same reads with query_database_data until they are.
Is there a REST API?
Yes, for three collections. GET or POST https://lite.bidlo.ai/api/v1/{collection_id}, where collection_id is projects, contractors or facilities. The key names the team, so a request carries no other identity.
curl -X POST https://lite.bidlo.ai/api/v1/facilities \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"filters": [
{
"field_id": "FACILITY_LOCATION_FIELD_ID",
"operator": "within_distance",
"value": { "lat": 30.2672, "lng": -97.7431, "radiusInMeters": 50000 }
}
],
"limit": 100
}'curl -X POST https://lite.bidlo.ai/api/v1/projects \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"filters": [
{
"field_id": "PROJECT_BID_DATE_FIELD_ID",
"operator": "between",
"value": { "start": "2026-10-01", "end": "2026-12-31" }
}
],
"sorts": [{ "field_id": "PROJECT_BID_DATE_FIELD_ID", "direction": "asc" }],
"limit": 100
}'A REST filter names its field by id where an MCP filter names it in words, and one field takes one filter — a second on the same field is a 400. Read the ids off get_collection_fields and carry them across. limit defaults to 100 and tops out at 2,000; page starts at 1 and comes back in meta.
{
"data": [
{
"id": "…",
"name": "Bexar Resurface Roadway - ID: 0143-01-068",
"collection_id": "…",
"fields": {
"<field_id>": {
"value": "…",
"field_name": "Bid Date",
"field_type": "date",
"field_id": "…"
}
}
}
],
"meta": { "page": 1, "collection_id": "…" }
}The full reference is /openapi.json, an OpenAPI 3.1 document. The two paths in it marked x-status: coming are not live yet.
How do I get a key?
A team owner or admin mints one in the app, under Settings → Features → API. The MCP page beside it mints the same key labelled Bidlo MCP with read access, and prints a finished client config under it.
A key is sk_live_ and 48 hex characters, it carries read or write access, and it is hashed at rest — Bidlo shows it once and cannot show it again.
Bidlo sets up new teams itself today, so a key starts with a call.
What can I build with it?
- 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.
Anything those reads will not answer, mail support@bidlo.ai.