Budgets, quotes and the 402
Count is always free. On County, State, and National, search is free too. On API Only and the trial, count also prices the set before you page.
Count is always free. It tells you how many distinct projects match. On County, State, and National, search is also free, so you use count to size the set — not to decide whether you can afford it. On API Only and the free trial, the same quote prices the set: those plans charge 1 credit per new record, never overshoot the balance, and never trim a page. A refused page arrives with a quote that says what to retry with.
The free quote
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.
Reading a quote
| Field | What it tells you |
|---|---|
matchCount | How many distinct projects match. Post-collapse, so it counts developments, not filings. |
matchCountIsExact | false when the index held more rows than one request may read, and the true total is higher. scanCeiling is that row budget — not a project count, and not a bound on matchCount. |
billable.records | How many of those you would actually be charged for. Lower than matchCount when you already hold some unchanged. |
billable.credits | The cost. records × creditRate. |
billable.isUpperBound | true when the count was exact — the real charge can only be lower. When the scan hit its ceiling (matchCountIsExact: false) the quote priced a prefix of the match and is not a ceiling, and this field says so. |
affordability.affordable | Whether the whole remaining set fits the balance. false does not mean your next search call fails — you can still page and collect maxAffordableRecords records; the first page that does not fit is refused. |
affordability.shortfallCredits | How many more you would need. |
affordability.maxAffordableRecords | How many records the balance can pay for right now. |
truncation.policy | refuse on a paid plan, trial_truncate on a trial key. It names the page rule — what happens to a page the balance cannot pay for — while the affordability block above is set-scoped. |
ordering | The sort that would be applied, so "the first 500" is a defined set rather than an arbitrary one. |
The refusal
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.
insufficient_credits fires when the page a request would deliver costs more than the balance has left. error.details.quote prices two different things and says which is which: matchCount is the whole match set, courtesy-priced by the same arithmetic as the count endpoint, so the number you narrow against and the number a refusal names cannot disagree; creditsRequired is the billable cost of the refused page, never the set's. affordableRecordCount is the limit to retry with, and countUrl is a free URL that re-prices this exact filter set — which is what lets an agent recover without a human.
Why not just deliver what I can afford?
Because a page trimmed to a budget would be indistinguishable from a page the data made short. A customer who asks for 100 records and silently receives 40 — because that was all the balance covered — concludes our coverage is thin, builds on a partial dataset, and finds out months later. Refusing the page whole is louder and cheaper for everyone, and it keeps a promise worth building on: paid pages are never trimmed, so a short page always means that is all that matched — never that your balance ran out. Trial keys are the exception, and they say so explicitly in meta.trialTruncation.
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.