← all tools
Data & sourcing

Shovels

Find contractors by the work they are actually doing.

Cost per callBYOK · vendor billed
Your own keyrequired
CategoryData & sourcing

Shovels reads US building permits. A contractor pulling permits in a metro has live projects and, usually, a crew to hire. For construction and the trades this beats any job board, because the permit exists before the job ad does.

Shovels is the sourcing front end for trades recruiting. Contractors come out; enrichment turns them into contacts.

things you can ask for

  • Which electrical contractors are active in Phoenix, and what are their details per Prospeo?
  • Find roofing firms that pulled permits this quarter and file them in Loxo
  • Who is doing solar work in this county? Sanity-check them on Serper before I call
  • How big is this contractor's operation?

works well with Shovels

  • Prospeo

    contact details for the contractors the permits named

  • Serper

    a quick check that a local business is what it looks like

  • Aircall

    trades BD is phone-first, so log the calls

  • Loxo

    file the contractors as companies on your desk

Ask for the outcome and your agent composes the run itself. Or start from a skill, a whole pipeline it already knows end to end.

browse all skills →

What your agent reads before it touches Shovels

Every tool ships with a written playbook, and the agent loads it before the first call. Auth, rate limits, what each call costs, which actions need your approval, and the mistakes worth avoiding. It is the difference between an agent that knows the tool and one that guesses at it.

Read the Shovels docs →
Show the raw playbookwritten for the agent

HOW in lib/shovels.py. Paths from docs.shovels.ai (2026-06-01); index: reference/docs/shovels/raw/endpoints.md.

Auth & config

  • Base URL: https://api.shovels.ai/v2 · Auth: header X-API-Key (env SHOVELS_API_KEY; never hardcode).
  • Pagination: cursor — size (≤100, default 50) + cursornext_cursor; iter_permits/iter_contractors auto-walk it.
  • Credit-meteredget_usage() to check (safe read-only pilot).

Core workflow (geo_id first)

Everything keys off a geo_id. Resolve the place first — search_addresses / search_cities / search_zipcodes / search_counties / search_jurisdictions / search_states (they return geo_ids) — then pass geo_id to search_permits / search_contractors. Both searches REQUIRE geo_id + permit_from + permit_to (YYYY-MM-DD).

Operations

Geo: search_addresses/cities/counties/jurisdictions/states/zipcodes. Permits: search_permits/iter_permits, get_permit. Contractors: search_contractors/iter_contractors, get_contractor, get_contractor_employees, get_contractor_permits, get_contractor_metrics. Residents/metrics: get_address_residents, get_address_metrics_current/monthly, plus city/county/jurisdiction details + metrics. Lists/meta: list_tags, list_zip_codes, data_release_date, get_usage. CONFIRMED: the two searches + usage; per-record detail/metrics sub-paths are pattern-inferred — verify on first pilot, request() covers any that differ.

Recruitment use (why it's here)

Construction/trades recruiting: find active contractors pulling permits in a metro + date window (search_contractors by trade) → they're the hiring companies; get_contractor_employees for contacts → enrich → outreach/ATS. Or search_permits for project activity as a hiring signal. job value is in cents.

Handoff

Sourcing/signal layer (construction): Shovels finds active contractors/permits → qualify vs ICP → enrich contacts (waterfall) → sequence / push to ATS. Pairs with the standard pipeline.

Callable surface — lib/shovels.py

Import: from lib.shovels import Shovels → instantiate Shovels() (reads key from env). Base: https://api.shovels.ai/v2. Generic passthrough: request(method, path, *, params, json).

  • data_release_date() -> Any — GET /meta/release — latest data release date (inferred sub-path).
  • get_address_metrics_current(geo_id: str, **params) -> Any — GET /addresses/{geo_id}/metrics/current (inferred).
  • get_address_metrics_monthly(geo_id: str, **params) -> Any — GET /addresses/{geo_id}/metrics/monthly (inferred).
  • get_address_residents(geo_id: str, **params) -> Any — GET /addresses/{geo_id}/residents — residents at an address (homeowner field) (inferred).
  • get_city(geo_id: str) -> Any — GET /cities/{geo_id} — city details (inferred).
  • get_city_metrics_current(geo_id: str, **params) -> Any
  • get_city_metrics_monthly(geo_id: str, **params) -> Any
  • get_contractor(contractor_id: str) -> Any — GET /contractors/{id} — contractor profile (inferred path; verify).
  • get_contractor_employees(contractor_id: str, **params) -> Any — GET /contractors/{id}/employees — employee/contact data (inferred).
  • get_contractor_metrics(contractor_id: str, **params) -> Any — GET /contractors/{id}/metrics — filtered metrics for a contractor (inferred).
  • get_contractor_permits(contractor_id: str, **params) -> Any — GET /contractors/{id}/permits — permits a contractor worked on (inferred). May need permit_from/to.
  • get_county(geo_id: str) -> Any
  • get_county_metrics_current(geo_id: str, **params) -> Any
  • get_county_metrics_monthly(geo_id: str, **params) -> Any
  • get_jurisdiction(geo_id: str) -> Any
  • get_jurisdiction_metrics_current(geo_id: str, **params) -> Any
  • get_jurisdiction_metrics_monthly(geo_id: str, **params) -> Any
  • get_permit(permit_id: str) -> Any — GET /permits/{id} — a single permit by id.
  • get_usage() -> Any — GET /usage — API usage / credits (safe read-only pilot).
  • iter_contractors(*, geo_id: str, permit_from: str, permit_to: str, **params) -> Iterator[dict]
  • iter_permits(*, geo_id: str, permit_from: str, permit_to: str, **params) -> Iterator[dict] — Auto-paginate /permits/search across all cursor pages.
  • list_tags(**params) -> Any — GET /lists/tags — all available permit tags (inferred sub-path).
  • list_zip_codes(**params) -> Any — GET /lists/zipcodes — all available zip codes (inferred sub-path).
  • search_addresses(**params) -> Any — GET /addresses/search — resolve an address → geo_id (+ details).
  • search_cities(**params) -> Any — GET /cities/search — resolve cities → geo_ids.
  • search_contractors(*, geo_id: str, permit_from: str, permit_to: str, **params) -> Any — GET /contractors/search — REQUIRES geo_id + permit_from + permit_to. Ordered by most-recent
  • search_counties(**params) -> Any — GET /counties/search.
  • search_jurisdictions(**params) -> Any — GET /jurisdictions/search.
  • search_permits(*, geo_id: str, permit_from: str, permit_to: str, **params) -> Any — GET /permits/search — REQUIRES geo_id + permit_from + permit_to (YYYY-MM-DD). One page.
  • search_states(**params) -> Any — GET /states/search.
  • search_zipcodes(**params) -> Any — GET /zipcodes/search.