Skip to main content

AI search, asynchronously

Find projects no structured filter can express — "unresolved traffic concerns". Searching is free; Find Contact Info is billed separately.

Lead generation and business developmentAI-native analysts and MCP clients

Who this is for

You are looking for something the Building Type tree does not have a field for. "Projects where neighbours raised unresolved traffic concerns" is not a filter and never will be; it is a question about what the minutes say.

How it works

  1. 1You submit a natural-language query, optionally bounded by the small, closed structured set this route accepts: states/stateIds, countyIds, cityIds, type, subtype, dateFrom, dateTo, leadTypes, limit, offset, sort, order, representation, deliveryMode, include, contactOptions, notify. Two traps worth reading twice. County and city are numeric ids only here — no names, unlike /projects/search. And sort is snake_case here — meeting_date, updated_at, data_richness — not the camelCase tokens on the filters page, so a copied sort value is a 400. Every other filter — assetClasses, propertyTypes, propertySubtypes, projectSizes, acreageMin/Max, totalUnitCountMin/Max, status, requestTypes, constructionTypes, dateRange, projectIds, polygon/bbox/circle — is a hard 400 invalid_request before anything is reserved. Put those on a /projects/count + /projects/search pull and use the job for the concept half of the thesis.
  2. 2We retrieve candidates and rank them by how well they match.
  3. 3We read the candidates in ranked order and confirm or reject each one against your query, working through them in batches.
  4. 4We stop as soon as a batch confirms nothing — matches concentrate at the top of the ranking, so continuing past that point spends time and money to find nothing.
  5. 5You receive the confirmed matches. On County, State, and National that delivery is free. On API Only and the trial, you are charged 1 credit for each new record.

Phrasing your query

Describe the concept you are hunting for; do not enumerate amenities or attributes as if they were filters. A prompt that reads as a list of hard constraints — amenity types, square footage, architectural style — is refused before any credits are reserved with 400 invalid_request and error.details.unsupportedConstraints naming what cannot yet be proved against the returned record. The same idea phrased descriptively is accepted and matched against what the minutes actually say.

PromptOutcome
"Communities with a lagoon, water park, or major water amenity"Refused pre-spend: reads as an amenity-type constraint (unsupportedConstraints: ["amenityTypes"]). Nothing reserved, nothing charged.
"Resort-style master-planned communities, especially projects that mention a lagoon"Accepted: a description the evidence verifier can confirm against the meeting record, with the quote that proves it.

Submit

Submit an AI search

bash
curl -X POST 'https://api.boardwalkai.com/api/v1/projects/ai-search/jobs' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY' \
  -H 'Idempotency-Key: traffic-concerns-2026-08-02' \
  -H 'Content-Type: application/json' \
  -d '{
    "query": "projects where neighbours raised unresolved traffic concerns",
    "states": [
        "UT",
        "NV",
        "AZ"
    ],
    "limit": 100
}'

Response · HTTP 202

json
{
  "requestId": 918442,
  "status": "queued",
  "pollUrl": "https://api.boardwalkai.com/api/v1/projects/ai-search/jobs/918442",
  "meta": {
    "creditsReserved": 100,
    "limit": 100,
    "trialTruncation": null,
    "requestId": "req_01K1QF3M0000EXAMPLE0001"
  }
}

The response shape on this page is the one the endpoint builds — the keys, their nesting and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Credits are reserved up front against your requested limit, and settled down to what is actually delivered. The Idempotency-Key matters more here than anywhere else on the surface: a duplicated submit reserves the budget twice, and two identical submits can hold the whole pool while both run. With a key, a retry replays the same job. Because the reservation holds real budget, submit modest limits, serially — jobs are worked through in series, so ten queued 100-record jobs hold 1,000 credits while they wait.

When the job settles — completed, partial, failed or cancelled — we also email the submitter's account address: counts, credits charged and refunded, and the poll URL, never record data. It is on by default; opt out with notify: {"email": false} on the submit body.

Poll

Poll it — and note the charge is smaller than the reservation

