Roofbird

Developers

Read your roofing leads from code, or from an AI agent

Roofbird scores every roof inside the area you draw and ranks them worst-roof-first. The API starts a scan from nothing more than a postcode, and hands you that ranked list so you can push it into your CRM, your dialer, your route planner, or an assistant that answers questions about it.

Two minutes to your first lead

  1. 1Create a key in the dashboard under Developer. It is read-only unless you explicitly allow writes.
  2. 2Send it as a bearer token. Every response tells you your remaining allowance.
# who am I, and how much headroom is left
curl https://dashboard.roofbird.ai/api/v1/me \
  -H "Authorization: Bearer rb_live_..."

# the leads, already ranked worst-roof-first
curl "https://dashboard.roofbird.ai/api/v1/areas/AREA_ID/leads?limit=25" \
  -H "Authorization: Bearer rb_live_..."

# only the doors worth walking today
curl "https://dashboard.roofbird.ai/api/v1/areas/AREA_ID/leads?tier=replace_now,worn" \
  -H "Authorization: Bearer rb_live_..."

Endpoints

POST /v1/scans

Get NEW leads. Send { postcode }, { lat, lon } or your own { polygon }. Needs a write-scoped key.

The only call that runs AI and the only one that spends quota. A 5-digit postcode is enough to start.

GET /v1/me

Confirms the key works and returns account totals plus your current rate-limit position.

Call it first. Cheapest way to verify auth and to learn how much headroom you have before you start pulling.

GET /v1/areas

Your service areas, newest first, each with a lead count and the number of candidate buildings inside the polygon.

The candidate count tells you how much of a neighbourhood the scan actually had to choose from.

GET /v1/areas/{id}/leads

The leads in an area, already ordered best-lead-first. Filter with ?tier=replace_now,worn or ?ranked_only=true.

The endpoint you will use most. Preserve the order you receive — it is the answer to which door to knock first.

GET /v1/leads/{id}

One lead in full: roof condition, visible signs, damage types, owner and property record, and a pitch line.

Everything you need to explain to a human why this roof is on the list.

GET/POST/DELETE /v1/webhooks

Register a URL to receive events. POST and DELETE need a write-scoped key.

So your system reacts when a scan finishes instead of polling for it.

Sort on the rank, not on the score

Each lead carries two numbers and they are not equivalent. rank.percentile is a comparative position: 0 is the worst roof in the area, which makes it the best lead. It comes from ranking the roofs against each other in a single pass, which is what makes it discriminate.

score.buy_probability is an absolute number assigned to one house viewed alone. Across every lead in the system it reads 54 about a third of the time. A list ordered by it is close to arbitrary, so we return the leads already in the right order and tell you to keep it.

rank.tier is the field to show a person: replace_now and worn are worth a door, midlife and recent are not. If a lead says “No trigger — nurture”, that is an honest answer rather than missing data. Most leads are nurture, and a crew sent to five intact roofs stops trusting the whole list.

Getting new leads: a postcode is enough

curl -X POST https://dashboard.roofbird.ai/api/v1/scans \
  -H "Authorization: Bearer rb_live_..." \
  -H "Content-Type: application/json" \
  -d '{"postcode": "32073"}'

You can also send { lat, lon } or your own { polygon } (up to 12 sq mi). It takes 90 to 180 seconds and returns how many leads it kept, how many roofs it assessed, and what quota you have left.

A postcode's bounding box is not the postcode. A single postcode can span 38 sq mi against a 12 sq mi scan limit, and its edges routinely fall in the next postcode or the next county. So the scan centres a legal box on the postcode and then tells you, in postcodes_found, where the leads actually landed — with a warning if fewer than half match what you asked for. Send an explicit polygon when you need exact coverage.

Do not retry a timed-out scan blindly. A second concurrent scan is refused with 409 for exactly that reason: the first one may still be running and you would pay twice. If the HTTP call times out, the scan.completed webhook is the reliable signal, and the leads are in the area either way.

Zero leads is a real answer. It usually means the roofs there are sound or recently replaced, and it returns 201 with an explanation rather than an error. A crew sent to five intact roofs stops trusting the list.

What a call costs

Reading is free of quota. Pulling leads through the API runs no AI. It does not touch your credit allowance or your balance. Re-reading a lead you already paid to have scanned costs you nothing.

Any call that makes the AI do work is deducted from your balance. A scan assesses roofs with a vision model, and every roof it assesses comes off your plan's monthly scan quota. Unlocking a homeowner's phone or email spends one of your included unlocks, with overage billed per unlock. Unlocking is dashboard-only on purpose, so an agent listing your leads cannot spend that budget by accident.

Rate limits, per key

Plan
Per minute
Per day
Free trial
30
1,000
Hunter · $199/mo
120
10,000
Hunter Pro · $499/mo
300
50,000

Both windows apply. Every response carries X-RateLimit-Remaining-Minute and X-RateLimit-Remaining-Day, so pace yourself rather than probing for a 429. A 429 returns the window that tripped and retry_after_sec.

Everything the CSV has, the API has

The lead export is 121 columns and the API returns all of them, grouped: the roof assessment and its visible signs, the estimated revenue band, the full parcel record (mortgage, tax, sale history, living area, beds and baths), the Street View read, the job-shaping detail that turns a score into a quote, your own tags and lead status, and provenance.

Homeowner contact details are included for leads you have already unlocked. contact.phones — each number carrying its dnc flag — contact.emails and the mailing address are returned whenever contact.unlocked is true. You paid for those, so reading them back costs nothing.

When a lead is still locked those fields are null. Unlocking spends one of your plan's credits — the full roof read and the owner's contact together — and can only be done in the dashboard — deliberately, so no script or agent can run up that bill on your behalf.

Webhooks

Register a URL and we POST to it when a scan finishes, so your system reacts instead of polling. A scan that found nothing still sends an event — an empty result is usually the thing you most need to know about.

Each request carries X-Roofbird-Signature: t=<unix>,v1=<hex>. Compute HMAC-SHA256 of t + "." + rawBody with your signing secret and compare it to v1. Reject anything where the timestamp is more than five minutes old: it sits inside the signed payload precisely so a captured request cannot be replayed at you later.

Delivery is at least once, so dedupe on the event id. Retries run at roughly 1 minute, 5 minutes, 25 minutes, 2 hours and 10 hours, then stop. An endpoint that fails twenty times in a row is disabled automatically and shown as such in your dashboard.

Building on this with an AI agent

Point your agent at dashboard.roofbird.ai/llms.txt and it will know how to rank these leads, which field to quote to a human, when not to manufacture urgency, and what an action will cost — without you explaining any of it. The OpenAPI 3.1 spec is served unauthenticated so a tool can read the contract before it holds a credential.