Skip to main content

Count a query first

See how many projects match. On API Only and the trial, count also prices the set and shows what a 402 looks like.

Who this is for

Everyone. Count is always free. On County, State, and National, search is free too — count tells you how big the set is. On API Only and the free trial, the same response also prices the set, because those plans charge 1 credit per new record.

The scenario

You are on API Only, with 500 credits left. Your criteria match 5,000 projects. What happens?

Step 1 — ask, for free

Price a broad query — before spending anything

bash
curl 'https://api.boardwalkai.com/api/v1/projects/count?states=UT&assetClasses=Apartment%20Building&dateRange=all' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": {
    "matchCount": 5000,
    "matchCountIsExact": true,
    "matchCountBasis": "enumerated",
    "countedAs": "distinct_projects",
    "collapseApplied": true,
    "duplicatesCollapsed": 0,
    "scanCeiling": 10000,
    "billable": {
      "records": 5000,
      "credits": 0,
      "creditRate": 0,
      "isUpperBound": true,
      "nonBillable": {
        "alreadyDeliveredUnchanged": 0,
        "ledgerApplied": true
      }
    },
    "affordability": {
      "affordable": true,
      "blockReason": null,
      "shortfallCredits": 0,
      "maxAffordableRecords": 9223372036854776000,
      "boundBy": "period_balance",
      "unlimited": false
    },
    "ordering": {
      "sort": "meetingDate",
      "order": "desc",
      "tiebreak": null,
      "meaning": "Most recent meeting evidence first — the date of the meeting the record was extracted from, not when Boardwalk ingested it and not when the project was created."
    },
    "truncation": {
      "policy": "refuse",
      "wouldTruncate": false,
      "deliverableRecords": null,
      "explanation": "The whole match set fits inside the remaining balance: page through the metered route and every page will be delivered in full until the set is exhausted."
    }
  },
  "meta": {
    "schemaVersion": "2026-09-16",
    "representation": "standard",
    "deliveryMode": "all",
    "credits": {
      "limit": 500,
      "used": 0,
      "remaining": 500,
      "charged": 0,
      "periodStart": "2026-08-01",
      "periodEnd": "2026-08-31",
      "isTeamPool": true
    },
    "appliedFilters": {
      "states": [
        1
      ],
      "assetClasses": [
        "Apartment Building"
      ],
      "dateRange": "all"
    },
    "appliedDefaults": [
      "taxonomyMatchMode=primary_only",
      "includeGovernmentDecisions=false",
      "leadTypes=private",
      "sort=meetingDate",
      "order=desc"
    ],
    "resolvedFrom": {
      "states": [
        {
          "input": "UT",
          "id": 1
        }
      ]
    },
    "warnings": []
  },
  "links": {
    "self": "https://api.boardwalkai.com/api/v1/projects/count?states=UT&assetClasses=Apartment%20Building&dateRange=all",
    "search": "https://api.boardwalkai.com/api/v1/projects/search?states=UT&assetClasses=Apartment%20Building&dateRange=all",
    "docs": "https://boardwalkai.com/docs/api/budgets/"
  }
}

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.

Read data.affordability. affordable: false, shortfallCredits: 4500, maxAffordableRecords: 500. That is not a prediction that your next search call fails — it means the whole remaining set does not fit your balance. Search delivers at most one page per request, and data.truncation.policy: refuse names the page rule: a paid page is delivered whole or refused whole, never trimmed. Page this set from the top at limit=100 and pages 1–5 each deliver and bill 100 credits; the balance is then zero, and page 6 is refused with 402 credits_exhausted. You collect exactly the 500 records the count promised and stop without overspending. The count's value is knowing all of that — what the set costs and what your balance can buy — before you spend anything, so the spending is a decision rather than a discovery.

Step 2 — see what refusal actually looks like

insufficient_credits is the other refusal: it fires when the page a request would deliver costs more than the balance has left. Here the caller has 40 credits remaining and asks for a fresh page of 100. That page is refused whole, and this is the entire outcome — no records, no partial page, no charge:

What a request you cannot afford looks like

bash
curl 'https://api.boardwalkai.com/api/v1/projects/search?states=UT&assetClasses=Apartment%20Building&dateRange=all&limit=100' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 402