bash
curl 'https://api.boardwalkai.com/api/v1/projects/ai-search/jobs/918442' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "requestId": 918442,
  "status": "completed",
  "creditsReserved": 100,
  "creditsCharged": 1,
  "createdAt": "2026-08-02T14:31:02+00:00",
  "startedAt": "2026-08-02T14:31:04+00:00",
  "completedAt": "2026-08-02T14:33:47+00:00",
  "expiresAt": "2026-08-05T14:33:47+00:00",
  "data": [
    {
      "id": 184203,
      "recordType": "project",
      "projectName": "Alta Ridge Phase II",
      "projectHeadline": "Planning Commission approved 240 apartments with conditions",
      "boardwalkLink": "https://boardwalkai.com/map/#project=184203",
      "mergedFrom": [
        184203,
        184987
      ],
      "lastUpdated": "2026-06-18T09:22:41Z",
      "meetingDate": "2026-06-17",
      "delivery": {
        "alreadyExported": false,
        "updatedSinceExport": null,
        "lastExportedAt": null,
        "billed": true
      },
      "address": "3600 S Constitution Blvd, West Valley City, UT",
      "city": "West Valley City",
      "county": "Salt Lake",
      "state": "UT",
      "postalCode": "84119",
      "latitude": 40.6916,
      "longitude": -112.0011,
      "locationPrecision": "address",
      "locationPrecisionLabel": "Address",
      "isApproximate": false,
      "parcelApn": [
        "15-27-301-004"
      ],
      "inOpportunityZone": true,
      "inFemaFloodZone": false,
      "propertyType": "residential",
      "propertyTypeLabel": "Residential",
      "propertySubtype": "multifamily",
      "propertySubtypeLabel": "Multifamily",
      "assetClass": "Apartment Building",
      "assetClassLabel": "Apartment Building",
      "leadType": "private",
      "leadTypeLabel": "Private",
      "stage": "entitlement",
      "stageLabel": "Entitlement",
      "landUses": [
        {
          "type": "residential",
          "typeLabel": "Residential",
          "subtype": "multifamily",
          "subtypeLabel": "Multifamily",
          "assetClass": "Apartment Building",
          "assetClassLabel": "Apartment Building",
          "matchedOn": []
        }
      ],
      "status": "approved_with_conditions",
      "statusLabel": "Approved with Conditions",
      "statusSummary": "The commission approved the preliminary plat subject to eight conditions.",
      "requestType": "final_plat",
      "requestTypeLabel": "Final Plat",
      "decisionBody": "Planning Commission",
      "caseNumbers": [
        "PLAT-2026-0142"
      ],
      "acreage": 9.8,
      "squareFootage": null,
      "unitMix": [
        {
          "count": 240,
          "type": "apartment",
          "typeLabel": "Apartment"
        }
      ],
      "totalUnitCount": 240,
      "bedroomCount": null,
      "buildingStories": null,
      "floorCount": null,
      "buildingHeightFeet": null,
      "parkingSpaces": 372,
      "existingZoning": "A-1",
      "proposedZoning": "RM-16",
      "isRezone": true,
      "constructionType": "new_building",
      "constructionTypeLabel": "New Building",
      "projectSize": null,
      "projectSizeLabel": null,
      "developerCompany": "Alta Ridge Development LLC",
      "developerCompanyRole": "developer",
      "developerCompanyRoleLabel": "Developer",
      "contacts": [
        {
          "name": "Rachel Okafor",
          "title": "Director of Development",
          "role": "developer",
          "roleLabel": "Developer",
          "partyClass": "external",
          "partyClassLabel": "External Party",
          "entityType": "person",
          "company": "Alta Ridge Development LLC",
          "email": "r.okafor@example.com",
          "emailSource": "filing",
          "phone": "+1-555-0142",
          "phoneSource": "filing",
          "linkedinUrl": null,
          "linkedinSource": null,
          "contactProvenance": "filing",
          "enrichmentSource": null,
          "enrichedAt": null,
          "enrichmentStatus": "not_requested",
          "contactsWithheldReason": null
        }
      ],
      "contactEnrichment": {
        "status": "not_requested",
        "statusLabel": "Not Requested",
        "attemptedAt": null,
        "contactsFound": 0,
        "source": "registry"
      },
      "companyEnrichment": [],
      "publicOfficials": [
        {
          "name": "Dana Whitfield",
          "title": "Senior Planner",
          "role": "city_planner",
          "roleLabel": "City Planner",
          "partyClass": "government",
          "organization": "West Valley City"
        }
      ],
      "contactSummary": {
        "externalCount": 1,
        "governmentCount": 1,
        "unclassifiedCount": 0,
        "withEmail": 1,
        "withPhone": 1,
        "withLinkedin": 0,
        "enrichedCount": 0,
        "lowConfidenceWithheld": 0,
        "withheldCount": 0,
        "withheldReason": null
      },
      "keyFacts": [
        {
          "fact": "240 apartment units across six buildings",
          "category": "unit_mix",
          "categoryLabel": "Unit Mix"
        }
      ],
      "evidence": "Commissioner Reyes moved to approve subject to conditions 1-8.",
      "summary": "A 240-unit garden-style apartment community on 9.8 acres.",
      "voteSummary": {
        "yes": 2,
        "no": 1,
        "abstain": 0,
        "absent": 1,
        "total": 4,
        "isUnanimous": false,
        "meetingDate": "2026-06-17"
      },
      "sourceDocumentUrl": "https://www.example-city.gov/media/agendas/2026-06-17-planning-commission-packet.pdf",
      "aiMatch": {
        "score": 10,
        "summary": "The minutes record unresolved traffic and circulation concerns raised by nearby residents.",
        "channels": [
          "semantic",
          "keyword"
        ]
      }
    }
  ],
  "meta": {
    "schemaVersion": "2026-09-16",
    "representation": "standard",
    "total": 3000,
    "limit": 100,
    "offset": 0,
    "creditsUsed": 1,
    "creditsReserved": 100,
    "trialTruncation": null,
    "parsedQuery": {
      "structuredFilters": {
        "stateIds": [
          1,
          57,
          54
        ]
      },
      "searchKeywords": "traffic concerns neighbours",
      "semanticQuery": "projects where neighbours raised unresolved traffic concerns",
      "explanation": "Looks for public-comment evidence of unresolved traffic concerns.",
      "confidence": 0.92,
      "query_type": "evidence_search"
    },
    "searchMethod": "ai_evidence_verified",
    "matchesByProjectId": {
      "184203": {
        "confirmed": true,
        "evidenceWhy": "The minutes record unresolved traffic and circulation concerns raised by nearby residents.",
        "evidenceQuotes": [
          "Residents raised concerns about traffic, access, and circulation."
        ],
        "retrievalScore": 0.08125,
        "channels": [
          "semantic",
          "keyword"
        ],
        "channelCount": 2
      }
    },
    "lowConfidence": false,
    "candidatesRetrieved": 3000,
    "candidatesScreened": 2000,
    "batchesRun": 2,
    "screeningCeiling": 10000,
    "screeningCeilingSource": "request_cap",
    "batchSizeYields": [
      1,
      0
    ],
    "stopReason": "unproductive_batch",
    "partialReason": null,
    "matchCount": 1,
    "matchCountIsExact": false,
    "reusedVerdictCount": 0,
    "verifierErrorCount": 0,
    "delivery": {
      "mode": "all",
      "notPreviouslyExported": 1,
      "previouslyExported": 0,
      "updatedSinceExport": 0,
      "billed": 1,
      "notRebilled": 0,
      "ledgerApplied": true
    },
    "requestId": "req_01K1QF3M0000EXAMPLE0001"
  },
  "stopReason": "unproductive_batch",
  "partialReason": null
}

