DEVELOPER API · V1

API documentation

The BuildVue API hands you the same permit backed development projects the web app shows, as JSON, so you can pull them into your own reporting, your CRM or a spreadsheet. It is part of the Business plan. Every endpoint returns visible development projects only, the same rule the product follows, so single family homes and other non development work never appear.

The base address is https://buildvue.co/api/v1 and every response is JSON. A call that works wraps its payload as { data, meta } and a call that fails returns { error }, so you can branch on the shape alone.

Authentication

Create a key on your API keys page. A key is shown once, at the moment you create it, because we keep only a fingerprint of it afterwards. Store it the way you store any other secret and keep it out of code you publish. You can hold five keys at a time and you can revoke any of them from the same page, which takes effect on the next request.

Send the key as a bearer token on every request.

curl https://buildvue.co/api/v1/projects \
  -H "Authorization: Bearer bv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

A request with no key, or with a key that has been revoked, comes back as 401. A key belonging to an account on a smaller plan comes back as 403.

List projects

GET /api/v1/projects returns a page of projects with the filters, sorting and totals the web app uses. Every parameter is optional.

ParameterWhat it does
pagePage number, starting at 1.
pageSizeProjects per page. The default is 50 and the most you can ask for is 100.
sortOne of recency, lastActivity, status, value, stories, units, commercial, retail, parking.
sortDirasc or desc. The default is desc.
textFree text over addresses, permit numbers, descriptions, contractors, developers and architects.
cityCity name, such as Seattle or Bellevue.
neighborhoodOne neighborhood name. Use neighborhoods for a comma separated list.
typeresidential, commercial or mixed_use.
subTypeOne sub type such as multi_family or retail. Use subTypes for a comma separated list.
statusdesign_review, land_use, construction or complete. A comma separated list works too.
zoningOne zoning code. Use zonings for a comma separated list.
applicationDatelast_month, last_quarter, last_year or gt_year.
north, south, east, westMap bounds in degrees. Send all four together to filter by area.
valueMin, valueMaxEstimated construction value range in dollars.
storiesMin, storiesMaxHeight range in stories.
unitsMin, unitsMaxResidential unit count range.
parkingMin, parkingMaxParking stall count range.
includeOldCompletedSet to true to include projects that finished long ago. They are left out by default.
curl "https://buildvue.co/api/v1/projects?city=Seattle&status=construction&unitsMin=50&pageSize=2" \
  -H "Authorization: Bearer $BUILDVUE_API_KEY"
{
  "data": [
    {
      "id": "clx8f0k2p0001qk3h9m2v7c1a",
      "address": "1200 Stewart St",
      "city": "Seattle",
      "neighborhood": "Denny Triangle",
      "status": "construction",
      "projectType": "mixed_use",
      "projectSubTypes": ["multi_family", "retail"],
      "unitCount": 386,
      "stories": 44,
      "parkingSpaces": 120,
      "estimatedValue": "184000000",
      "latitude": 47.6183,
      "longitude": -122.3361
    }
  ],
  "meta": {
    "page": 1,
    "limit": 2,
    "total": 137,
    "totalPages": 69,
    "aggregates": { "count": 137, "units": 21044, "estValue": 8114000000 },
    "version": "v1"
  }
}

The example above is trimmed. A record carries every field the app holds for a project, and anything the source records leave blank comes back as null rather than being dropped.

Get one project

GET /api/v1/projects/{id} returns the full record for one project, with its permits, parcels, sales history, contacts and press coverage. The id is the one the list endpoint gives you.

curl https://buildvue.co/api/v1/projects/clx8f0k2p0001qk3h9m2v7c1a \
  -H "Authorization: Bearer $BUILDVUE_API_KEY"
{
  "data": {
    "id": "clx8f0k2p0001qk3h9m2v7c1a",
    "address": "1200 Stewart St",
    "status": "construction",
    "developer": "Onni Group",
    "architect": "Weber Thompson",
    "unitCount": 386,
    "permits": [
      {
        "permitNumber": "6811234-CN",
        "type": "construction",
        "status": "issued",
        "jurisdiction": "seattle",
        "contractor": "Lease Crutcher Lewis",
        "contractorLicense": "LEASECL881QK",
        "applicationDate": "2024-03-11T00:00:00.000Z",
        "issueDate": "2025-01-22T00:00:00.000Z"
      }
    ],
    "contacts": [
      { "name": "Onni Group", "role": "developer", "company": "Onni Group" }
    ],
    "parcels": [
      { "pin": "0659000305", "sales": [{ "price": 42500000, "date": "2021-06-30T00:00:00.000Z" }] }
    ],
    "newsArticles": []
  },
  "meta": { "version": "v1" }
}

Pipeline aggregates

GET /api/v1/aggregates returns counts and sums for each phase of the pipeline, which is the quickest way to size a market without paging through every project. The filters work as they do on the project list.

ParameterWhat it does
textFree text, matched the same way as on the project list.
typeresidential, commercial or mixed_use.
zoningOne zoning code. Use zonings for a comma separated list.
north, south, east, westMap bounds in degrees. Send all four together to filter by area.
curl "https://buildvue.co/api/v1/aggregates?type=residential" \
  -H "Authorization: Bearer $BUILDVUE_API_KEY"
{
  "data": {
    "design_review": { "count": 62, "units": 9184, "parking": 2410, "officeSqft": 0, "retailSqft": 41200, "estValue": 3120000000 },
    "land_use": { "count": 148, "units": 19022, "parking": 5104, "officeSqft": 122000, "retailSqft": 96400, "estValue": 6740000000 },
    "construction": { "count": 137, "units": 21044, "parking": 6890, "officeSqft": 210500, "retailSqft": 118300, "estValue": 8114000000 },
    "complete": { "count": 204, "units": 28110, "parking": 9033, "officeSqft": 318000, "retailSqft": 174900, "estValue": 10980000000 }
  },
  "meta": { "version": "v1" }
}

Errors

Every failure uses one envelope, so you can read the code without parsing the message. The message is meant for a person reading a log and may be reworded. The code will not change.

{
  "error": {
    "code": "UPGRADE_REQUIRED",
    "message": "The developer API is part of the Business plan. Upgrade to Business to use it."
  }
}
CodeWhat it does
UNAUTHORIZED (401)The key is missing, mistyped or revoked.
UPGRADE_REQUIRED (403)The account behind the key is not on the Business plan.
INVALID_QUERY (400)A query parameter failed validation. The details field names the fields at fault.
INVALID_ID (400)The project id is not a well formed id.
NOT_FOUND (404)No visible project carries that id.
RATE_LIMITED (429)You went past your rate limit. Retry-After tells you how many seconds to wait.
INTERNAL_ERROR (500)Something failed on our side. Retrying is safe.

Rate limits

Each key may make 60 requests a minute. Calls for a single project also draw on an account wide budget of 600 an hour that is shared with the web app, so a busy afternoon inside the product counts toward it as well.

Going past a limit gives you a 429 carrying the RATE_LIMITED code. The response includes Retry-After in seconds, along with X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, so your client can back off without guessing.

Questions

Write to support@buildvue.co if you need a field the API does not return yet, or if you are planning something large enough that these limits get in your way.