json
{
  "error": {
    "code": "insufficient_credits",
    "message": "This request would deliver 100 records and cost 100 credits. You have 40.",
    "param": null,
    "details": {
      "quote": {
        "matchCount": 5000,
        "matchCountIsExact": true,
        "creditRate": 0,
        "creditsRequired": 100,
        "creditsRemaining": 40,
        "affordableRecordCount": 0,
        "countUrl": "https://api.boardwalkai.com/api/v1/projects/count?states=UT&assetClasses=Apartment%20Building&dateRange=all"
      }
    },
    "docsUrl": "https://boardwalkai.com/docs/api/errors#insufficient_credits",
    "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.

The quote is the recovery kit. creditsRequired: 100 is what the refused page would have cost — always the page's cost, never the set's. affordableRecordCount: 40 is the limit to retry with: limit=40 buys the part the balance can still pay for. matchCount: 5000 sizes the whole set, and countUrl is the free URL that re-prices this exact filter set. An agent that receives this response can recover on its own — it does not need a human to interpret a number.

Step 3 — narrow, and check again

Count a narrower query

bash
curl 'https://api.boardwalkai.com/api/v1/projects/count?states=UT&assetClasses=Apartment%20Building&meetingDateFrom=2026-05-01&totalUnitCountMin=50' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": {
    "matchCount": 250,
    "matchCountIsExact": true,
    "matchCountBasis": "enumerated",
    "countedAs": "distinct_projects",
    "collapseApplied": true,
    "duplicatesCollapsed": 0,
    "scanCeiling": 10000,
    "billable": {
      "records": 250,
      "credits": 0,
      "creditRate": 0,
      "isUpperBound": true,
      "nonBillable": {
        "alreadyDeliveredUnchanged": 0,
        "ledgerApplied": true
      }
    },
    "affordability": {
      "affordable": true,
      "blockReason": null,
      "shortfallCredits": 0,
      "maxAffordableRecords": 9223372036854776000,
      "boundBy": "period_balance",
      "unlimited": false
    },
    "ordering": {
      "sort": "meetingDate",
      "order": "desc",
      "tiebreak": null,
      "meaning": "Most recent meeting evidence first — the date of the meeting the record was extracted from, not when Boardwalk ingested it and not when the project was created."
    },
    "truncation": {
      "policy": "refuse",
      "wouldTruncate": false,
      "deliverableRecords": null,
      "explanation": "The whole match set fits inside the remaining balance: page through the metered route and every page will be delivered in full until the set is exhausted."
    }
  },
  "meta": {
    "schemaVersion": "2026-09-16",
    "representation": "standard",
    "deliveryMode": "all",
    "credits": {
      "limit": 500,
      "used": 0,
      "remaining": 500,
      "charged": 0,
      "periodStart": "2026-08-01",
      "periodEnd": "2026-08-31",
      "isTeamPool": true
    },
    "appliedFilters": {
      "states": [
        1
      ],
      "assetClasses": [
        "Apartment Building"
      ],
      "meetingDateFrom": "2026-05-01T00:00:00+00:00",
      "totalUnitCountMin": 50
    },
    "appliedDefaults": [
      "taxonomyMatchMode=primary_only",
      "includeGovernmentDecisions=false",
      "leadTypes=private",
      "sort=meetingDate",
      "order=desc"
    ],
    "resolvedFrom": {
      "states": [
        {
          "input": "UT",
          "id": 1
        }
      ]
    },
    "warnings": []
  },
  "links": {
    "self": "https://api.boardwalkai.com/api/v1/projects/count?states=UT&assetClasses=Apartment%20Building&meetingDateFrom=2026-05-01&totalUnitCountMin=50",
    "search": "https://api.boardwalkai.com/api/v1/projects/search?states=UT&assetClasses=Apartment%20Building&meetingDateFrom=2026-05-01&totalUnitCountMin=50",
    "docs": "https://boardwalkai.com/docs/api/budgets/"
  }
}

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.

250 records, 250 credits, affordable: true, shortfallCredits: 0. On API Only and the trial, the whole set now fits: page through search and every page is delivered in full until the set is exhausted, for a total of exactly what the quote said, or less. On County, State, and National, search was already free — count just told you the size.

What it costs

Credit costFree — 0 creditsFree, always, including when your balance is zero. How many projects match. On API Only and the trial it also prices the set; on County, State, and National, search is free.

Counting is free even when your balance is zero. Telling a customer who has run out that they may not ask what something would cost is the opposite of the point.

The pattern to build

  1. 1Build the filter set.
  2. 2GET /projects/count with it. Zero credits.
  3. 3If affordability.affordable is false, the whole set will not fit — decide deliberately. Either narrow — a date bound, a smaller geography, a minimum unit count — and count again, or page from the top knowing you will collect maxAffordableRecords records and the first page past them is refused.
  4. 4When it is affordable, send the identical filter set to GET /projects/search. The links.search field in the count response is that exact URL, already built.

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.