The response shape on this page is the one the endpoint builds — the keys, their nesting and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

On API Only and the trial you may see reserved 100, charged 1. That is the normal case, not an exception. The quote is a ceiling, not a prediction: early stop means most requests confirm far fewer records than the limit they reserved against, and unused credits are refunded. On County, State, and National the job is free, so reservation and charge both stay at 0.

Credit costFree — 0 creditsPolling is free. The records the job delivers were charged when the job settled, not when you read them.

The ceilings

CeilingValueWhat it means for you
Candidates reviewedUp to 10,000 per requestA hard stop on how much reading one request can do. Reaching it is reported, not hidden.
Confirmed matches returnedUp to 100 per requestThe largest result set one AI search can deliver. Free on County, State, and National; on API Only and the trial, at most 100 credits.
Per-account daily budgetEnforced, value not publishedThere is a per-account daily ceiling on screening work. A job may finish partial with partialReason: daily_ai_budget_exhausted; confirmed matches still ship and unused reserved credits are refunded.

What it costs

Credit costFree — 0 creditsCounty, State, and National: free. Standalone API Only and the free trial: credits are reserved at 1 per requested record and settled against confirmed matches. Finding contact info on those records is a separate enrichment job (included on API Only after the record is purchased).
Confirmed matches deliveredCounty / State / NationalTrial or API Only
000
23023
1000100

Changed your mind? Cancel it

DELETE /projects/ai-search/jobs/{id} is free and only ever gives credits back. A queued job is cancelled outright with its full reservation refunded. A running job stops at the next batch boundary and settles partial with partialReason: cancelled_by_user — confirmed records are charged and delivered on the poll, the unused reservation is refunded. Cancelling a job that already finished is an idempotent no-op. Teammates on your credit pool can poll and cancel each other's jobs; anyone else gets a 404.

What can go wrong

  • 402 insufficient_credits — the reservation does not fit in the balance. Lower limit, or top up. Nothing was reserved.
  • A job that ends partial delivered real, billed, verified records and stopped early for a stated reason in stopReason / partialReason. It is not a failure and the records are yours.
  • A job that ends expired means the result was not collected in time — results expire 72 hours after completion, so poll promptly. Submit a new search; records that were billed remain yours and re-deliver free while unchanged.

Next

Ready to make a call?

A free Boardwalk trial includes API access, 50 record-export credits (search, CSV, MCP), and 5 Find Contact Info lookups. Counting, taxonomy, location lookups and the analytics plane cost nothing, so you can evaluate the data before you spend a credit.