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
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
{
"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
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
{
"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
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
{
"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
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
- 1Build the filter set.
- 2
GET /projects/countwith it. Zero credits. - 3If
affordability.affordableis 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 collectmaxAffordableRecordsrecords and the first page past them is refused. - 4When it is affordable, send the identical filter set to
GET /projects/search. Thelinks.searchfield 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.