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.
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 →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 →HOW in lib/shovels.py. Paths from docs.shovels.ai (2026-06-01); index: reference/docs/shovels/raw/endpoints.md.
https://api.shovels.ai/v2 · Auth: header X-API-Key (env SHOVELS_API_KEY; never hardcode).size (≤100, default 50) + cursor → next_cursor; iter_permits/iter_contractors auto-walk it.get_usage() to check (safe read-only pilot).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).
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.
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.
Sourcing/signal layer (construction): Shovels finds active contractors/permits → qualify vs ICP → enrich contacts (waterfall) → sequence / push to ATS. Pairs with the standard pipeline.
lib/shovels.pyImport: 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) -> Anyget_city_metrics_monthly(geo_id: str, **params) -> Anyget_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) -> Anyget_county_metrics_current(geo_id: str, **params) -> Anyget_county_metrics_monthly(geo_id: str, **params) -> Anyget_jurisdiction(geo_id: str) -> Anyget_jurisdiction_metrics_current(geo_id: str, **params) -> Anyget_jurisdiction_metrics_monthly(geo_id: str, **params) -> Anyget_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-recentsearch_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.