Skip to main content

Count before you buy

Price any query for zero credits, and see exactly what a request you cannot afford returns.

Who this is for

Everyone. This is not an advanced technique — it is how the product works, and the first thing any client, human or agent, should do with a new filter set.

The scenario

You have 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",
    "scanCeiling": 10000,
    "billable": {
      "records": 5000,
      "credits": 5000,
      "creditRate": 1,
      "isUpperBound": true,
      "nonBillable": {
        "alreadyDeliveredUnchanged": 0,
        "ledgerApplied": true
      }
    },
    "affordability": {
      "affordable": false,
      "blockReason": "insufficient_credits",
      "shortfallCredits": 4500,
      "maxAffordableRecords": 500,
      "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": "Paid plans are never partially delivered. A request that costs more than the balance is refused with 402 and a quote; narrow the filters or top up."
    }
  },
  "meta": {
    "schemaVersion": "2026-08-02",
    "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": [
        44
      ],
      "assetClasses": [
        "Apartment Building"
      ],
      "dateRange": "all"
    },
    "appliedDefaults": [],
    "resolvedFrom": {
      "states": {
        "UT": 44
      }
    },
    "warnings": [
      {
        "code": "filter_evidence_unconfirmed",
        "message": "These filters currently match our full index. A returned record with a null value for the attribute you filtered on is a record we could not confirm.",
        "params": [
          "assetClasses"
        ]
      }
    ]
  },
  "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. And read data.truncation.policy: refuse. That is the answer to "what would happen if I just called search" — you would get a 402, not 500 of the 5,000 records.

Step 2 — see what refusal actually looks like

If you call the metered endpoint anyway, 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 5,000 records and cost 5,000 credits. You have 500.",
    "param": null,
    "details": {
      "quote": {
        "matchCount": 5000,
        "matchCountIsExact": true,
        "creditRate": 1,
        "creditsRequired": 5000,
        "creditsRemaining": 500,
        "affordableRecordCount": 500,
        "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 countUrl in the quote is the free URL that 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

Add one filter and the same query becomes affordable

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",
    "scanCeiling": 10000,
    "billable": {
      "records": 250,
      "credits": 250,
      "creditRate": 1,
      "isUpperBound": true,
      "nonBillable": {
        "alreadyDeliveredUnchanged": 0,
        "ledgerApplied": true
      }
    },
    "affordability": {
      "affordable": true,
      "blockReason": null,
      "shortfallCredits": 0,
      "maxAffordableRecords": 500,
      "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": "This request fits inside the remaining balance, so every matching record is delivered."
    }
  },
  "meta": {
    "schemaVersion": "2026-08-02",
    "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": [
        44
      ],
      "assetClasses": [
        "Apartment Building"
      ],
      "meetingDateFrom": "2026-05-01T00:00:00+00:00",
      "totalUnitCountMin": 50
    },
    "appliedDefaults": [],
    "resolvedFrom": {
      "states": {
        "UT": 44
      }
    },
    "warnings": [
      {
        "code": "filter_evidence_unconfirmed",
        "message": "These filters currently match our full index. A returned record with a null value for the attribute you filtered on is a record we could not confirm.",
        "params": [
          "assetClasses",
          "totalUnitCountMin"
        ]
      }
    ]
  },
  "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. Now the metered call will succeed and will cost exactly what the quote said, or less.

What it costs

Credit costFree — 0 creditsFree, always, including when your balance is zero. This is the endpoint that tells you what a query would cost before you buy it.

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, narrow — add a date bound, a smaller geography, a minimum unit count — and count again.
  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 and a sandbox key. Counting, filtering, the location tree and the analytics plane cost nothing, so you can evaluate the data before you spend a credit.