# Get Address Metrics Current
Source: https://docs.shovels.ai/api-reference/addresses/get-address-metrics-current
https://api.shovels.ai/spec/v2/openapi.production.yaml get /addresses/{geo_id}/metrics/current
Returns current address metrics.
# Get Address Metrics Monthly
Source: https://docs.shovels.ai/api-reference/addresses/get-address-metrics-monthly
https://api.shovels.ai/spec/v2/openapi.production.yaml get /addresses/{geo_id}/metrics/monthly
Returns monthly address metrics.
# Get Residents
Source: https://docs.shovels.ai/api-reference/addresses/get-residents
https://api.shovels.ai/spec/v2/openapi.production.yaml get /addresses/{geo_id}/residents
Return residents for a given address geo ID. Results are paginated using cursor-based pagination.
# Search Addresses
Source: https://docs.shovels.ai/api-reference/addresses/search-addresses
https://api.shovels.ai/spec/v2/openapi.production.yaml get /addresses/search
Searches addresses by their text fields (street number, street, city, state, and ZIP code) using prefix, full-text, and fuzzy matching.
# Get City Details
Source: https://docs.shovels.ai/api-reference/cities/get-city-details
https://api.shovels.ai/spec/v2/openapi.production.yaml get /cities
Return city details and related location hierarchy.
# Get City Metrics Current
Source: https://docs.shovels.ai/api-reference/cities/get-city-metrics-current
https://api.shovels.ai/spec/v2/openapi.production.yaml get /cities/{geo_id}/metrics/current
Returns current city metrics.
# Get City Metrics Monthly
Source: https://docs.shovels.ai/api-reference/cities/get-city-metrics-monthly
https://api.shovels.ai/spec/v2/openapi.production.yaml get /cities/{geo_id}/metrics/monthly
Returns monthly city metrics.
# Search Cities
Source: https://docs.shovels.ai/api-reference/cities/search-cities
https://api.shovels.ai/spec/v2/openapi.production.yaml get /cities/search
Searches for cities based on the provided search term.
# Get Contractor Employees
Source: https://docs.shovels.ai/api-reference/contractors/get-contractor-employees
https://api.shovels.ai/spec/v2/openapi.production.yaml get /contractors/{id}/employees
Returns a paginated list of employees for a specific contractor.
# Get Contractors By Id
Source: https://docs.shovels.ai/api-reference/contractors/get-contractors-by-id
https://api.shovels.ai/spec/v2/openapi.production.yaml get /contractors
Returns contractors by their IDs. Multiple `id` query parameters can be provided in the same API call
# Get Filtered Metrics By Contractor Id
Source: https://docs.shovels.ai/api-reference/contractors/get-filtered-metrics-by-contractor-id
https://api.shovels.ai/spec/v2/openapi.production.yaml get /contractors/{id}/metrics
Returns contractor monthly metrics filtered by contractor ID, property type, and tags. Metrics include permit count, average duration, and average pass rate.
# Get Permits By Contractor Id
Source: https://docs.shovels.ai/api-reference/contractors/get-permits-by-contractor-id
https://api.shovels.ai/spec/v2/openapi.production.yaml get /contractors/{id}/permits
Retrieves all permits associated with a single contractor.
# Search Contractors
Source: https://docs.shovels.ai/api-reference/contractors/search-contractors
https://api.shovels.ai/spec/v2/openapi.production.yaml get /contractors/search
Returns contractors doing work within the given location area filtered by type of work. Contractors must meet all specified parameters, with multiple parameters treated as AND queries. Use contractor_classification_derived to filter by derived classifications (ALL specified values required). The permit_q parameter uses full-text search with English stemming (e.g., 'installing' matches 'install'). Multi-word queries use AND semantics (e.g., 'solar panel' finds permits mentioning both words). **NOTE:** Contractors are ordered by the start date of the most recent permit on which they worked.
# Get County Details
Source: https://docs.shovels.ai/api-reference/counties/get-county-details
https://api.shovels.ai/spec/v2/openapi.production.yaml get /counties
Return county details and related location hierarchy.
# Get County Metrics Current
Source: https://docs.shovels.ai/api-reference/counties/get-county-metrics-current
https://api.shovels.ai/spec/v2/openapi.production.yaml get /counties/{geo_id}/metrics/current
Returns current county metrics.
# Get County Metrics Monthly
Source: https://docs.shovels.ai/api-reference/counties/get-county-metrics-monthly
https://api.shovels.ai/spec/v2/openapi.production.yaml get /counties/{geo_id}/metrics/monthly
Returns monthly county metrics.
# Search Counties
Source: https://docs.shovels.ai/api-reference/counties/search-counties
https://api.shovels.ai/spec/v2/openapi.production.yaml get /counties/search
Searches for counties based on the provided search term.
# Get Decisions By Id
Source: https://docs.shovels.ai/api-reference/decisions/get-decisions-by-id
https://api.shovels.ai/spec/v2/openapi.production.yaml get /decisions
Returns the requested decisions by ID. Unknown IDs are silently omitted (no per-ID 404). Up to 50 IDs per request.
# Search Decisions
Source: https://docs.shovels.ai/api-reference/decisions/search-decisions
https://api.shovels.ai/spec/v2/openapi.production.yaml get /decisions/search
Returns a list of zoning / land-use decisions matching the supplied filters. Decisions should meet ALL parameter filters (AND logic). Date range (`decision_from`/`decision_to`) and `geo_id` are required.
# Get Jurisdiction Details
Source: https://docs.shovels.ai/api-reference/jurisdictions/get-jurisdiction-details
https://api.shovels.ai/spec/v2/openapi.production.yaml get /jurisdictions
Return jurisdiction details and related location hierarchy.
# Get Jurisdiction Metrics Current
Source: https://docs.shovels.ai/api-reference/jurisdictions/get-jurisdiction-metrics-current
https://api.shovels.ai/spec/v2/openapi.production.yaml get /jurisdictions/{geo_id}/metrics/current
Returns current jurisdiction metrics.
# Get Jurisdiction Metrics Monthly
Source: https://docs.shovels.ai/api-reference/jurisdictions/get-jurisdiction-metrics-monthly
https://api.shovels.ai/spec/v2/openapi.production.yaml get /jurisdictions/{geo_id}/metrics/monthly
Returns monthly jurisdiction metrics.
# Search Jurisdictions
Source: https://docs.shovels.ai/api-reference/jurisdictions/search-jurisdictions
https://api.shovels.ai/spec/v2/openapi.production.yaml get /jurisdictions/search
Searches for jurisdictions based on the provided search term.
# Get All Available Tags
Source: https://docs.shovels.ai/api-reference/lists/get-all-available-tags
https://api.shovels.ai/spec/v2/openapi.production.yaml get /list/tags
Returns all available permit tags.
# Get All Available Zip Codes
Source: https://docs.shovels.ai/api-reference/lists/get-all-available-zip-codes
https://api.shovels.ai/spec/v2/openapi.production.yaml get /list/zip
Returns all available ZIP codes for which we have permit and contractor data.
# Get Coverage
Source: https://docs.shovels.ai/api-reference/meta/get-coverage
https://api.shovels.ai/spec/v2/openapi.production.yaml get /meta/coverage
Returns per-field data coverage tiers (partial or missing) for the specified geography and date range. Coverage is bucketed by each permit's start_date year; every calendar year the range touches is counted in full. For a whole-calendar-year window the totals equal a start_date-filtered permit search over the same window at 5-digit ZIP granularity. A sub-year window still counts its touched years in full, so it can report a broader population than the search. ZIP coverage is bucketed at the 5-digit level, so a search narrowed to a ZIP+4 extension returns a subset of that population. Fields classified as reliable (fill rate >= 80%) are omitted. Empty windows return every tracked field as missing with permits_total=0.
# Get Data Release Date
Source: https://docs.shovels.ai/api-reference/meta/get-data-release-date
https://api.shovels.ai/spec/v2/openapi.production.yaml get /meta/release
Returns the release date of the current data served by the API.
# Get Permits By Id
Source: https://docs.shovels.ai/api-reference/permits/get-permits-by-id
https://api.shovels.ai/spec/v2/openapi.production.yaml get /permits
Returns a list of permits records for given IDs. Results are paginated using cursor-based pagination for efficient navigation.
# Search Permits
Source: https://docs.shovels.ai/api-reference/permits/search-permits
https://api.shovels.ai/spec/v2/openapi.production.yaml get /permits/search
Returns a list of matching permits records. Permits should meet ALL of the parameter filters. Multiple parameters are treated as AND queries. Use contractor_classification_derived to filter by contractor's derived classifications (ALL specified values required).
# Get Properties By Id
Source: https://docs.shovels.ai/api-reference/properties/get-properties-by-id
https://api.shovels.ai/spec/v2/openapi.production.yaml get /properties
**Beta.** Query parameters, response fields, and the absence-trust surface may still change in response to how the endpoint is used in practice. Treat the shape as unstable while it is in beta. Returns properties by their address id. Provide up to 50 `id` query parameters in one call; rows come back in request order and unknown ids are omitted (no per-id 404). A non-address geolocation id (city, county, or jurisdiction) is rejected.
# Search Properties
Source: https://docs.shovels.ai/api-reference/properties/search-properties
https://api.shovels.ai/spec/v2/openapi.production.yaml get /properties/search
**Beta.** Query parameters, response fields, and the absence-trust surface may still change in response to how the endpoint is used in practice. Treat the shape as unstable while it is in beta. Returns properties within a geo scope (state, county, city, ZIP, ZIP+4, or address) and/or owned by named `legal_owner`s, most recently permitted first. At least one of `geo_id` and `legal_owner` is required; a `legal_owner` search needs no geo scope. Pass `cursor` from a page's `next_cursor` to page through results. Filter by `permit_tags` (presence and `-`-prefixed exclusion), `permit_status`, `permit_from`, and `property_type`. Properties with no attributed type are excluded; property attributes are available for 70.1% of properties. An absence filter (a `-`-prefixed tag) returns properties with no resolved permit of the excluded type in covered, arrived data: a permit that never linked to an address, or one that has not yet arrived within a jurisdiction's ingestion lag, is not counted against the claim. Every absence-class row carries a `trust` object stating three honesty terms — the jurisdiction's permit `coverage_tier`, its `unresolved_rate` (the share of its permits that never linked to an address), and the row's `data_horizon` — plus `horizon_basis` (whether that horizon is measured, pooled, or a state prior) and `footprint_basis` (whether suppression could resolve the row's geo); the response `trust_summary` row-weights them across the page. A presence-only search omits both. Freshness: /properties is served from a whole-table rebuild swapped in by a blue/green cutover — the rebuild is the unit of refresh, and the absence-trust metadata is rebuilt from the same data it describes. An absence claim's staleness bound is the rebuild cadence plus each jurisdiction's permit-ingestion lag: permits reach the data a median 84 days (p90 188 days) after their start_date, so a recent 'no permit since D' stays under-observed until that lag elapses. Each row's `data_horizon` is the per-row handle on that bound — the most recent start_date past which 'no X since D' is under-observed for that row. Not supported, each for one reason: `permit_to` or any upper date bound — a max-date rollup cannot bound a closed date window without dropping addresses whose latest permit postdates it (use /permits/search); 'before D' absence — a max-date cannot tell a history that also holds later permits from one that does not; closed-window absence within [D1, D2] — only 'ever' and 'since D' are expressible on max-date maps; 'has X since D and no Y since D' in one query — a mixed query binds an exclusion to 'ever', so per-predicate dates cannot be composed; and jurisdiction geo_ids as a scope.
# Search States
Source: https://docs.shovels.ai/api-reference/states/search-states
https://api.shovels.ai/spec/v2/openapi.production.yaml get /states/search
Searches for US states based on the provided search term.
# Get Usage
Source: https://docs.shovels.ai/api-reference/usage/get-usage
https://api.shovels.ai/spec/v2/openapi.production.yaml get /usage
Get your current credit usage for the rolling 30-day period.
# Search Zipcodes
Source: https://docs.shovels.ai/api-reference/zipcodes/search-zipcodes
https://api.shovels.ai/spec/v2/openapi.production.yaml get /zipcodes/search
Searches for zipcodes based on the provided search term.
# About the API Reference
Source: https://docs.shovels.ai/docs/api-reference-about
How the Shovels API Reference is generated, what it covers, and where to report an error.
Every page in the **API Reference** tab is generated directly from the Shovels OpenAPI
specification — the same specification the live API is built against. Endpoints, parameters,
field names, defaults, and response schemas all come from that one source, so the reference
cannot drift out of step with the API itself.
## What this means for you
* **The reference is always current.** When an endpoint gains a parameter or a default changes,
the documentation updates with it. There is no separate step where someone has to remember to
write it down.
* **Examples run against the real API.** The interactive playground on each endpoint page calls
`https://api.shovels.ai/v2` using your own API key.
* **Prose guides are written by hand.** Everything outside the API Reference tab — quickstarts,
tutorials, the data dictionaries, troubleshooting — is written and maintained by our team.
Those pages explain *how* and *why*; the API Reference documents *what*.
## Found an error?
If an endpoint page shows a wrong default, a stale field, or a response that doesn't match what
the API actually returns, email [support@shovels.ai](mailto:support@shovels.ai) with the endpoint
and what you observed.
Corrections to the API Reference are made in the OpenAPI specification rather than on the
documentation site, so these fixes ship with the API. Please include the endpoint path and
method — it's the fastest way for us to trace the field back to the specification.
# API
Source: https://docs.shovels.ai/docs/data-dictionary-api
All fields, parameters, and objects available in the **Shovels API** schema.
Monetary fields (`job_value`, `fees`, `total_job_value`, `avg_job_value`, `property_assess_market_value`, `assess_market_value`) are integers expressed **in cents** — divide by 100 for the dollar amount (e.g. `2500000` = \$25,000).
| Field Name | Data Type | Description | Example |
| :-------------------------------- | :-------- | :-------------------------------------------------------------------------------- | :-------------------------------- |
| id | `string` | The permit identifier | "caf3b9d5ce317d53" |
| number | `string` | The permit number provided by the jurisdiction — Source: Permit Jurisdiction | "No-15837" |
| jurisdiction | `string` | The jurisdiction where the permit was filed — Source: Permit Jurisdiction | "City of Oakland" |
| description | `string` | The free-form description of the work on the permit — Source: Permit Jurisdiction | "Solar panel installation" |
| job\_value | `integer` | The reported job value on the permit, in cents — Source: Permit Jurisdiction | 2500000 |
| type | `string` | The type of permit — Source: Permit Jurisdiction | "Residential accessory structure" |
| subtype | `string` | The subtype of permit — Source: Permit Jurisdiction | "garage" |
| fees | `integer` | The fees charged by the jurisdiction, in cents — Source: Permit Jurisdiction | 15000 |
| status | `string` | The status of the permit | "active" |
| file\_date | `string` | The date the permit was filed (YYYY-MM-DD) — Source: Permit Jurisdiction | "2023-01-01" |
| issue\_date | `string` | The date the permit was issued (YYYY-MM-DD) — Source: Permit Jurisdiction | "2023-01-31" |
| final\_date | `string` | The date the permit was finalized (YYYY-MM-DD) — Source: Permit Jurisdiction | "2023-06-01" |
| start\_date | `string` | The earliest of `file_date`, `issue_date`, and `final_date` | "2023-01-01" |
| end\_date | `string` | The latest of `file_date`, `issue_date`, and `final_date` | "2023-06-01" |
| total\_duration | `integer` | Number of days from `start_date` to `end_date` | 151 |
| construction\_duration | `integer` | Number of days it took to complete the project | 120 |
| approval\_duration | `integer` | Number of days it took to approve the permit | 30 |
| inspection\_pass\_rate | `integer` | The inspection pass rate as a percentage (0-100) | 95 |
| contractor\_id | `string` | The unique ID of the contractor associated with the permit | "c452b7d5cf317a76" |
| tags | `array` | A list of tags associated with the permit | `["solar", "residential"]` |
| address | `object` | The property's address | |
| geo\_ids | `object` | Geographic identifiers | |
| property\_census\_tract | `string` | The property's census tract | "06075000100" |
| property\_congressional\_district | `string` | The property's congressional district | "11" |
| property\_type | `string` | The property's type | "Residential" |
| property\_type\_detail | `string` | The property's sub-classification | "Single Family Home" |
| property\_legal\_owner | `string` | The property's legal owner | "Robert Johnson" |
| property\_owner\_type | `string` | The property's owner classification | "individual" |
| property\_lot\_size | `integer` | The property's lot size (in square feet) | 5000 |
| property\_building\_area | `integer` | The property's building area (in square feet) | 1250 |
| property\_story\_count | `integer` | The property's number of stories | 2 |
| property\_unit\_count | `integer` | The property's number of units | 1 |
| property\_year\_built | `integer` | The property's year built | 1978 |
| property\_assess\_market\_value | `integer` | The property's assessed market value, in cents | 75000000 |
Served by the `/properties` endpoints (beta). One record per property, with its permit history summarized on the record. The `trust` object and its fields are present only on absence-class search responses (a `-`-prefixed tag in `permit_tags`).
| Field Name | Data Type | Description | Example |
| :----------------------------- | :-------- | :------------------------------------------------------------------------------------- | :------------------------------ |
| id | `string` | The property's ID (its address geolocation ID) | "cmVzMTIzNDU2" |
| street\_no | `string` | Street number of the address | "123" |
| street | `string` | Street name of the address | "Main St" |
| city | `string` | City of the address | "San Francisco" |
| city\_id | `string` | The city's geolocation ID | "Y2l0eTk4NzY" |
| zip\_code | `string` | ZIP code of the address | "94103" |
| zip\_code\_ext | `string` | ZIP+4 extension of the address | "1234" |
| county | `string` | County of the address | "San Francisco County" |
| county\_id | `string` | The county's geolocation ID | "Y291bnR5NTQz" |
| state | `string` | State of the address | "CA" |
| lat | `number` | Latitude of the address | 37.7749 |
| long | `number` | Longitude of the address | -122.4194 |
| permit\_count | `integer` | Count of permits linked to the property | 12 |
| untagged\_permit\_count | `integer` | Count of the property's permits carrying no canonical tag | 2 |
| total\_job\_value | `integer` | Sum of permit job values, in cents | 250000000 |
| contractor\_count | `integer` | Distinct contractors across the property's permits | 5 |
| tags | `array` | Distinct canonical tags present on the property's permits | `["solar", "roofing"]` |
| statuses | `array` | Distinct permit statuses present, including `unknown` | `["final", "active"]` |
| tag\_status\_pairs | `array` | Distinct tag:status pairs present | `["solar:final"]` |
| tag\_tally | `object` | Map of tag → permit count | `{"solar": 2}` |
| last\_date\_by\_tag | `object` | Map of tag → latest permit start\_date | `{"solar": "2023-06-01"}` |
| last\_date\_by\_status | `object` | Map of status → latest permit start\_date | `{"final": "2023-06-01"}` |
| last\_date\_by\_pair | `object` | Map of tag:status → latest permit start\_date | `{"solar:final": "2023-06-01"}` |
| last\_unfinaled\_date\_by\_tag | `object` | Map of tag → latest unfinaled permit start\_date | `{"roofing": "2024-02-15"}` |
| last\_permit\_date | `string` | Latest permit start\_date across all the property's permits; null when never permitted | "2024-02-15" |
| apn | `string` | Assessor parcel number (returned for mapping; not searchable) | "1234-056-789" |
| property\_type | `string` | The property's type | "residential" |
| property\_type\_detail | `string` | The property's sub-classification | "Single Family Home" |
| year\_built | `integer` | Year the property was built | 1978 |
| lot\_size | `integer` | Lot size (in square feet) | 5000 |
| building\_area | `integer` | Building area (in square feet) | 1250 |
| story\_count | `integer` | Number of stories | 2 |
| unit\_count | `integer` | Number of units | 1 |
| assess\_market\_value | `integer` | Assessed market value, in cents | 75000000 |
| legal\_owner | `string` | The property's legal owner | "Robert Johnson" |
| owner\_type | `string` | The property owner's classification | "individual" |
| trust | `object` | Per-row absence-confidence fields; absence-class responses only | |
| trust.coverage\_tier | `string` | Permit coverage bucket for the row's jurisdiction: high, medium, low | "high" |
| trust.unresolved\_rate | `number` | Share of the jurisdiction's permits of the tag that never linked to an address (0–1) | 0.04 |
| trust.data\_horizon | `string` | Most recent date past which "no permit since D" is under-observed (YYYY-MM-DD) | "2026-03-15" |
| trust.horizon\_basis | `string` | How the horizon was estimated: measured, pooled, prior | "measured" |
| trust.footprint\_basis | `string` | Whether coverage suppression could resolve the row's geography: matched, unknown | "matched" |
| trust.flags | `array` | Row-level caveats | `["since_d_beyond_horizon"]` |
Absence-class search responses also carry a page-level `trust_summary` object:
| Field Name | Data Type | Description | Example |
| :------------------------------ | :-------- | :------------------------------------------------------------------------------------------- | :------ |
| expected\_miss\_rate | `number` | Estimated probability that a returned "no permit" answer is wrong due to ingestion lag (0–1) | 0.02 |
| rows\_flagged | `integer` | Number of rows on the page carrying a trust flag | 3 |
| row\_weighted\_unresolved\_rate | `number` | Row-weighted mean unresolved\_rate across the page (0–1) | 0.05 |
| suppressed\_scopes | `integer` | Number of coverage scopes removed from the result | 1 |
| Field Name | Data Type | Description | Example |
| :-------------------------- | :-------- | :---------------------------------------------------------------------------------------------------------- | :------------------------------------------------ |
| id | `string` | The contractor ID | "c452b7d5cf317a76" |
| license | `string` | The contractor license number — Source: Permit Jurisdiction | "123456" |
| name | `string` | The contractor name — Source: Permit Jurisdiction | "John Builder" |
| business\_name | `string` | The contractor's business name — Source: Permit Jurisdiction | "Builder Co" |
| business\_type | `string` | The type of business: JointVenture, Corporation, Partnership, Limited Liability, Sole Owner | "Corporation" |
| classification | `string` | The contractor's classification/certification | "B - General Building" |
| classification\_derived | `array` | Array of derived contractor classifications | `["General Building"]` |
| license\_issue\_date | `string` | The license issue date (YYYY-MM-DD) — Source: Permit Jurisdiction | "2015-04-01" |
| license\_exp\_date | `string` | The license expiration date (YYYY-MM-DD) — Source: Permit Jurisdiction | "2025-04-01" |
| license\_inact\_date | `string` | Date the license became inactive (YYYY-MM-DD) — Source: Permit Jurisdiction | "2020-01-01" |
| license\_act\_date | `string` | Date the license became active (YYYY-MM-DD) — Source: Permit Jurisdiction | "2015-04-01" |
| primary\_phone | `string` | The contractor's primary phone number | "123-456-7890" |
| primary\_email | `string` | The contractor's primary email | "[info@builderco.com](mailto:info@builderco.com)" |
| phone | `string` | The contractor's phone number(s) | "123-456-7890" |
| email | `string` | The contractor's email(s) | "[info@builderco.com](mailto:info@builderco.com)" |
| website | `string` | The contractor's website | "builderco.com" |
| dba | `string` | Doing Business As name for the contractor | "Builder Co" |
| sic | `string` | Standard Industrial Classification (SIC) code | "1521" |
| naics | `string` | North American Industry Classification System (NAICS) code | "236118" |
| linkedin\_url | `string` | LinkedIn URL of the contractor | "linkedin.com/company/builderco" |
| revenue | `string` | Annual revenue of the contractor's business | "\$1M-\$5M" |
| employee\_count | `string` | Number of employees working for the contractor | "10-50" |
| primary\_industry | `string` | Primary industry in which the contractor operates | "Construction" |
| review\_count | `integer` | Number of reviews the contractor has received | 42 |
| rating | `number` | Rating of the contractor based on reviews | 4.7 |
| status\_tally | `object` | Summary of status counts for the contractor's work (active, final, unknown, inactive, in\_review) | `{"active": 5, "final": 10}` |
| tag\_tally | `object` | Summary of tag counts for the contractor's work | `{"solar": 3}` |
| permit\_count | `integer` | The total number of permits | 100 |
| avg\_job\_value | `integer` | The average job value of all permits, in cents | 2500000 |
| total\_job\_value | `integer` | The total job value of all permits, in cents | 250000000 |
| avg\_construction\_duration | `integer` | The average construction duration in days | 120 |
| avg\_inspection\_pass\_rate | `integer` | The average inspection pass rate as a percentage (0-100) | 95 |
| first\_seen\_date | `string` | Date the contractor was first seen in the system (YYYY-MM-DD) | "2018-06-15" |
| address | `object` | The contractor's address (street\_no, street, city, jurisdiction, zip\_code, zip\_code\_ext, state, latlng) | |
| Field Name | Data Type | Description | Example |
| :--------------- | :-------- | :------------------------------------------- | :---------------------------------------------------- |
| name | `string` | Name of the resident | "Jane Doe" |
| personal\_emails | `string` | Email(s) of the resident | "[jane.doe@example.com](mailto:jane.doe@example.com)" |
| phone | `string` | Phone number of the resident | "123-456-7890" |
| linkedin\_url | `string` | LinkedIn URL of the resident | "linkedin.com/in/janedoe" |
| net\_worth | `string` | Net worth range of the resident | "1,000,000-2,499,999" |
| income\_range | `string` | Income range of the resident | "150,000-199,999" |
| is\_homeowner | `boolean` | Whether the resident is a homeowner | true |
| street\_no | `string` | Street number of the resident's address | "123" |
| street | `string` | Street name of the resident's address | "Main St" |
| city | `string` | City of the resident's address | "Anytown" |
| state | `string` | State of the resident's address | "CA" |
| zip\_code | `string` | Zip code of the resident's address | "12345" |
| zip\_code\_ext | `string` | Zip code extension of the resident's address | "6789" |
Metrics are returned in **current** and **monthly** variants across geographies (address, city, county, jurisdiction, state) and contractors. Fields common to the metrics schemas are listed below; the notes column indicates where a field is variant-specific.
| Field Name | Data Type | Description | Example |
| :-------------------------- | :-------- | :---------------------------------------------------------------------------------------------------- | :------------ |
| geo\_id | `string` | Unique identifier for the entity | "CA" |
| tag | `string` | Specific tag or category for the metrics | "solar" |
| permit\_count | `integer` | Total number of permits issued | 100 |
| permit\_active\_count | `integer` | Total number of permits in active status — Current metrics only | 50 |
| permit\_in\_review\_count | `integer` | Total number of permits in review status — Current metrics only | 10 |
| contractor\_count | `integer` | Total number of unique contractors | 25 |
| avg\_construction\_duration | `integer` | Average duration of construction projects in days | 120 |
| avg\_approval\_duration | `integer` | Average duration of permit approval process in days | 30 |
| total\_job\_value | `integer` | Total value of all jobs/permits, in cents | 250000000 |
| avg\_inspection\_pass\_rate | `integer` | Average inspection pass rate as a percentage (0-100) | 95 |
| property\_type | `string` | Type of property (e.g. residential, commercial) — City/county/jurisdiction metrics only (not address) | "residential" |
| date | `string` | The month for which the metrics are calculated (YYYY-MM-DD) — Monthly metrics only | "2024-01-01" |
| Field Name | Data Type | Description | Example |
| :--------- | :-------- | :---------------------------- | :------------------- |
| state | `string` | The state of the property. | "CA" |
| county | `string` | The county of the property. | "Santa Clara County" |
| city | `string` | The city of the property. | "San Jose" |
| zip\_code | `string` | The ZIP code of the property. | "95112" |
# Enterprise Data License
Source: https://docs.shovels.ai/docs/data-dictionary-edl
The exhaustive list of all fields in the Shovels platform, sorted by table.
| Field Name | Data Type | Description | Example |
| :-------------------------------- | :-------- | :----------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- |
| id | `string` | The Shovels ID for the permit. | "caf3b9d5ce317d53" |
| permit\_number | `string` | The jurisdiction permit number — Source: Permit Jurisdiction | "RE2303928" |
| apn | `string` | Parcel Number | "3209567" |
| county\_fips | `string` | Federal FIPS code for the county — Source: Gov (Fed) | "06193" |
| state\_fips | `string` | Federal FIPS code for the state — Source: Gov (Fed) | "06" |
| cbsa | `string` | Core Based Statistical Area — Source: Gov (Fed) | "San Francisco-Oakland-Berkeley, CA" |
| cbsa\_fips | `string` | Core Based Statistical Area FIPS code — Source: Gov (Fed) | "41869" |
| status | `string` | The status of the permit (active, in\_review, in\_active, or final) | "active" |
| start\_date | `date` | The earliest date on record for the permit | "2023-01-01" |
| end\_date | `date` | The last date on record for the permit | "2024-12-31" |
| issue\_date | `date` | The date the permit was issued — Source: Permit Jurisdiction | "2023-01-31" |
| file\_date | `date` | The date the permit was filed with the permit office — Source: Permit Jurisdiction | "2023-01-01" |
| final\_date | `date` | The date the permit (and underlying project) was completed — Source: Permit Jurisdiction | "2024-12-31" |
| type | `string` | The type of permit (or project) as determined by the jurisdiction — Source: Permit Jurisdiction | "Residential accessory structure" |
| subtype | `string` | The sub-type of permit (or project) as determined by the jurisdiction — Source: Permit Jurisdiction | "garage" |
| approval\_duration | `integer` | The number of days it took to approve the permit | "30" |
| construction\_duration | `integer` | The number of days between permit approval and completion | "700" |
| total\_duration | `integer` | The total number of days between permit filing and completion | "730" |
| fees | `decimal` | The total reported fees paid for the permit — Source: Permit Jurisdiction | "1000.00" |
| job\_value | `decimal` | The total reported job value, as self-reported by the applicant — Source: Permit Jurisdiction | "100000.00" |
| inspection\_pass\_rate | `double` | The ratio of passed inspections against total inspections for the permit | "50(%)" |
| inspection\_passed | `boolean` | `TRUE` if the permit passed ANY inspection | "TRUE" |
| description | `string` | The long-form description of the permit, as reported by the jurisdiction — Source: Permit Jurisdiction | "single car garage" |
| owner\_name | `string` | The name of the property owner — Source: Permit Jurisdiction | "John Doe" |
| owner\_street | `string` | The street address of the property owner — Source: Permit Jurisdiction | "123 Main St" |
| owner\_city | `string` | The city of the property owner — Source: Permit Jurisdiction | "San Francisco" |
| owner\_state | `string` | The state of the property owner — Source: Permit Jurisdiction | "CA" |
| owner\_zipcode | `string` | The zipcode of the property owner — Source: Permit Jurisdiction | "94101" |
| owner\_email | `string` | The email address of the property owner — Source: Permit Jurisdiction | "[john.doe@example.com](mailto:john.doe@example.com)" |
| owner\_phone | `string` | The phone number of the property owner — Source: Permit Jurisdiction | "123-456-7890" |
| applicant\_name | `string` | The name of the permit applicant — Source: Permit Jurisdiction | "John Smith" |
| applicant\_street | `string` | The street address of the permit applicant — Source: Permit Jurisdiction | "789 A St" |
| applicant\_city | `string` | The city of the permit applicant — Source: Permit Jurisdiction | "San Francisco" |
| applicant\_state | `string` | The state of the permit applicant — Source: Permit Jurisdiction | "CA" |
| applicant\_zipcode | `string` | The zipcode of the permit applicant — Source: Permit Jurisdiction | "94101" |
| applicant\_email | `string` | The email address of the permit applicant — Source: Permit Jurisdiction | "[john.smith@example.com](mailto:john.smith@example.com)" |
| applicant\_phone | `string` | The phone number of the permit applicant — Source: Permit Jurisdiction | "123-456-7890" |
| window\_door | `boolean` | `TRUE` if the permit includes a window or door | "TRUE" |
| street\_no | `string` | The street number of the property — Source: Permit Jurisdiction | "123" |
| street | `string` | The street name of the property — Source: Permit Jurisdiction | "Main St" |
| city | `string` | The city of the property — Source: Permit Jurisdiction | "San Francisco" |
| zipcode | `string` | The zipcode of the property — Source: Permit Jurisdiction | "94101" |
| zipcode\_ext | `string` | The zipcode extension of the property — Source: Permit Jurisdiction | "1234" |
| jurisdiction | `string` | The jurisdiction that issued the permit — Source: Permit Jurisdiction | "San Francisco" |
| county | `string` | The county of the property — Source: Permit Jurisdiction | "San Francisco" |
| state | `string` | The state of the property — Source: Permit Jurisdiction | "CA" |
| lat | `decimal` | The latitude of the property | "37.7749" |
| long | `decimal` | The longitude of the property | "-122.4194" |
| address\_id | `string` | The Shovels `geo_ID` for the address | "caf3b9d5ce317d53" |
| city\_id | `string` | The Shovels `geo_ID` for the city | "KLais31" |
| jurisdiction\_id | `string` | The Shovels `geo_ID` for the jurisdiction | "ALa2s33" |
| county\_id | `string` | The Shovels `geo_ID` for the county | "BLa2s33" |
| addition | `boolean` | `TRUE` if the permit includes an addition | "TRUE" |
| adu | `boolean` | `TRUE` if the permit includes an Additional Dwelling Unit (ADU) | "FALSE" |
| bathroom | `boolean` | `TRUE` if the permit includes a bathroom | "FALSE" |
| battery | `boolean` | `TRUE` if the permit includes a battery | "FALSE" |
| demolition | `boolean` | `TRUE` if the permit includes demolition | "FALSE" |
| electric\_meter | `boolean` | `TRUE` if the permit includes an electric meter | "FALSE" |
| electrical | `boolean` | `TRUE` if the permit includes electrical work | "TRUE" |
| ev\_charger | `boolean` | `TRUE` if the permit includes an EV charger | "TRUE" |
| fire\_sprinkler | `boolean` | `TRUE` if the permit includes a fire sprinkler | "TRUE" |
| gas | `boolean` | `TRUE` if the permit includes gas work | "FALSE" |
| generator | `boolean` | `TRUE` if the permit includes a generator | "FALSE" |
| grading | `boolean` | `TRUE` if the permit includes grading | "FALSE" |
| heat\_pump | `boolean` | `TRUE` if the permit includes a heat pump | "FALSE" |
| hvac | `boolean` | `TRUE` if the permit includes HVAC work | "FALSE" |
| kitchen | `boolean` | `TRUE` if the permit includes a kitchen | "FALSE" |
| new\_construction | `boolean` | `TRUE` if the permit includes new construction | "FALSE" |
| plumbing | `boolean` | `TRUE` if the permit includes plumbing work | "FALSE" |
| pool\_and\_hot\_tub | `boolean` | `TRUE` if the permit includes a pool or hot tub | "FALSE" |
| remodel | `boolean` | `TRUE` if the permit includes remodeling work | "FALSE" |
| roofing | `boolean` | `TRUE` if the permit includes roofing work | "FALSE" |
| solar | `boolean` | `TRUE` if the permit includes solar work | "TRUE" |
| water\_heater | `boolean` | `TRUE` if the permit includes a water heater | "FALSE" |
| property\_year\_built | `integer` | The year the property was built | "1980" |
| property\_lot\_size | `integer` | The size of the property lot in square feet | "1000" |
| property\_story\_count | `integer` | The number of stories on the property | "2" |
| property\_unit\_count | `integer` | The number of units on the property | "1" |
| property\_assess\_market\_value | `integer` | The assessed market value of the property (in USD) | "19000000.00" |
| property\_type | `string` | The type of property | "Residential" |
| property\_type\_detail | `string` | The detailed type of property | "Single Family Home" |
| property\_building\_area | `integer` | The total building area of the property (in square feet) | "1000" |
| property\_legal\_owner | `string` | The name of the legal owner of the property | "John Doe" |
| property\_owner\_type | `string` | The type of owner of the property | "Individual" |
| property\_census\_tract | `string` | The property's census tract number — Source: Gov (Fed) | "06075000100" |
| property\_congressional\_district | `string` | The property's congressional district number — Source: Gov (Fed) | "11" |
| contractor\_id | `string` | The Shovels ID for the contractor (if any) associated with this permit | "John Smith Construction, LLC" |
| contractor\_group\_id | `string` | The Shovels ID for the highest level contractor entity associated with this permit | "Smith & Brothers, LLC" |
| Field Name | Data Type | Description | Example |
| :------------------- | :-------- | :-------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------- |
| biz\_name | `string` | The business name of the contractor — Source: Permit Jurisdiction | "John Smith Construction, LLC" |
| biz\_type | `string` | The type of business: Joint Venture, Corporation, Partnership, Limited Liability, Sole Owner | "General Contractor" |
| name | `string` | The name of the contractor associated with the business, if available — Source: Permit Jurisdiction | "John Smith" |
| dba | `string` | The "Doing Business As" name of the contractor, as recorded on the permit | "John Smith Construction, LLC" |
| classification | `array` | The type of license(s) issued to the contractor | `["General Contractor", "Plumbing"]` |
| license | `string` | The license number of the contractor | "1234567890" |
| license\_act\_date | `date` | The activation date of the contractor's license | "2022-01-04" |
| license\_exp\_date | `date` | The expiration date of the contractor's license | "2026-12-31" |
| license\_inact\_date | `date` | The inactivation date of the contractor's license | "NULL" |
| license\_issue\_date | `date` | The issue date of the contractor's license | "2022-01-01" |
| first\_seen\_date | `date` | The date the contractor was first seen in the Shovels database | "2023-01-01" |
| sic | `string` | The Standard Industrial Classification (SIC) code of the contractor | "1234" |
| naics | `string` | The North American Industry Classification System (NAICS) code of the contractor | "123456" |
| phone | `string` | The phone number of the contractor — Source: Permit Jurisdiction | "123-456-7890" |
| email | `string` | The email address of the contractor — Source: Permit Jurisdiction | "[john.smith@example.com](mailto:john.smith@example.com)" |
| primary\_industry | `string` | The primary industry of the contractor | "Construction" |
| primary\_phone | `string` | The most relevant phone number of the contractor, based on recent permits | "123-456-7890" |
| primary\_email | `string` | The most relevant email address of the contractor, based on recent permits | "[john.smith@example.com](mailto:john.smith@example.com)" |
| linkedin\_url | `string` | The LinkedIn URL of the contractor | "[www.linkedin.com/in/johnsmith](http://www.linkedin.com/in/johnsmith)" |
| website | `string` | The website of the contractor | "[www.johnsmithconstruction.com](http://www.johnsmithconstruction.com)" |
| revenue | `integer` | The annual revenue of the contractor (in USD) | "1000000" |
| street | `string` | The street address of the contractor | "A St" |
| street\_no | `string` | The street number of the contractor | "789" |
| city | `string` | The city of the contractor | "San Francisco" |
| county | `string` | The county of the contractor | "San Francisco" |
| state | `string` | The state of the contractor | "CA" |
| zipcode | `integer` | The zipcode of the contractor | "94101" |
| zipcode\_ext | `integer` | The zipcode extension of the contractor | "1234" |
| lat | `decimal` | The latitude of the contractor's address | "37.7749" |
| lng | `decimal` | The longitude of the contractor's address | "-122.4194" |
| Field Name | Data Type | Description | Example |
| :---------------------------------- | :-------- | :-------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- |
| contractor\_id | `string` | Unique Shovels ID for each contractor record. | "e79c3393ad" |
| contractor\_group\_id | `string` | Unique Shovels ID for related contractor records. | "a894slk74382siv2" |
| person\_id | `string` | Unique ID for the individual contractor or employee. | "aa145cd32c95f69b95e9233f6a957a32" |
| name | `string` | The name of the individual. | "Jane Smith" |
| street\_no | `string` | The street number of the individual's address. | "789" |
| street | `string` | The street name of the individual's address. | "A St" |
| city | `string` | The city of the individual's address. | "San Francisco" |
| zipcode | `string` | The zipcode of the individual's address. | "94101" |
| zipcode\_ext | `string` | The zipcode extension of the individual's address. | "1234" |
| state | `string` | The state of the individual's address. | "CA" |
| phone | `string` | The phone number of the individual. | "123-456-7890" |
| email | `string` | The personal email address of the individual. | "[jane.smith@example.com](mailto:jane.smith@example.com)" |
| email\_validation\_status | `string` | State of whether the email address is validated. | "Valid (Digital)" |
| email\_last\_seen | `date` | Most recent date the email was confirmed active. | "2025-02-07" |
| business\_email | `string` | The business email of the individual. | "[jane.smith@johnsmithconstruction.com](mailto:jane.smith@johnsmithconstruction.com)" |
| business\_email\_validation\_status | `string` | State of whether the business email is validated. | "Valid (Digital)" |
| business\_email\_last\_seen | `date` | Most recent date the business email was confirmed active. | "2025-02-02" |
| linkedin\_url | `string` | The LinkedIn URL of the individual. | "[www.linkedin.com/in/jane-smith-construction](http://www.linkedin.com/in/jane-smith-construction)" |
| homeowner | `string` | Indicates whether the individual owns their home. | "Y" |
| gender | `string` | Indicates the gender of the individual. | "F" |
| age\_range | `string` | The age bracket of the individual. | "35-44" |
| is\_married | `boolean` | `TRUE` if the individual is married. | "TRUE" |
| has\_children | `boolean` | `TRUE` if the individual has children. | "FALSE" |
| income\_range | `string` | The annual income bracket of the individual (in USD). | "60,000 to 74,999" |
| net\_worth | `string` | The net worth bracket of the individual (in USD). | "1,000,000 to 2,499,999" |
| job\_title | `string` | Current professional position. | "Revenue Analyst" |
| seniority\_level | `string` | Current seniority level in the individual's position. | "Staff" |
| department | `string` | Current department or division of employment. | "Revenue Operations" |
| job\_title\_last\_updated | `date` | The date the job title information was last updated. | "2025-01-01" |
| work\_history | `array` | The individual's employment history details. | `[{"company_name": "John Smith Construction, LLC", "duration": "3 years 5 months", "end_time": "Present"}]` |
| education\_history | `array` | The individual's education history. | `[{"degree": "Bachelors of Arts, Marketing", "institution_name": "University of California, Berkeley", "end_time": "2014-06-01", etc}]` |
| Field Name | Data Type | Description | Example |
| :----------------------------------- | :-------- | :----------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id | `string` | Unique ID for each resident record. | "we8lsd25l08" |
| company\_id | `string` | Unique ID for the company associated with the resident. | "82lsdsso4800x25lk" |
| name | `string` | The full name of the resident. | "John Doe" |
| street\_no | `string` | The street number of the resident's address. | "123" |
| street | `string` | The street name of the resident's address. | "Main St" |
| city | `string` | The city of the resident's address. | "San Francisco" |
| zipcode | `string` | The zipcode of the resident's address. | "94101" |
| zipcode\_ext | `string` | The zipcode extension of the resident's address. | "1234" |
| state | `string` | The state of the resident's address. | "CA" |
| phone | `string` | The phone number of the resident. | "123-456-7890" |
| personal\_emails | `string` | The personal email addresses of the resident. | "[john.doe@example.com](mailto:john.doe@example.com)" |
| personal\_emails\_validation\_status | `string` | State of whether the personal email address is validated. | "Valid (Digital)" |
| personal\_emails\_last\_seen | `date` | Most recent date the personal email was confirmed active. | "2025-01-28" |
| business\_email | `string` | The business email of the resident. | "[john.doe@acme.com](mailto:john.doe@acme.com)" |
| business\_email\_validation\_status | `string` | State of whether the business email is validated. | "Valid (Digital)" |
| business\_email\_last\_seen | `date` | Most recent date the business email was confirmed active. | "2025-02-08" |
| linkedin\_url | `string` | The LinkedIn URL of the resident. | "[www.linkedin.com/in/john-doe-acme](http://www.linkedin.com/in/john-doe-acme)" |
| homeowner | `string` | Indicates whether the resident owns their home. | "Y" |
| gender | `string` | Indicates the gender of the resident. | "M" |
| age\_range | `string` | The age bracket of the resident. | "55-64" |
| is\_married | `boolean` | `TRUE` if the resident is married. | "TRUE" |
| has\_children | `boolean` | `TRUE` if the resident has children. | "FALSE" |
| income\_range | `string` | The income bracket of the resident. | "100,000 to 124,999" |
| net\_worth | `string` | The net worth bracket of the resident. | "2,500,000 to 3,749,999" |
| job\_title | `string` | The current professional position of the resident. | "Senior Project Manager" |
| seniority\_level | `string` | The current seniority level of the resident in their position. | "Staff" |
| department | `string` | The current department or division of employment for the resident. | "Operations" |
| job\_title\_last\_updated | `date` | The date the job title information was last updated. | "2025-02-01" |
| work\_history | `string` | The resident's employment history. | `[{"company_name": "Acme, LLC", "duration": "14 years 9 months", "end_time": "Present"}]` |
| education\_history | `string` | The resident's education history. | `[{"degree": "Bachelors of Science, Electrical Engineering", "institution_name": "University of California, San Diego", "end_time": "1972-06-10", etc}]` |
| social\_connections | `string` | The number or details of the residents professional network connections. | "500+" |
| company\_name | `string` | The name of the company the resident works for. | "Acme, LLC" |
| company\_domain | `string` | The domain of the company the resident works for. | "acme.com" |
| company\_phone | `string` | The primary phone number of the resident's company. | "102-040-5060" |
| company\_sic | `string` | The Standard Industrial Classification (SIC) code of the resident's company. | "1234" |
| company\_street\_no | `string` | The company's primary location's street address number. | "111" |
| company\_street | `string` | The company's primary location's street address. | "Acme St" |
| company\_city | `string` | The company's primary location's city. | "San Francisco" |
| company\_zip | `string` | The company's primary location's zipcode. | "94101" |
| company\_state | `string` | The company's primary location's state. | "CA" |
| company\_linkedin\_url | `string` | The LinkedIn URL of the company. | "[www.linkedin.com/company/acme](http://www.linkedin.com/company/acme)" |
| company\_revenue | `string` | The annual revenue bracket of the company (in USD). | "1 Billion and over" |
| company\_employee\_count | `string` | The number of employees of the company. | "150,000" |
| company\_primary\_industry | `string` | The primary industry of the company. | "Manufacturing" |
| company\_description | `string` | The description of the company. | "Acme is a leading manufacturer of cartoon mousetraps." |
| company\_naics | `string` | The North American Industry Classification System (NAICS) code of the company. | "123456" |
| address\_id | `string` | The Shovels ID for the address of the resident. | "we8lsd25l08" |
# Overview
Source: https://docs.shovels.ai/docs/data-dictionary-index
Welcome to the Shovels Data Dictionary!
This section of our documentation is a reference guide to all the data within the platform, including field names, data types, descriptions, examples, and more.
There will be an overarching table for core concepts and objects, and then sub-pages with tables specific to each product within the platform.
Generally speaking, the data hierarchy is as follows:
1. Shovels Online
2. Shovels API (includes all objects available in **Online**, with some extras)
3. Shovels EDL (includes all objects, fields, and data available in the entire platform)
Check out the product-specific data dictionaries below.
The complete list of fields and filters available in **Shovels Online**.
All fields, parameters, and objects available in the **Shovels API**.
The exhaustive list of all fields in the Shovels platform.
# Online
Source: https://docs.shovels.ai/docs/data-dictionary-online
The complete list of fields and filters available in **Shovels Online**.
Work In Progress
# Building Blocks
Source: https://docs.shovels.ai/docs/foundations-building-blocks
Understanding the key data components that make up the Shovels platform.
## The Shovels Dataset
When we refer to *The Shovels Dataset*, we mean the entirety of the data we have.
This data has already gone through our processing pipelines and QA, and is the backbone of the entire platform (even if some of the data isn't yet available on all of the platforms yet).
In this article, we'll walk through each of the key object types in our dataset, and a bit about their provenance.
### Permits
If the Shovels dataset is the backbone of the platform, then permits are the spinal cord running through the entire thing.
Permits are rich sources of data, which (usually) include a wide range of information such as:
* property address
* property owner (and contact information)
* contractor (and contact information) if applicable
* project description
* project category (e.g. residential, commercial, industrial, etc)
* project sub-category (e.g. single-family, multi-family, office space, etc)
* permit type (e.g. demolition, excavation, grading, etc)
* application date
* approval dates and status
* project completion, if applicable
* project value and fees (for calculating applicable taxes)
* other included documents or materials
That is a treasure trove of information, and when analyzed at scale allows for some intriguing insights. These are our derived metrics, which we add and enrich to the permit object.
Permit data is extremely useful in too many ways to list here, but if you want to know the status and details of a project, then the permit is the best place to start.
For more information on how we obtain our permit data (and the challenges that accompany that), see our section on [Permit Availability](/docs/foundations-understanding-permits).
#### Permit Categories
In the list above, you'll see a `permit type` field. That is a complicated nut to crack, as there are so many different names and variations of how permits indicate what kind of work is being done. For the Shovels dataset, we refer to this field as **Category**.
These refer to what kind of project the permit is for, such as `heat pump` or `solar panel` or `Additional Dwelling Unit (ADU)`. Sometimes, there isn't a clearly listed category, and the specifics are hidden in the `project description` field. Sometimes, the project spans multiple types, but only a single type is included on the permit field.
This is where we put the majority of the "ai" in "shovels.ai": by pumping all of the data through our purpose-built and specifically trained LLMs to ensure that we capture every angle of the permit. Even obscure abbreviations or misspellings are corrected and categorized appropriately.
### Contractors
This can vary widely by permit type and jurisdiction, but most permits applications are submitted by the contractor doing the project, which allows us to create a database of all the permits submitted by individual contractors or contracting companies.
Using the same fields from the permits, we can keep track in a separate table the contractors, and derive their own subset of special metrics based on their submitted permits.
Sometimes the permits will contain detailed contact information for the contractor, but in cases where it's lacking we will enrich that ourselves.
So if you want to reach out by email, phone, or just explore their website and project history, our `contractors` database will be a great place to begin.
#### Employees
As a subset of the `contractors` table, we also include detailed information about the employees of a contractor organization, if there are any we can find.
This includes demographic data for the individual employee as well as their role in the company, which will help with understanding who is making the decisions and who is doing the work on the ground.
The `employees` table is a new addition, so we're still completing our enrichment process across the board -- results may be incomplete in the short term.
### Properties
Properties are a required field of every permit. Just like with the contractors, permit data lets us build a database and historical record of construction projects done by parcel or address.
Combining this with publicly and privately available GIS data let's us do a ton of fancy mapping and geographical analysis, which can be useful for real estate exploration and trend analysis.
### Parcels
Contrasted against properties, parcels show property characteristics such as zoning, building use, building dimensions and land usage, and more.
Connected with the same GIS data as with properties, we're able to show an all-encompassing view of properties, nationwide.
### Residents
The crown jewel of any demographics dataset is the people themselves. Combining permit data (which shows home ownership and project involvement) with other publicly available data sources (like census records, zoning and building use designations, and others) let's us extrapolate whether someone is a homeowner, renter, or dependent of either.
It's important to us to maintain the highest standards of ethics and data privacy, so we thoroughly vet all of our data sources to ensure that they're publicly available and ethically sourced. Whenever possible, we extrapolate the data from our existing records.
## More to come?
The construction, energy, and real estate industries are as diverse as they are lucrative, and we know that we're only scratching the surface.
If there's a particular data type or data point you'd like to see us add, reach out to [Support](mailto:support@shovels.ai) and we'll see what we can do.
# Data Philosophy
Source: https://docs.shovels.ai/docs/foundations-data-philosophy
Gain insights into how the Shovels team approaches the data in our platform.
## The Shovels Mission
Most construction-related data is publicly available in theory, but difficult to access in practice.
Data like building permits unlock information regarding contractor activity, job fees, and project distributions. Census and tax assessor data provides demographics and economic data. Parcel and property data provides the underlying grid for connecting the dots, geo-spatially.
Shovels takes all these disparate data streams and brings them into a single platform, making it more accessible than ever.
## Data Practices
As data fanatics, we care *a lot* about data quality and robust pipelines. We've built our architecture from the ground up to handle 100s of millions of records at a time, to be ingested, parsed, categorized, enriched, and delivered.
We use advanced LLMs that are self-trained to permit data specifically, to ensure that we're capturing all the varied nuance of permit data across the wide range of permit jurisdictions and requirements.
## Data vs Insights
Data is the easy (once you have it). Insights are the more complicated part that require understanding the data, the market(s) interested in the data, and the knowing which dots to connect where in order to derive them.
We've never been purely interested in raw data. The key for us is combining multiple data streams, where each source builds upon the others organically. This is no easy task, as there isn't always an obvious foreign key to join everything together. Nor is it so simple to simply access these data streams. But that's the hard work you pay us to do for you.
This is how we're able to deliver not just the data, but the insights surrounding it. Data plus insights equals intelligence, and that is the Shovels mission.
## What's Next?
We have a lot of good ideas, and therefore a ton of work to do to bring the Shovels platform to the next level.
If it's construction-related, then we're interested in it. We won't give away everything now, but stay tuned to our [LinkedIn](https://www.linkedin.com/company/shovels) and [Blog](https://shovels.ai/blog) for those updated when they come.
# Permit Availability
Source: https://docs.shovels.ai/docs/foundations-understanding-permits
Introduction to US permit availability and variation across 20k+ jurisdictions.
## Introduction
In the US, building permits are a critical part of the construction, energy, and real estate industries. If you're here, you probably know this already.
Historically, it's been difficult to access this permit data: it was usually a manual process, using paper records, and often was bottle-necked by staffing limitations in permit jurisdiction offices.
And the difficulties only compound when attempting to access this data at any scale from county to nationwide.
While Shovels isn't the first to offer this permit data online, we're the first to do using advanced LLM-based processing, the first to make it accessible programmatically via an API, and the first to combine as many datasets (like `properties`, `contractors`, `employees`, `residents`, and more) with the permit data.
What we offer is as full a picture of the US construction industry as possible, and we're also tackling the problem of jurisdiction coverage to make sure that you can confidently access permit data for every jurisdiction in the country.
That is our north star, and we're driving towards it every day.
## Permit Availability
Online permit data brokers either source the data themselves by building their own scraping and processing pipelines, or they pay a third-party data provider to do that work for them.
At Shovels, we do it ourselves. As of 2026 we source permits first-party — our own scraping and collection pipeline run across the country — rather than licensing from third-party providers. Even so, there are over 20,000 permit jurisdictions in the US, and no one has them all.
So we run the collection ourselves and combine every source we can reach — including jurisdictions that publish nothing online, which our team activates directly — to maximize coverage. At the core, permit availability is a scraping and automation problem with linearly-scaling maintenance costs, and we invest in it directly rather than buying a snapshot.
### Digitization
Since scraping is the name of the game, we're reliant on the permit jurisdiction offices to digitize their records, and make them available to the public for digital access. Permit records are public record by law, and technically accessible to anyone that wants them. Major municipalities are usually pretty good about this, and even maintain ongoing projects to digitize their historical records, not just the newly submitted permits.
However, in the rural counties and unincorporated areas, this is not always the case.
It's a work in progress, but we're getting there.
### FOIA requests
In certain cases, Freedom of Information Act (FOIA) requests are a way to directly request the records. Often, the individual jurisdictions will maintain their own request portals, but in cases where they don't the federal FOIA request form is the only recourse.
We try to avoid this as much as possible as it's an involved and manual process that doesn't scale well.
### Permit Quality
Now taking the records that *are* digitized and available, we still need to ensure that the data within them is clean and usable.
The most obvious issue could be the actual quality of the record scan. If the permit paperwork is filled out online, then it's usually pretty good. But if it's done by hand, either by the permit office clerk or the contractor, then it's a different story.
However, we've gotten pretty good at parsing these records, so it's rare that we need to actually throw out a digitized permit for legibility reasons.
The biggest hangup in this is the variance in required fields across jurisdictions. Some, like many of the larger cities in California, have well-define requirements that include a wide range of data for the project itself, the property, and the professionals involves.
Others, like more rural counties or less-regulated states like Texas or New Hampshire, have fewer requirements.
Balancing this variance in data depth and quality in a unified platform is a challenge, but one we're eager to keep tackling.
## The Shovels Dataset
This now brings us to the end result: the Shovels dataset.
We have permits from all 50 states (including DC), and have at least *some* coverage in all major metropolitan areas.
If you'd like to see the exact details, feel free to peruse our [Coverage Map](https://www.shovels.ai/coverage).
### Cleaning, Standardizing, and Deriving
Like we mentioned in the section on Permit Quality, we have a detailed and in-depth pipeline for handling the raw permit data we receive.
Here's a snippet of the process, from a blog post [overview of our partnership with Prolific](https://www.shovels.ai/blog/unlocking-shovelss-potential-with-prolific/) on how we trained our pipeline to handle the permit data we received.
Categorizing the data we have is just one step of our process: we also derive interesting metrics where we can. Some of these derived metrics, like `construction_duration` or `inspection_pass_rate` are simple calculations, others like `job_value` (which is an extremely nuanced data point to understand, due to how frequently it's under-reported) involve heavy modeling.
We don't just provide the construction and permit data, we make it useful.
### Limitations
Implicit in the entire article thus far are complications and limitations. If something isn't available, or isn't legible, or only goes back a few years, then we (along with anyone else in the construction insights space) are going to struggle.
But we wouldn't be here if we weren't confident we can sort it out eventually. Thank you for your patience while we keep digging.
## Where to go from here?
We're ultimately reliant on the jurisdictions' individual digitization efforts, so we're going to ensure that we continue to support and foster relationships with these state and local government offices to ensure a smooth and seamless experience for everyone.
And we'll keep adding more data sources until we can round every jurisdiction in the US, all 20k+ of them.
### Requesting Permit Data
We have a running roadmap of where we want to get data from next, but we're always open to suggestions from users like you to help us prioritize certain areas over others.
If there's a key geographical area that is important to your business needs, reach out to us at [sales@shovels.ai](mailto:sales@shovels.ai) and we'll add it to our list.
# Welcome to Shovels
Source: https://docs.shovels.ai/docs/introduction
Discover the Shovels.ai platform—your comprehensive resource for construction data, APIs, and developer tools. Access industry-leading insights, technical documentation, and everything you need to build, integrate, and innovate with construction intelligence.
Shovels is the industry-leading source of construction intel; cleaned, categorized, and ready for building your next lead list or integrating with your climate- or prop-tech solution.
This is our documentation hub, where we'll host all the technical information you need to get the most out of our platform.
If you have a question that isn't answered by our range of tutorials, how-to walkthroughs, troubleshooting guides, or reference materials, then that's on us. Drop us an email and we'll answer it directly.
### Get started with the Shovels Platform today:
Explore Contractors and Permits at every geographical level, with filters for job type, building type, permit details, and more.
Query permits and contractors from your terminal or AI agent. One binary, JSON output, zero dependencies.
Leverage our best-in-industry API, which is ready for direct product integration or programmatic data wrangling.
Let your data science team loose with the entirety of the Shovels datasets, which ranges from Permits to Contractors to Properties to Demographics, nationwide.
### Helpful Links
[Contact Sales](mailto:sales@shovels.ai)
[Contact Support](mailto:support@shovels.ai)
[Keep in Touch (our monthly Newsletter)](https://www.shovels.ai)
# What is a geo_id and How Do I Get One?
Source: https://docs.shovels.ai/docs/knowledge-base/api/address-resolution/geo-id-lookup
A geo_id is Shovels' unified geographic identifier for addresses, zip codes, cities, counties, jurisdictions, and states. Each geography level has its own search endpoint for resolving a geo_id.
**A geo\_id is Shovels' unified geographic identifier that can represent a state (`CA`), zip code (`94103`), city, county, jurisdiction, or specific address.** Once you have a geo\_id, you pass it to other endpoints (such as permit search) to query data at that geography level.
Each geography level has its own search endpoint for resolving a geo\_id. For an address-level geo\_id, use the Address Search endpoint (`GET /v2/addresses/search`).
## Understanding geo\_id
The `geo_id` is a unified geography identifier that works at multiple levels. State and zip code geo\_ids are human-readable, while city, county, jurisdiction, and address geo\_ids are opaque encoded strings:
| Geography Level | Example geo\_id | Resolve with |
| --------------- | -------------------------- | ------------------------------------------------------------------------- |
| State | `CA` | [Search States](/api-reference/states/search-states) |
| Zip Code | `94103` | [Search Zipcodes](/api-reference/zipcodes/search-zipcodes) |
| County | `Q291bnR5XzEyMzQ1` | [Search Counties](/api-reference/counties/search-counties) |
| City | `Q2l0eV8xMjM0NQ` | [Search Cities](/api-reference/cities/search-cities) |
| Jurisdiction | `Q2l0eV8xMjM0NQ` | [Search Jurisdictions](/api-reference/jurisdictions/search-jurisdictions) |
| Address | `MDEyMzQ1Njc4OWFiY2RlZg==` | [Search Addresses](/api-reference/addresses/search-addresses) |
The same permit search works whether you pass a state abbreviation, zip code, or city/address geo\_id—you're just changing the geography level.
## Getting an Address geo\_id
The Address Search endpoint returns the `geo_id` associated with an address. Pass your search text in the required `q` parameter:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/addresses/search?q=123+Main+St,+San+Francisco,+CA" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
The returned address geo\_id is an opaque encoded string. Use it to query permits:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
GET /v2/permits/search?geo_id=ADDRESS_GEO_ID&permit_from=2024-01-01
```
## State and Zip geo\_ids
State and zip code geo\_ids are human-readable, so you can often use them directly without a lookup:
* State: the 2-letter abbreviation (California = `CA`, Texas = `TX`, Florida = `FL`)
* Zip code: the 5-digit ZIP, optionally with a 4-digit extension (`94103` or `94103-1234`)
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
GET /v2/permits/search?geo_id=CA&permit_from=2024-01-01
```
To confirm coverage or look up the exact value, use [Search States](/api-reference/states/search-states) or [Search Zipcodes](/api-reference/zipcodes/search-zipcodes).
## City, County, and Jurisdiction geo\_ids
City, county, and jurisdiction geo\_ids are opaque encoded strings, so you can't construct them by hand. Resolve them through their dedicated search endpoints first, then pass the returned geo\_id to your query:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/cities/search?q=San+Francisco,+CA" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
The same pattern applies to [Search Counties](/api-reference/counties/search-counties) and [Search Jurisdictions](/api-reference/jurisdictions/search-jurisdictions).
## What If No geo\_id Is Found?
If the address or jurisdiction isn't in our system, the API returns a 200 response with an empty `items` array. This means:
* The address format was understood
* No matching address or jurisdiction exists for that query in our database
## Related Articles
* [Resolving addresses](/docs/knowledge-base/api/address-resolution/resolving-addresses)
* [Error handling for geo\_id](/docs/knowledge-base/api/errors/error-handling)
# Resolving Addresses to geo_id
Source: https://docs.shovels.ai/docs/knowledge-base/api/address-resolution/resolving-addresses
Learn the essential first step in using the Shovels API: resolving addresses through our Address Search endpoint to get the geo_id for permit queries.
Address resolution is the essential first step in using Shovels' API effectively.
## The Two-Step Process
1. **Resolve the address** - Get a `geo_id` from our Address Search endpoint
2. **Query permits** - Use the `geo_id` to retrieve permit data
## Step 1: Resolve the Address
Use the Address Search API endpoint to find addresses with permits in our system:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/addresses/search?q=123+Main+St,+San+Francisco,+CA" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
The response includes:
* Standardized address information
* A unique `geo_id`
## Step 2: Query Permits
Use the `geo_id` to retrieve permits:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/permits/search?geo_id=ADDRESS_GEO_ID&permit_from=2020-01-01" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
## Why geo\_id Matters
The `geo_id` system offers several advantages:
* **Consistent identification** regardless of address formatting variations
* **Stable reference point** even if address details change over time
* **Improved query performance** compared to direct address searching
If the address doesn't have any permits in our database, the API returns a 200 OK response with an empty `items` array.
## Best Practices
* Provide as much address information as possible (street number, name, city, state, ZIP)
* Our system standardizes addresses using multiple verification sources
* The `geo_id` parameter works at multiple levels: address, zip code, city, county, or state
## Alternative: Shovels Online
For quick lookups without code, search by address directly in [Shovels Online](https://app.shovels.ai): on the Permits tab, choose **Address** under the location filter and start typing. Address search is available on permit searches only.
## Related Articles
* [How to find a geo\_id](/docs/knowledge-base/api/address-resolution/geo-id-lookup)
* [State and zip validation](/docs/knowledge-base/api/address-resolution/state-zip-validation)
* [API Reference: Search Addresses](/api-reference/addresses/search-addresses)
# State and Zip Code Validation in Address Searches
Source: https://docs.shovels.ai/docs/knowledge-base/api/address-resolution/state-zip-validation
Understand how the Shovels API handles state and zip code validation in address searches and what to expect with incorrect location parameters.
When using Shovels' API for address searches, it's important to understand how state and zip code parameters are processed.
## Key Behavior
**State and zip code parameters are not strictly validated** as part of geo\_id resolution.
If you provide an incorrect state or zip code when searching for addresses:
* You won't receive an explicit error message
* The API returns a 200 OK response with an empty result set (`items: []`)
## Why This Happens
This behavior is by design to accommodate variations in how addresses might be recorded across different jurisdictions.
## Best Practices
For the most reliable results:
1. **Focus on the street address** - Street name, number, and city are most important
2. **Verify location data** - Double-check state and zip codes before querying
3. **Handle empty results** - If you consistently get empty results for addresses you expect to find, verify the state and zip information
Our system uses multiple address verification sources to ensure accuracy:
* National Address Dataset from the US Census
* Open Address dataset
* Simple Maps
* ESRI
## Interpreting Empty Results
An empty result set can mean:
* The address exists but with different state/zip information
* The address doesn't have permits in our system
* There's a formatting mismatch in your query
## Troubleshooting
If you're getting unexpected empty results:
1. Try the query with just the street address and city
2. Verify the state abbreviation is correct (use 2-letter codes)
3. Check that the zip code matches the city
4. Search at a broader geography level first (city or zip) to confirm coverage
## Related Articles
* [Resolving addresses](/docs/knowledge-base/api/address-resolution/resolving-addresses)
* [Error handling](/docs/knowledge-base/api/errors/error-handling)
# Understanding API Endpoints
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/api-endpoints
Quick overview of Shovels API endpoints. For complete details, see the API Reference documentation.
**An API endpoint is a specific URL that represents a particular function or query.** The Shovels API (v2) provides access to permits, properties, decisions, contractors, addresses, and supporting resources. For complete endpoint documentation, see the [API Reference](/api-reference).
## Main Endpoint Categories
| Category | Base Path | Purpose |
| --------------- | ------------------- | ------------------------------------------------------ |
| **Permits** | `/v2/permits/*` | Building permit data |
| **Properties** | `/v2/properties/*` | Property records with summarized permit history (beta) |
| **Decisions** | `/v2/decisions/*` | Zoning and land use decisions |
| **Contractors** | `/v2/contractors/*` | Contractor information |
| **Addresses** | `/v2/addresses/*` | Address resolution to geo\_ids |
| **Lists** | `/v2/list/*` | Predefined values (tags, zip codes) |
| **Meta** | `/v2/meta/*` | API metadata and release info |
## Search vs Detail Endpoints
* **Search endpoints** return multiple records based on filters
* **Detail endpoints** return specific records by ID
Search responses include full detail payloads—you usually don't need a separate detail call.
## Full Documentation
For complete endpoint details, parameters, and interactive testing:
* [API Reference](/api-reference) - Full endpoint documentation with playground
* [API Introduction](/docs/shovels-api-introduction) - Authentication, pagination, and usage tips
## Related Articles
* [Query parameters explained](/docs/knowledge-base/api/basics/query-parameters)
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
# How to Access Your API Key
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/api-key-access
Learn how to access and manage your Shovels API key from your account dashboard for authenticating API requests.
Your API key is required to authenticate all requests to the Shovels API.
## Accessing Your API Key
1. [Log in to your Shovels account](https://app.shovels.ai/account?tab=apikey)
2. Open the **API key** tab
3. Your API key is displayed in the **API Key** field
## Using Your API Key
Include your API key in the header of every API request:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET "https://api.shovels.ai/v2/meta/release" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
## Free Plan API Access
New accounts start on the Free plan, with a monthly credit allowance to explore our capabilities before committing to a paid plan. See [shovels.ai/pricing](https://www.shovels.ai/pricing).
Every plan is credit-based: each record returned counts as one credit, on Free and paid plans alike.
## Tracking Your Usage
You can view your API credit usage on the **Manage subscription** tab of your account or by calling the `GET /v2/usage` endpoint. Credits reset monthly on your subscription or upgrade date.
## Need More Credits?
If you hit your limit and need more credits:
* Contact [sales@shovels.ai](mailto:sales@shovels.ai)
* Call us at [1-800-511-7457](tel:+18005117457)
## Related Articles
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
* [API credit limits](/docs/knowledge-base/api/basics/credit-limits)
* [API documentation](https://docs.shovels.ai/docs/shovels-api-introduction)
# Can I Automate API Calls?
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/automate-calls
Learn how to automate Shovels API calls by integrating the API into your applications using programming languages like Python.
Yes, developers can automate API calls by integrating the Shovels API into their applications.
## Getting Started
The API can be integrated using any programming language that supports HTTP requests. Common choices include:
* Python
* JavaScript/Node.js
* Java
* Go
* Ruby
* PHP
## Example: Python Integration
```python theme={"theme":{"light":"css-variables","dark":"css-variables"}}
import requests
API_KEY = "YOUR_API_KEY_HERE"
BASE_URL = "https://api.shovels.ai/v2"
headers = {
"X-API-Key": API_KEY
}
# Search for permits
response = requests.get(
f"{BASE_URL}/permits/search",
headers=headers,
params={
"geo_id": "CA",
"permit_from": "2024-01-01",
"size": 100
}
)
data = response.json()
print(f"Found {len(data['items'])} permits")
```
## Code Snippets in Documentation
Our [API documentation](/api-reference) provides code snippets in multiple languages for each endpoint, making it easy to generate integration code for your preferred language.
## Best Practices for Automation
1. **Handle credit and rate limits** - Implement backoff when approaching limits
2. **Use pagination** - Process large result sets in batches
3. **Cache results** - Store data locally to minimize API calls
4. **Error handling** - Handle both errors and empty results gracefully
5. **Respect the API** - Avoid frivolous or unnecessary requests
Check your API usage on the **Manage subscription** tab of your account to monitor your credit consumption.
## Related Articles
* [API documentation](/api-reference)
* [API credit limits](/docs/knowledge-base/api/basics/credit-limits)
* [API rate limits](/docs/knowledge-base/api/basics/rate-limits)
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
# What is My API Credit Limit?
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/credit-limits
Learn how Shovels API credit limits work on the Free plan and paid plans, and how to get a larger monthly credit allowance.
**Your credit limit is the number of records you can retrieve each month, and every plan has one.** Each record returned by the API counts as one credit.
**Credit limits vs. rate limits:** Credit limits track how many records you can consume based on your plan (a billing concept). Rate limits restrict how quickly you can make requests to protect system stability (a technical concept). This page covers credit limits. See [API Rate Limits](/docs/knowledge-base/api/basics/rate-limits) for rate limiting details.
## Free Plan
The Free plan provides **500 credits per month** with no time limit or credit card required. Credits are counted the same way on every plan: each record returned counts as one credit, so a search returning 100 permits uses 100 credits.
If you need a larger allowance, see [shovels.ai/pricing](https://www.shovels.ai/pricing).
## Paid Plan Credit Limits
Paid plans include progressively larger monthly credit allowances. See [shovels.ai/pricing](https://www.shovels.ai/pricing) for current allowances, or contact [sales@shovels.ai](mailto:sales@shovels.ai) for Enterprise volumes.
For details on plan limits and pricing, see [shovels.ai/pricing](https://www.shovels.ai/pricing) or contact [sales@shovels.ai](mailto:sales@shovels.ai).
## If You Hit Your Credit Limit
When you exceed your credit limit, the API returns a **402 Payment Required** response. Contact [sales@shovels.ai](mailto:sales@shovels.ai) to increase your credit allocation.
For full details on API usage policies, see the [API Introduction](/docs/shovels-api-introduction#acceptable-use-policy).
## Understanding Your Credits
### Records Per Call
Search endpoints return up to **100 records per page** (default: 10) by setting the `size` parameter. Detail endpoints accept up to **50 IDs** per call. Each record returned counts against your credits—so a call returning 100 permits uses 100 credits.
### Efficient Querying
* Use search endpoints to get multiple records at once
* Search responses include full detail payloads—no separate detail call needed
* Use filters to get exactly the data you need
## Tracking Your Usage
Monitor your API usage on the **Manage subscription** tab of your account or via the `GET /v2/usage` endpoint. Credits reset monthly on your subscription or upgrade date.
## Need Higher Credit Limits?
Contact our team to discuss higher-tier plans:
* Email: [sales@shovels.ai](mailto:sales@shovels.ai)
* Phone: [1-800-511-7457](tel:+18005117457)
## Related Articles
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
* [How to access your API key](/docs/knowledge-base/api/basics/api-key-access)
* [API Rate Limits](/docs/knowledge-base/api/basics/rate-limits)
# Why is job_value Returned in Cents?
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/job-value-cents
Learn why the Shovels API returns job_value in cents rather than dollars, following industry-standard practices for handling monetary values.
**The `job_value` field is returned in cents, not dollars.** A value of `1000000` represents \$10,000.00. This is intentional and follows established API design patterns for handling monetary values.
## Why Cents?
Returning monetary values in cents (the smallest currency unit) is a common practice in financial APIs. This approach:
* **Avoids floating-point precision errors** — representing \$10.50 as `1050` cents eliminates rounding issues that can occur with decimal numbers
* **Simplifies integer arithmetic** — calculations with whole numbers are faster and more reliable
* **Follows industry standards** — major payment APIs like [Stripe](https://docs.stripe.com/currencies#zero-decimal) use the same convention
## Converting to Dollars
To convert `job_value` to dollars in your application, divide by 100:
```python theme={"theme":{"light":"css-variables","dark":"css-variables"}}
job_value_cents = 1000000
job_value_dollars = job_value_cents / 100 # Result: 10000.00
```
```javascript theme={"theme":{"light":"css-variables","dark":"css-variables"}}
const jobValueCents = 1000000;
const jobValueDollars = jobValueCents / 100; // Result: 10000.00
```
When displaying monetary values to users, format the result as currency after converting from cents.
## Related Articles
* [Understanding Query Parameters](/docs/knowledge-base/api/basics/query-parameters)
* [Data Dictionary](/docs/data-dictionary-api)
* [Understanding Permits](/docs/foundations-understanding-permits)
# How Many Permits Can I Retrieve Per API Call?
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/permits-per-call
Learn about the maximum number of permits you can retrieve in a single Shovels API call and how to paginate through larger result sets.
The number of records you can retrieve depends on the endpoint type:
| Endpoint Type | Max Records | Example |
| -------------------- | --------------- | --------------------------------- |
| **Search endpoints** | 100 per page | `GET /v2/permits/search?size=100` |
| **Detail endpoints** | 50 IDs per call | `GET /v2/permits?id=123,456,...` |
## Search Endpoints
Use the `size` parameter to control how many records are returned (default: 10, max: 100):
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/permits/search?geo_id=94103&size=100" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
## Pagination for Larger Results
If you need more than 100 permits, paginate through the results using the `next_cursor` value provided in the response.
### Cursor-Based Pagination
1. Make your initial request
2. Check the response for `next_cursor`
3. Use that cursor value in your next request
4. Repeat until no more results
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# First request
GET /v2/permits/search?geo_id=CA&size=100
# Subsequent requests with cursor
GET /v2/permits/search?geo_id=CA&size=100&cursor=CURSOR_VALUE
```
Your API credits are based on records returned—a call returning 100 permits uses 100 credits, while a call returning 1 permit uses 1 credit.
## Detail Endpoints
When retrieving specific records by ID, you can request up to **50 IDs** per call:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/permits?id=permit_123,permit_456,permit_789" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
The same limit applies to contractor lookups via `GET /v2/contractors?id=...`.
## Best Practices
* Set `size=100` on search endpoints to maximize records per request
* Batch detail lookups to avoid exceeding the 50 ID limit
* Handle pagination in your code to process complete datasets
* Store results locally to avoid re-fetching the same data
## Related Articles
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
* [Understanding query parameters](/docs/knowledge-base/api/basics/query-parameters)
# Understanding Query Parameters
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/query-parameters
Learn how to use query parameters in the Shovels API to filter and refine your permit and contractor searches.
Query parameters are additional details you include in your API request to specify the data you want to retrieve. They help refine your query to get more relevant results.
## Common Query Parameters
### Geographic Filtering
The `geo_id` parameter enables precise searching by:
* State (e.g., `CA`, `TX`, `FL`)
* Zip code (e.g., `94103`)
* City or county
* Specific address
### Date Filtering
Filter permits by date ranges:
* `permit_from` - Start date for permit search
* `permit_to` - End date for permit search
### Pagination
Control result sets:
* `size` - Number of records per page (default: 10, max: 100)
* `cursor` - For cursor-based pagination
## Example Requests
### Search permits in California from 2024
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/permits/search?geo_id=CA&permit_from=2024-01-01&permit_to=2024-12-31&size=100" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
### Search contractors by name
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/contractors/search?name=Smith" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
The `geo_id` parameter is a unified geography filter. The same permit search works whether you pass a state abbreviation, zip code, or individual address geo\_id—you're just swapping out the geography level.
## Using Filters Efficiently
To optimize your API responses:
1. Apply date filters to narrow results
2. Use property type filters when relevant
3. Start with broader geography, then narrow down
4. Use the `/list/*` endpoints to get valid values for filter parameters
## Learn More
* [API Reference](/api-reference)
* [Path vs query parameters](/docs/knowledge-base/api/basics/api-endpoints)
# What Are the Shovels API Rate Limits?
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/rate-limits
Learn about the Shovels API rate limits that protect system stability and ensure fair access for all users.
**API rate limits restrict how quickly you can make requests** to ensure system stability and fair access for all users.
**Rate limits vs. credit limits:** Rate limits restrict how quickly you can make requests (a technical concept). Credit limits track how many records you can consume based on your plan (a billing concept). This page covers rate limits. See [API Credit Limits](/docs/knowledge-base/api/basics/credit-limits) for credit limit details.
## Current Rate Limits
We expect that you will respect our platform and avoid frivolous requests. We are constantly monitoring usage and will enforce rate limits on an individual basis as needed.
If you receive a **429 Too Many Requests** response, slow down your request rate and retry after a brief delay.
## Best Practices
* **Space out requests** — avoid sending many requests simultaneously
* **Implement backoff** — when you receive a 429 response, wait before retrying
* **Use pagination efficiently** — fetch larger pages with the `size` parameter instead of many small requests
* **Cache results locally** — store fetched data to avoid re-fetching the same records
## If You Believe You're Being Rate Limited
If you feel that your API key is being rate limited unexpectedly, please reach out to [support@shovels.ai](mailto:support@shovels.ai) for clarification.
## Related Articles
* [API Credit Limits](/docs/knowledge-base/api/basics/credit-limits)
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
* [API Introduction](/docs/shovels-api-introduction)
# How Do API Credits Work?
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/request-counts
The Shovels API uses a record-based credit system. Each record returned counts against your credits—a search returning 100 permits uses 100 credits, while a single permit lookup uses 1.
**The Shovels API uses a record-based credit system.** Each record returned by an API call counts against your credits. A permit search that returns 100 results uses 100 credits, while a detail lookup for a single permit uses 1 credit.
## How Credits Are Counted
Your credits are based on **records returned**, not the number of API calls you make:
* A search returning **100 permits** = **100 credits used**
* A search returning **50 contractors** = **50 credits used**
* A detail lookup for **1 permit** = **1 credit used**
* A detail lookup for **3 contractors** = **3 credits used**
This applies to all endpoints:
* Search endpoints (`GET /v2/permits/search`, `GET /v2/contractors/search`)
* Detail endpoints (`GET /v2/permits`, `GET /v2/contractors`)
## Practical Examples
| Request | Records Returned | Credits Used |
| ------------------------------------ | ---------------- | ------------ |
| `GET /v2/permits/search?size=100` | 100 permits | 100 |
| `GET /v2/permits/search?size=100` | 47 permits | 47 |
| `GET /v2/permits?id=123` | 1 permit | 1 |
| `GET /v2/contractors/search?size=50` | 50 contractors | 50 |
| `GET /v2/contractors?id=abc,def,ghi` | 3 contractors | 3 |
This applies to every plan, including Free. There is no separate request-based metering.
## Managing Your Credits
1. **Use filters effectively** - Narrow your searches to return only the records you need
2. **Use search endpoints** - They include full details in the response, avoiding extra lookups
3. **Cache results locally** - Store fetched data to avoid re-fetching the same records
## Tracking Your Usage
### Via API
Use the `GET /v2/usage` endpoint to check your credit consumption:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET "https://api.shovels.ai/v2/usage" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
Response (paid plan example):
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"credits_used": 1500,
"credit_limit": 10000
}
```
### Via Response Headers
Every API response includes credit tracking headers:
| Header | Description |
| --------------------- | ---------------------------------------------- |
| `X-Credits-Request` | Credits consumed by this request |
| `X-Credits-Limit` | Your total credit limit (omitted if unlimited) |
| `X-Credits-Remaining` | Credits remaining (omitted if unlimited) |
### Via your account page
You can also view credit usage on the **Manage subscription** tab at [app.shovels.ai/account](https://app.shovels.ai/account).
Credits reset monthly on your subscription or upgrade date.
## Related Articles
* [API credit limits](/docs/knowledge-base/api/basics/credit-limits)
* [Permits per API call](/docs/knowledge-base/api/basics/permits-per-call)
# How to Verify Your API Request Was Successful
Source: https://docs.shovels.ai/docs/knowledge-base/api/basics/successful-requests
Learn how to determine if your Shovels API request succeeded by checking HTTP status codes and response data.
A successful API request returns a **200 status code** along with the requested data.
## Success Response
When your request succeeds, you'll receive:
* HTTP status code: `200 OK`
* Response body with your requested data
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"items": [...],
"size": 10,
"next_cursor": "...",
"total_count": { "value": 150, "relation": "eq" }
}
```
`total_count` is returned only when you pass `include_count=true` on the first page (no cursor). `relation` is `eq` for an exact count, or `gte` when the true count exceeds the 10,000 cap.
## Error Responses
If there are issues, you'll receive an error code indicating the problem:
| Status Code | Meaning |
| ----------- | ------------------------------------------ |
| 200 | Success (may have empty results) |
| 400 | Bad request - check your parameters |
| 401 | Unauthorized - check your API key |
| 422 | Unprocessable - missing required parameter |
| 429 | Rate limit exceeded |
| 500 | Server error - try again later |
## Empty Results vs Errors
A 200 response with an empty `items` array is **not an error**. It means:
* The address or geo\_id is valid
* No permits match your query criteria
This commonly happens when:
* The address has no permits in our system
* Your date filters exclude all results
* The geo\_id format is valid but doesn't match any records
## Troubleshooting
1. **Check your API key** - Ensure it's correctly included in the header
2. **Verify required parameters** - Most endpoints require a geo\_id
3. **Review date formats** - Use YYYY-MM-DD format
4. **Check the response body** - Error messages describe what's wrong
## Related Articles
* [Error handling](/docs/knowledge-base/api/errors/error-handling)
* [422 error explained](/docs/knowledge-base/api/errors/422-error)
# How to Search for Contractors
Source: https://docs.shovels.ai/docs/knowledge-base/api/contractors/contractor-search
Learn the difference between permit search and contractor search in the Shovels API, and how to find contractor contact information and permit history.
Contractor search returns B2B contact information, license data, and classification data about contractors who have pulled permits.
## Permit Search vs Contractor Search
| Search Type | Returns |
| --------------------- | -------------------------------------------------------------------------- |
| **Permit Search** | Permits pulled at specific addresses in a geographic area |
| **Contractor Search** | Contact information, license data, and classification data for contractors |
Not every permit has an associated contractor. This depends on whether the jurisdiction requires contractor information on permit applications, and whether the contractor provides it.
## Searching for Contractors
`/contractors/search` requires `geo_id`, `permit_from`, and `permit_to` on every request. The date range filters to contractors who pulled permits within that window.
### By Name
Add the `contractor_name` filter (minimum 3 characters) to search by full or partial name:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Full name search
curl -X GET \
"https://api.shovels.ai/v2/contractors/search?geo_id=CA&permit_from=2024-01-01&permit_to=2024-12-31&contractor_name=John+Smith+Panels" \
-H "X-API-Key: YOUR_API_KEY_HERE"
# Partial name search
curl -X GET \
"https://api.shovels.ai/v2/contractors/search?geo_id=CA&permit_from=2024-01-01&permit_to=2024-12-31&contractor_name=Smith" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
### By Geography
Search for contractors in a specific area:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/contractors/search?geo_id=94103&permit_from=2024-01-01&permit_to=2024-12-31" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
## Fuzzy Matching
* **Address lookup** is fuzzy
* **Contractor lookup** is exact, or you can search by part of the name
For example, searching for "John Smith Panels" you can search by (minimum 3 characters):
* "John"
* "Joh"
* "Panels"
## Contractor Information Available
* Business name and DBA
* Contact information (phone, email, address)
* License numbers and status
* Permit history and counts
* Performance metrics
## Related Articles
* [Employee data](/docs/knowledge-base/api/contractors/employee-data)
* [Service area lookup](/docs/knowledge-base/api/contractors/service-area-lookup)
* [Filtering by company size](/docs/knowledge-base/api/contractors/filtering-by-size)
# How to Get Contractor Employee Information
Source: https://docs.shovels.ai/docs/knowledge-base/api/contractors/employee-data
Learn how to use the Shovels API to retrieve contractor employee contact details including business and personal emails, job titles, and seniority levels.
The Shovels API provides a dedicated endpoint to retrieve employee information for specific contractors, including contact details, job titles, and seniority levels.
## Using the Employees Endpoint
**Endpoint:** `GET /contractors/{id}/employees`
### Required Parameters
* `{id}` - The contractor ID (path parameter)
### Optional Parameters
* `cursor` - For pagination through large result sets
* `size` - Number of items per page (1-100, default: 10)
## Sample Request
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl --request GET \
--url https://api.shovels.ai/v2/contractors/{id}/employees \
--header 'X-API-Key: YOUR_API_KEY_HERE'
```
## Response Data
### Contact Information
* `email` - Email address
* `business_email` - Business email address
* `phone` - Phone number
* `linkedin_url` - LinkedIn profile
### Professional Details
* `job_title` - Current position
* `seniority_level` - Senior, Mid, Junior, etc.
* `department` - Department or division
### Additional Data
* `name` - Full name
* Address details (street, city, state, zip)
* Demographics (age range, income range, etc.)
## Scaling Your Data Collection
### Pagination
Use cursor-based pagination for large employee lists. Check for `next_cursor` in the response to retrieve subsequent pages.
### Bulk Processing
To process multiple contractors at scale:
1. Obtain contractor IDs using the contractors search endpoints
2. Iterate through each contractor ID
3. Call the employees endpoint for each contractor
4. Implement proper error handling and respect rate limits
### Targeting Decision Makers
Filter results by `seniority_level` (Senior, Executive) and `job_title` to identify:
* Owners
* Sales directors
* Project managers
* Other key personnel
## Best Practices
* Respect rate limits and avoid frivolous requests
* Use pagination to handle large datasets efficiently
* Store and cache results to minimize API calls
* Filter by job titles and seniority levels to focus on decision-makers
* Always include proper error handling in your implementation
## Related Articles
* [Contractor search](/docs/knowledge-base/api/contractors/contractor-search)
* [Service area lookup](/docs/knowledge-base/api/contractors/service-area-lookup)
* [API Reference: Contractor Employees](/api-reference/contractors/get-contractor-employees)
# How to Filter Contractors by Company Size
Source: https://docs.shovels.ai/docs/knowledge-base/api/contractors/filtering-by-size
Learn how to use employee count ranges in the Shovels API to filter contractors by company size for targeted outreach.
The Shovels API provides employee count data for contractors, allowing you to filter by company size.
## Employee Count in Response Data
When querying contractor endpoints, you'll receive employee count ranges in the response payload, enabling filtering by:
* Small businesses (fewer than 10 employees)
* Mid-size companies (10-100 employees)
* Enterprise-level contractors (100+ employees)
## How Company Size Is Determined
Our size classifications are determined through multiple data points:
* Business registrations
* Permit frequency
* Other proprietary indicators
## Use Cases
Filtering by company size is useful for:
* **Sales teams** segmenting outreach by company size
* **Marketing** targeting specific business sizes
* **Market analysis** understanding contractor landscape
## Example Workflow
1. Search for contractors in your target geography
2. Review employee count data in the response
3. Filter results based on your size requirements
4. Focus outreach on your target segment
Employee count data coverage varies. Not all contractors have this information available.
## Related Articles
* [Contractor search](/docs/knowledge-base/api/contractors/contractor-search)
* [Employee data](/docs/knowledge-base/api/contractors/employee-data)
* [Identifying independent contractors](/docs/knowledge-base/api/contractors/independent-contractors)
# How to Identify Independent Contractors
Source: https://docs.shovels.ai/docs/knowledge-base/api/contractors/independent-contractors
Learn reliable methods to identify sole proprietors and individual contractors in the Shovels API data.
Identifying independent contractors (ICs) in the Shovels API can be accomplished in several ways.
## Primary Method: business\_type Field
The most reliable method is through the `business_type` field, though it has limited coverage across our dataset.
## Heuristic Indicators
As a practical heuristic, consider these signals:
### Business Name Analysis
If a business name appears to be a person's name (e.g., "John Smith" rather than "Smith Construction"), it often indicates an independent contractor.
### Other Signals
* **Single contact person** across all permits
* **Low permit volume** compared to larger firms
* **Absence of multiple employee records**
## Why It Matters
Independent contractors represent a significant portion of the contracting ecosystem, especially in specialized trades like:
* Electrical work
* Plumbing
* HVAC
Understanding which contractors are independent professionals versus larger companies allows for:
* More tailored marketing approaches
* Accurate market segmentation
* Better targeting for products or services
The `business_type` field provides the most accurate identification when available, but coverage varies across the dataset.
## Related Articles
* [Contractor search](/docs/knowledge-base/api/contractors/contractor-search)
* [Filtering by company size](/docs/knowledge-base/api/contractors/filtering-by-size)
# How to Find a Contractor's Service Area
Source: https://docs.shovels.ai/docs/knowledge-base/api/contractors/service-area-lookup
Use the Shovels API to identify all cities and zip codes where a contractor has worked by analyzing their permit history.
Rather than relying on self-reported service areas, you can use a contractor's actual permit history to map their complete service footprint across cities and zip codes.
## Overview
By analyzing permit data, you can determine:
* All cities where a contractor has worked
* All zip codes in their service area
* Geographic coverage patterns over time
## Step-by-Step Process
### Step 1: Get Permits for a Contractor
Use the `/v2/contractors/{id}/permits` endpoint to retrieve all permits associated with a specific contractor:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
'https://api.shovels.ai/v2/contractors/YOUR_CONTRACTOR_ID/permits' \
-H 'X-API-Key: YOUR_API_KEY_HERE'
```
You need the contractor's `id` first. Get it from the [contractor search](/docs/knowledge-base/api/contractors/contractor-search) endpoint. Note that `/permits/search` has no `contractor_id` filter—this dedicated endpoint is the way to pull a single contractor's permits.
### Step 2: Extract Geographic Data
From each permit record, extract:
* City/jurisdiction names
* Zip codes
* Any additional geographic identifiers
### Step 3: Create Unique Lists
Process the permit data to create deduplicated lists of:
* All cities where the contractor has worked
* All zip codes in their service area
* Geographic coverage patterns over time
## Best Practices
1. **Filter by Recency** - The endpoint returns the contractor's full permit history; filter client-side on permit dates (e.g. last 2-3 years) for active service areas
2. **Handle Pagination** - Make sure to handle paginated responses to get complete permit histories
3. **Filter by Property Type** - Use each permit's `property_type` field to understand different service area patterns (residential vs commercial)
## Geographic Filtering Capabilities
The `geo_id` system enables precise searching by:
* Address
* Zip code
* Jurisdiction
* City
* County
This makes location-based queries powerful for understanding contractor coverage.
## Alternative: Shovels Online
In Shovels Online, we show recent permits for each contractor. You can:
* Search at the county, city, or address level
* Download permit and contractor data to CSV
* View contractor profiles with permit history
## Related Articles
* [Contractor search](/docs/knowledge-base/api/contractors/contractor-search)
* [Employee data](/docs/knowledge-base/api/contractors/employee-data)
* [API Reference](/api-reference)
# Where Decisions Are Available
Source: https://docs.shovels.ai/docs/knowledge-base/api/decisions/decisions-availability
Which Shovels plans include Decisions, how decisions coverage compares to permits, and where to learn more.
Shovels Decisions are available through the API to every account. This article covers plan access and coverage.
Decisions are currently in **beta**. For the full story behind the dataset and what's ahead, read the [Decisions API launch announcement](https://www.shovels.ai/blog/decisions-api-launch/).
## Plan Access
| Plan | Decisions Access |
| ------------------ | -------------------------------------------------------- |
| **Free plan** | Included—query the decisions endpoints with your API key |
| **API paid plans** | Included at no additional charge |
If you have an API key, you can query the [Search Decisions](/docs/knowledge-base/api/decisions/searching-decisions) and [Get Decisions By ID](/docs/knowledge-base/api/decisions/decisions-by-id) endpoints today.
## Coverage
At launch, Decisions covered 600+ cities and nearly 200,000 decision records, roughly half of the jurisdictions where Shovels has permit coverage. Coverage continues to expand toward parity with permit data.
Because coverage varies by jurisdiction, a geography with permit data may not yet have decision data. Check the [Coverage Dashboard](https://www.shovels.ai/coverage) for the latest.
## Other Access Methods
Decisions are available through the Shovels API. For questions about bulk or enterprise access, see the [launch announcement](https://www.shovels.ai/blog/decisions-api-launch/) or [contact sales](https://www.shovels.ai/contact).
## Related Articles
* [How to search for decisions](/docs/knowledge-base/api/decisions/searching-decisions)
* [Get decisions by ID](/docs/knowledge-base/api/decisions/decisions-by-id)
* [What are Shovels Decisions?](/docs/knowledge-base/data/decisions/overview)
# How to Get Decisions by ID
Source: https://docs.shovels.ai/docs/knowledge-base/api/decisions/decisions-by-id
Retrieve specific Shovels Decision records by their decision ID using the /decisions endpoint.
When you already know which decisions you want, use the [Get Decisions By ID](/api-reference/decisions/get-decisions-by-id) endpoint to fetch them directly—no date range or geo\_id required.
## Request
Pass one or more decision IDs in the `id` parameter:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/decisions?id=DECISION_ID_1&id=DECISION_ID_2" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
| Parameter | Description |
| --------- | ------------------------------------------------------- |
| `id` | One or more decision IDs. Up to **50 IDs** per request. |
## Behavior
* Unknown IDs are **silently omitted**—you won't get a per-ID 404. If you request 50 IDs and only 48 exist, the response contains 48 decisions.
* The response uses the same paginated envelope and decision fields as the search endpoint.
Decision IDs come from the [Search Decisions](/docs/knowledge-base/api/decisions/searching-decisions) endpoint. Store the `id` field from search results to look records up again later.
## Related Articles
* [How to search for decisions](/docs/knowledge-base/api/decisions/searching-decisions)
* [Where Decisions are available](/docs/knowledge-base/api/decisions/decisions-availability)
* [Decision data fields](/docs/knowledge-base/data/decisions/data-fields)
* [API Reference: Get Decisions By ID](/api-reference/decisions/get-decisions-by-id)
# How to Search for Decisions
Source: https://docs.shovels.ai/docs/knowledge-base/api/decisions/searching-decisions
Learn how to use the Shovels API to search zoning and land use decisions with the /decisions/search endpoint, including required parameters and filters.
To search Shovels Decisions, use the [Search Decisions](/api-reference/decisions/search-decisions) endpoint. Every request needs a date range and a `geo_id`, then you can narrow results with optional filters.
Decisions are currently in **beta**. For background on the dataset and roadmap, see the [Decisions API launch announcement](https://www.shovels.ai/blog/decisions-api-launch/).
## Required Parameters
Three parameters are required on every decisions search:
| Parameter | Description |
| --------------- | ----------------------------------------------------------------------------- |
| `decision_from` | Return decisions with a `decision_date` on or after this date (`YYYY-MM-DD`) |
| `decision_to` | Return decisions with a `decision_date` on or before this date (`YYYY-MM-DD`) |
| `geo_id` | The geographic area to search |
Omitting any of these returns a [422 error](/docs/knowledge-base/api/errors/422-error).
## Two-Step Process
### Step 1: Get a geo\_id
The `geo_id` accepts a US state code (such as `CA`) or a Shovels geolocation ID for an address, city, county, or jurisdiction. Resolve places to a geo\_id with one of:
* [Search Addresses](/api-reference/addresses/search-addresses)
* [Search Cities](/api-reference/cities/search-cities)
* [Search Counties](/api-reference/counties/search-counties)
* [Search Jurisdictions](/api-reference/jurisdictions/search-jurisdictions)
ZIP and ZIP+4 codes are **not** supported for decisions. The upstream source emits no ZIP information, so decisions cannot be filtered by ZIP. Free-form text such as `"123 Main St"` is also not accepted—resolve it to a geo\_id first.
### Step 2: Search Decisions Using the geo\_id
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/decisions/search?geo_id=CA&decision_from=2025-01-01&decision_to=2025-12-31" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
## Available Filters
All filters use AND logic—a decision must match every parameter you supply.
| Filter | Description |
| ------------------- | ----------------------------------------------------------------------------------- |
| `category` | One or more decision categories (e.g. Rezoning, Variance) |
| `subcategory` | One or more decision subcategories |
| `asset_class` | One or more asset classes (e.g. Residential, Commercial, Mixed-Use) |
| `property_type` | One or more property types |
| `min_project_value` | Minimum project value in **cents**, inclusive |
| `max_project_value` | Maximum project value in **cents**, inclusive |
| `decision_q` | Full-text query (max 100 characters) against the decision `title` and `description` |
`min_project_value` and `max_project_value` are expressed in cents. A \$5,000,000 project is `500000000`.
## Pagination
Search responses are paginated:
* `size` — results per page (1–100, default 10)
* `cursor` — pass the `next_cursor` from the previous response to fetch the next page
* `include_count` — set to `true` on the first page (no cursor) to include `total_count`. The count is exact up to 10,000; above that it is reported as a "greater than or equal to" value.
## Related Articles
* [Get decisions by ID](/docs/knowledge-base/api/decisions/decisions-by-id)
* [Where Decisions are available](/docs/knowledge-base/api/decisions/decisions-availability)
* [What are Shovels Decisions?](/docs/knowledge-base/data/decisions/overview)
* [Decision categories explained](/docs/knowledge-base/data/decisions/decision-categories)
* [API Reference: Search Decisions](/api-reference/decisions/search-decisions)
# What Does a 422 Error Mean in the Shovels API?
Source: https://docs.shovels.ai/docs/knowledge-base/api/errors/422-error
A 422 error means a required parameter is missing. Usually, you need to resolve your address to a geo_id first using the Address Search endpoint.
**A 422 error in the Shovels API means a required parameter is missing from your request.** The most common cause is querying permits without first resolving your address to a geo\_id. Solution: Use the Address Search endpoint to get a geo\_id, then use that geo\_id in your permit query.
## Common Causes
### Missing geo\_id
If you query permits without including a geo\_id:
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"detail": "Invalid geolocation ID value. Have you resolved your address into Shovels geolocation ID?"
}
```
### Invalid geo\_id Format
If the geo\_id format is incorrect or wasn't obtained through address resolution.
## How to Fix
1. **Resolve the address first** - Use the Address Search API to get a valid geo\_id
2. **Check required parameters** - Review the API documentation for required fields
3. **Review the error message** - The JSON output helps identify what's missing
## Example Workflow
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# WRONG: Querying permits without resolving address first
GET /v2/permits/search?street=123+Main+St # Will return 422
# RIGHT: Resolve address, then query permits
GET /v2/addresses/search?q=123+Main+St,+San+Francisco,+CA
# Get geo_id from response, then:
GET /v2/permits/search?geo_id=RESOLVED_GEO_ID
```
Always check the error response body—it tells you exactly what's missing or invalid.
## Full Documentation
For complete 422 error details including all location types and error examples, see [API Introduction - 422 Unprocessable Entity](/docs/shovels-api-introduction#422-unprocessable-entity).
## Related Articles
* [Error handling](/docs/knowledge-base/api/errors/error-handling)
* [Resolving addresses](/docs/knowledge-base/api/address-resolution/resolving-addresses)
* [API documentation](/api-reference)
# API Error Handling
Source: https://docs.shovels.ai/docs/knowledge-base/api/errors/error-handling
Quick guide to handling Shovels API errors. For complete details, see the API Introduction documentation.
**For comprehensive error handling documentation, see the [API Introduction - Error Handling](/docs/shovels-api-introduction#error-handling) section.** This article covers the most common scenarios.
## Most Common Issue: 422 Errors
A 422 error typically means you're missing a required parameter—usually a `geo_id`. **Solution:** Resolve your address first using the Address Search endpoint, then use the returned geo\_id in your permit query.
See [422 error explained](/docs/knowledge-base/api/errors/422-error) for details.
## Empty Results vs Errors
An empty `items` array with a 200 response is **not an error**—it means your query was valid but no matching data exists.
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"items": [],
"size": 10,
"next_cursor": null,
"total_count": { "value": 0, "relation": "eq" }
}
```
## Quick Reference
| Code | Meaning | Action |
| ---- | ------------------------- | ---------------------------------------------------------- |
| 200 | Success (may be empty) | Check `items` array |
| 400 | Bad request | Review request format |
| 401 | Unauthorized | Check API key |
| 422 | Missing/invalid parameter | [See 422 guide](/docs/knowledge-base/api/errors/422-error) |
| 500 | Server error | Contact [support@shovels.ai](mailto:support@shovels.ai) |
## Full Documentation
For complete error handling details, response code explanations, and troubleshooting tips, see:
* [API Introduction - Error Handling](/docs/shovels-api-introduction#error-handling)
* [API Introduction - Troubleshooting](/docs/shovels-api-introduction#troubleshooting)
## Related Articles
* [422 error explained](/docs/knowledge-base/api/errors/422-error)
* [Resolving addresses](/docs/knowledge-base/api/address-resolution/resolving-addresses)
# How to Search for Permits
Source: https://docs.shovels.ai/docs/knowledge-base/api/permits/permit-search
Learn how to use the Shovels API to search for building permits using geo_id, including step-by-step instructions and best practices.
To search for permits in the Shovels API, you'll use the `geo_id` parameter as the unique identifier linking addresses to their permit data.
## Two-Step Process
### Step 1: Get the Address's geo\_id
Use the [Search Addresses](/api-reference/addresses/search-addresses) endpoint with your target address:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/addresses/search?q=123+Main+St,+San+Francisco,+CA" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
This returns the address's `geo_id`—a unique identifier you'll need for the next step.
### Step 2: Search Permits Using the geo\_id
Use the [Search Permits](/api-reference/permits/search-permits) endpoint with the `geo_id` and your desired date range:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/permits/search?geo_id=ADDRESS_GEO_ID&permit_from=2020-01-01&permit_to=2024-12-31" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
The `geo_id` parameter is a unified geography filter. The same permit search works whether you pass a state abbreviation (`CA`), a zip code (`94103`), or an individual address's geo\_id. You're just swapping out the geography level.
## Available Filters
The permit search endpoint supports additional filters:
* **Date ranges** (`permit_from`, `permit_to`)
* **Permit types** and subtypes
* **Property types** (residential, commercial)
* **Contractor information**
* **Tags** for specific permit categories
## Alternative: Shovels Online
For quick lookups without code, search by address directly in [Shovels Online](https://app.shovels.ai): on the Permits tab, choose **Address** under the location filter and start typing. Address search is available on permit searches only.
## Best Practices
1. Always resolve addresses first to get accurate geo\_ids
2. Use date ranges to limit result sets
3. Apply filters to get exactly the permits you need
4. Handle pagination for large result sets
## Related Articles
* [Resolving addresses](/docs/knowledge-base/api/address-resolution/resolving-addresses)
* [Permits by state](/docs/knowledge-base/api/permits/permits-by-state)
* [API Reference: Search Permits](/api-reference/permits/search-permits)
# How to Search Permits by State
Source: https://docs.shovels.ai/docs/knowledge-base/api/permits/permits-by-state
Learn how to search for building permits in a specific US state using the Shovels API with 2-letter state abbreviations.
Use the 2-letter state abbreviation as the `geo_id` to search for permits in a specific state.
## State Abbreviation Format
Use standard 2-letter US state abbreviations:
* California = `CA`
* Texas = `TX`
* Florida = `FL`
* New York = `NY`
## Example Request
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/permits/search?geo_id=CA&permit_from=2024-01-01&permit_to=2024-12-31&size=100" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
## Narrowing Your Search
State-level searches can return many results. Consider adding filters:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Add property type filter
?geo_id=CA&property_type=residential
# Add permit type filter
?geo_id=CA&permit_tags=solar
# Combine with date range
?geo_id=CA&permit_from=2024-01-01&permit_to=2024-03-31
```
State-level searches may return large result sets. Use pagination and filters to manage the data effectively.
## Geography Levels
The same `geo_id` parameter works at multiple levels:
| Level | Example |
| -------- | ---------------------------- |
| State | `geo_id=CA` |
| Zip Code | `geo_id=94103` |
| City | Use city-specific geo\_id |
| Address | Use address-specific geo\_id |
## Related Articles
* [How to search for permits](/docs/knowledge-base/api/permits/permit-search)
* [Understanding query parameters](/docs/knowledge-base/api/basics/query-parameters)
* [API Reference](/api-reference)
# Finding Properties With No Permit on Record
Source: https://docs.shovels.ai/docs/knowledge-base/api/properties/absence-queries
Use absence filters on the Shovels /properties/search endpoint to find properties with no permit of a given type, and read the trust fields that score every absence answer.
The most valuable property question is often about what **hasn't** happened: which homes have no solar permit on record, which buildings were never re-roofed. The [Search Properties](/api-reference/properties/search-properties) endpoint answers this with a `-` prefix on `permit_tags`—and attaches a confidence score to every answer.
Properties are currently in **beta**. The absence-trust surface in particular may still change in response to how the endpoint is used in practice.
## Writing an Absence Query
Prefix a tag with `-` to exclude it:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/properties/search?geo_id=94103&permit_tags=-solar" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
This returns properties with no resolved solar permit on record. You can combine absence with other filters by repeating the key—for example `permit_tags=roofing&permit_tags=-solar` finds properties with a roofing permit but no solar permit. A comma-joined value like `permit_tags=roofing,-solar` is rejected with a [422](/docs/knowledge-base/api/errors/422-error).
Adding `permit_from` changes the meaning:
* In a **pure-absence** query (`permit_tags=-solar&permit_from=2020-01-01`), the exclusion means "no solar permit since that date."
* Combined with any **positive** filter, the exclusion means "never had a solar permit"—per-filter dates can't be composed on a property record.
## Why Absence Answers Need a Confidence Score
A property can look permit-free for reasons that have nothing to do with the property:
* Its permits never linked to an address (they exist, but unresolved).
* The work is recent and the permit simply hasn't arrived yet—permits reach Shovels a median of **84 days** after work starts (90% within 188 days).
* The local jurisdiction's coverage for that work type is thin.
Answered naively, "no permit here" would be confidently wrong. So the endpoint does two things instead:
1. **Removes what can't be answered.** Where coverage for a work type in an area is too thin, those properties are dropped from the absence result entirely rather than returned as false negatives. The response reports how many scopes were removed (`suppressed_scopes`).
2. **Scores what remains.** Every returned absence row carries a `trust` object, and the page carries a `trust_summary`.
## Reading the Trust Fields
Each row in an absence result includes a `trust` object:
| Field | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `coverage_tier` | Permit coverage bucket for the row's jurisdiction: `high`, `medium`, or `low` |
| `unresolved_rate` | Share of that jurisdiction's permits of this type that never linked to an address (0–1) |
| `data_horizon` | The most recent date past which "no permit since D" is under-observed for this row. If your `permit_from` is later than this date, the answer is a guess about data that hasn't arrived yet |
| `horizon_basis` | How the horizon was estimated: `measured`, `pooled`, or `prior` |
| `trust_jurisdiction_basis` | Whether the trust join used the row's own jurisdiction (`own`), its ZIP's dominant one (`dominant`), or none |
| `trust_jurisdiction_error_bar` | Measured error rate of that join — `0` on an `own` basis, and the 6.13% ZIP-dominant estimate error on a `dominant` basis |
| `footprint_basis` | Whether coverage suppression could resolve this row's geography: `matched` or `unknown` |
| `flags` | Row-level caveats, e.g. `since_d_beyond_horizon`, `untagged_permits_present`, `trust_row_missing` |
The page-level `trust_summary` aggregates across returned rows:
| Field | Description |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `expected_miss_rate` | The headline number: the estimated probability that a returned "no permit" answer is actually wrong because the permit hasn't arrived yet |
| `rows_flagged` | Number of rows on the page carrying a trust flag |
| `row_weighted_unresolved_rate` | Row-weighted mean `unresolved_rate` across the page |
| `suppressed_scopes` | Number of coverage scopes removed from the result |
For lead generation, filter your list by trust: rows with `coverage_tier=high`, no flags, and a `data_horizon` covering your date give the most reliable "no permit on record" answers.
Shovels knows permits, not installations. "No solar permit on record" is not the same claim as "no solar panels"—unpermitted work exists. The trust fields quantify data coverage, not construction reality.
## What Absence Queries Can't Do
* **"No permit before 2020"** or any closed date window—the property record keeps only the latest date per work type. Only "ever" and "since date D" are expressible. Use [Search Permits](/api-reference/permits/search-permits) instead.
* **Presence-only searches carry no trust fields.** The `trust` object and `trust_summary` appear only when the query includes an absence filter.
## Related Articles
* [How to search for properties](/docs/knowledge-base/api/properties/property-search)
* [Properties vs Permits: which endpoint?](/docs/knowledge-base/api/properties/properties-vs-permits)
* [Absence searches from the CLI](/docs/knowledge-base/cli/absence-and-trust) — Same surface, with per-page `trust_summaries`
* [Why am I getting so few results?](/docs/knowledge-base/data/quality/few-results)
* [API Reference: Search Properties](/api-reference/properties/search-properties)
# Get Properties by ID
Source: https://docs.shovels.ai/docs/knowledge-base/api/properties/properties-by-id
Fetch full property records for up to 50 known property IDs in one call with the Shovels API /properties endpoint.
If you already hold property IDs, fetch their full records with the [Get Properties By ID](/api-reference/properties/get-properties-by-id) endpoint. Provide up to **50** `id` parameters in one call.
Properties are currently in **beta**. Query parameters and response fields may still change in response to how the endpoint is used in practice.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/properties?id=FIRST_PROPERTY_ID&id=SECOND_PROPERTY_ID" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
## How It Behaves
* A property's `id` is its **address geolocation ID**—the same `id` returned by [Search Properties](/api-reference/properties/search-properties) and [Search Addresses](/api-reference/addresses/search-addresses).
* Results come back **in request order**.
* A valid address ID with no property behind it is simply omitted from the response—there is no per-ID 404. If you request 50 IDs and get 48 rows back, two IDs weren't found.
* A string that can't be **decoded** as an address ID is a different case: it fails the whole request with a [422 error](/docs/knowledge-base/api/errors/422-error), naming the offending value and its position.
* A non-address geolocation ID (city, county, or jurisdiction) is likewise rejected with a [422 error](/docs/knowledge-base/api/errors/422-error).
Only *unknown* IDs degrade gracefully. A single malformed or non-address ID costs you the entire batch, so validate IDs before sending 50 of them.
Rows returned here carry **no** `trust` object—the absence-trust surface exists only on [Search Properties](/api-reference/properties/search-properties).
Looking up a property by its assessor parcel number (APN)? APN is returned on every record for mapping into your own systems, but it is not searchable—APNs are county-specific, millions of them collide across counties, and roughly 30% of properties have none. Resolve the address to a `geo_id` instead.
## Related Articles
* [How to search for properties](/docs/knowledge-base/api/properties/property-search)
* [Resolving addresses](/docs/knowledge-base/api/address-resolution/resolving-addresses)
* [Getting a geo\_id](/docs/knowledge-base/api/address-resolution/geo-id-lookup)
* [API Reference: Get Properties By ID](/api-reference/properties/get-properties-by-id)
# Properties vs Permits: Which Endpoint?
Source: https://docs.shovels.ai/docs/knowledge-base/api/properties/properties-vs-permits
When to use the Shovels /properties endpoints and when to use /permits/search, including the queries properties deliberately don't support.
Properties and Permits expose the same underlying data in two shapes. Reach for **Properties** when the property is the unit of your answer; reach for **Permits** when an individual permit and its full timeline is.
| You want to | Use |
| ------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| Build a list of properties by location, owner, type, or attributes | [Search Properties](/api-reference/properties/search-properties) |
| Ask "which properties have (or lack) work of type X" | [Search Properties](/api-reference/properties/search-properties) |
| Pull full records for property IDs you already hold | [Get Properties By ID](/api-reference/properties/get-properties-by-id) |
| Ask anything with an upper date bound or a date window | [Search Permits](/api-reference/permits/search-permits) |
| See every individual permit and its full history | [Search Permits](/api-reference/permits/search-permits) |
| Scope by parcel number (APN) or by permitting jurisdiction | [Search Permits](/api-reference/permits/search-permits) |
## What Properties Deliberately Won't Answer
A property record keeps only the **latest** permit date per work type. Some questions can't be answered correctly from that summary, so the API rejects them rather than returning a wrong answer. Each of them is an exact question on `/permits/search`, which holds every individual permit.
| Query | Why it's not supported |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| "Permits before 2020" (`permit_to`, any upper bound) | Older permits hide behind newer ones—a property whose latest roofing permit is 2023 may or may not also have one from 2019 |
| "No solar before 2020" | Two properties whose latest solar permit is 2023 are indistinguishable, even if one also had solar in 2016 |
| "No roofing between 2018 and 2021" | A permit inside the window is hidden by a later one. Only "ever" and "since date D" are expressible |
| "Has solar since 2022 **and** no roofing since 2022" | Only one date can be carried per query—in a mixed query, the exclusion means "never" |
| Search by parcel number (APN) | APNs are county-specific, roughly 9.5M collide across counties, and \~30% of properties have none. APN is returned on every record for mapping, just not searchable |
| Scope by permitting jurisdiction | Jurisdiction is recorded on a minority of property records, so scoping to it would silently drop properties that were never permitted |
These aren't roadmap gaps—each is a question the property-level summary cannot answer correctly, so the API refuses it instead of returning a silently wrong result.
## Related Articles
* [How to search for properties](/docs/knowledge-base/api/properties/property-search)
* [How to search for permits](/docs/knowledge-base/api/permits/permit-search)
* [Finding properties with no permit on record](/docs/knowledge-base/api/properties/absence-queries)
* [API Reference: Search Properties](/api-reference/properties/search-properties)
# How to Search for Properties
Source: https://docs.shovels.ai/docs/knowledge-base/api/properties/property-search
Learn how to use the Shovels API to search US properties with the /properties/search endpoint, including required parameters, filters, and pagination.
To search Shovels Properties, use the [Search Properties](/api-reference/properties/search-properties) endpoint. Each result is one property with its permit history already summarized on the record—permit counts, work-type tags, latest activity dates, contractors, and job values—so you don't have to stitch individual permits together yourself.
Properties are currently in **beta**. Query parameters, response fields, and the absence-trust surface may still change in response to how the endpoint is used in practice.
## Required Parameters
Every search needs a scope: a location **or** an owner (or both).
| Parameter | Description |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `geo_id` | A 2-letter state code, 5-digit ZIP, ZIP+4, or a Shovels geolocation ID for a state, county, city, or address |
| `legal_owner` | A property owner name, matched on its canonical form. Pass up to 10 `legal_owner` parameters. Without a `geo_id`, this searches the owner's properties nationwide |
Omitting both returns a [422 error](/docs/knowledge-base/api/errors/422-error). Jurisdiction geo\_ids are not accepted—see [Properties vs Permits](/docs/knowledge-base/api/properties/properties-vs-permits) for why.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/properties/search?geo_id=94103&permit_tags=solar" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
To turn a typed address into a `geo_id`, call [Search Addresses](/api-reference/addresses/search-addresses) first. See [Resolving addresses](/docs/knowledge-base/api/address-resolution/resolving-addresses).
## Permit Filters
| Filter | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `permit_tags` | Canonical tags, one key per tag (`?permit_tags=roofing&permit_tags=-solar`). A positive tag keeps properties with that work type on some permit; a `-` prefix (e.g. `-solar`) keeps properties with **no** permit of that type on record—see [Absence search](/docs/knowledge-base/api/properties/absence-queries). Several positive tags require **every** tag, but not all on the same permit—matching is at address grain, a deliberate divergence from [Search Permits](/api-reference/permits/search-permits) |
| `permit_status` | Statuses, one key per status (`?permit_status=final&permit_status=active`): `final`, `in_review`, `inactive`, `active`. On its own it keeps properties with a permit in any of the named statuses; combined with positive tags, the tag and status must appear on the same permit. `unknown` is not filterable |
| `permit_from` | ISO date (`YYYY-MM-DD`). Positive filters match since this date; a pure-absence exclusion means "none since this date" |
| `permit_tags_unfinaled` | Keeps properties with an unfinaled permit of each named tag, one key per tag—see [Unfinaled permits](/docs/knowledge-base/api/properties/unfinaled-permits) |
These three filters take **repeated keys**, not one comma-joined value. `permit_tags=solar,-roofing` is rejected with a [422](/docs/knowledge-base/api/errors/422-error) naming the bad token—send `permit_tags=solar&permit_tags=-roofing` instead.
An unrecognized tag is also a 422 rather than an empty result, so a typo fails loudly here. [Search Permits](/api-reference/permits/search-permits) returns 200 with zero items for the same typo, which is easy to misread as "no such properties."
`permit_to` (and any other upper date bound) is **not supported** on properties. The property record keeps only the latest permit date per work type, so a closed date window can't be answered correctly. Use [Search Permits](/api-reference/permits/search-permits) for date-window queries.
With multiple positive tags, each tag must be present on the property but they may come from different permits. This differs from `/permits/search`, where multiple tags must appear on the same permit.
## Property Attribute Filters
| Filter | Description |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `property_type` | Repeatable: `residential`, `commercial`, `industrial`, `agricultural`, `vacant land`, `exempt`, `miscellaneous`, `office`, `recreational` |
| `property_min_market_value` / `property_max_market_value` | Assessed market value range in **cents** (dollars × 100) |
| `property_min_lot_size` / `property_max_lot_size` | Lot size range in square feet |
| `property_min_building_area` / `property_max_building_area` | Building area range in square feet |
| `property_min_unit_count` / `property_max_unit_count` | Unit count range |
| `property_min_year_built` / `property_max_year_built` | Year built range |
Attribute data (value, size, year, units, type) is available for roughly 60–70% of properties. A property with no value for an attribute never matches a range filter on it, so attribute filters narrow results to the covered set.
## Pagination
Results are sorted most recently permitted first.
* `size` — results per page (1–100, default 10)
* `cursor` — pass the `next_cursor` from the previous response to fetch the next page
* `include_total_count` — set to `true` on the first page (no cursor) to include `total_count`. The count is exact up to 10,000; above that it is reported as a "greater than or equal to" value.
## Related Articles
* [Get properties by ID](/docs/knowledge-base/api/properties/properties-by-id)
* [Finding properties with no permit on record](/docs/knowledge-base/api/properties/absence-queries)
* [Finding unfinaled permits](/docs/knowledge-base/api/properties/unfinaled-permits)
* [Properties vs Permits: which endpoint?](/docs/knowledge-base/api/properties/properties-vs-permits)
* [API Reference: Search Properties](/api-reference/properties/search-properties)
# Finding Properties With Unfinaled Permits
Source: https://docs.shovels.ai/docs/knowledge-base/api/properties/unfinaled-permits
Use the permit_tags_unfinaled filter on the Shovels /properties/search endpoint to find properties where a permit was pulled but never finaled.
A permit that was pulled but never finaled often signals an opportunity: a job that was opened and left unfinished—for example, a solar installer that went out of business mid-project. The `permit_tags_unfinaled` filter on [Search Properties](/api-reference/properties/search-properties) finds these properties directly.
Properties are currently in **beta**. Query parameters and response fields may still change in response to how the endpoint is used in practice.
## Usage
Pass one or more canonical tags, one key per tag (`?permit_tags_unfinaled=solar&permit_tags_unfinaled=roofing`). Each named tag must have an unfinaled permit at the property:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET \
"https://api.shovels.ai/v2/properties/search?geo_id=94103&permit_tags_unfinaled=solar" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
With `permit_from`, the tag's latest unfinaled permit must be on or after that date.
Each property record also includes `last_unfinaled_date_by_tag`, a map of work type to the latest unfinaled permit date, so you can see when the unfinished work was started.
## How "Unfinaled" Is Determined
The filter reads the permit's **status**, not its dates. That distinction matters:
* A permit with `final` status carries no `final_date` about 15% of the time, so "missing final date" is not evidence of unfinished work.
* Roughly 41% of permits with no `final_date` are in fact finaled by status. A date-based test would be wrong that often.
A work type counts as unfinaled at a property only when its **latest non-final permit is more recent than its latest final permit** of that type (equal dates count as finaled). This removes the false positives a plain "any non-final permit" test would keep—a property that re-roofed in 2015 (unfinaled) and again in 2022 (finaled) is not an unfinished roofing job.
A permit whose status is unknown counts as neither final nor unfinaled.
For what each permit status means (`active`, `in_review`, `inactive`, `final`), see [Permit statuses](/docs/knowledge-base/data/permits/permit-statuses).
## Related Articles
* [How to search for properties](/docs/knowledge-base/api/properties/property-search)
* [Permit statuses explained](/docs/knowledge-base/data/permits/permit-statuses)
* [How is project completion determined?](/docs/knowledge-base/data/permits/project-completion)
* [API Reference: Search Properties](/api-reference/properties/search-properties)
# How Do I Run Absence Searches in the CLI?
Source: https://docs.shovels.ai/docs/knowledge-base/cli/absence-and-trust
Find properties with no permit of a given type using the Shovels CLI, and read the per-row trust object and per-page meta.trust_summaries array that score every absence answer.
**Prefix a permit tag with `-` to find properties that have no permit of that type on record.** Because a property can look permit-free due to data coverage rather than reality, every absence answer carries trust metadata scoring how far to believe it.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties search --geo-id 92024 --permit-tags "-solar" --limit 10
```
You can combine absence with presence — `--permit-tags "roofing,-solar"` finds properties with a roofing permit but no solar permit.
Properties are in **beta**, and the absence-trust surface in particular may still change in response to how it's used in practice.
## How --permit-from Changes the Meaning
| Query | Meaning |
| --------------------------------------------------------- | ------------------------------------------- |
| `--permit-tags "-solar"` | Never had a solar permit |
| `--permit-tags "-solar" --permit-from 2020-01-01` | No solar permit **since** that date |
| `--permit-tags "roofing,-solar" --permit-from 2020-01-01` | Roofing since 2020, and **never** any solar |
Once any positive filter is present, the exclusion reverts to "never" — a property record carries only one date per work type, so per-filter dates can't be composed.
There is no `--permit-to`. "No roofing between 2018 and 2021" and "no solar before 2020" are not expressible, because a permit inside a closed window is hidden by a later one. Only "ever" and "since date D" work. Use `shovels permits search` for windows.
## Reading the Per-Row trust Object
Each absence row carries a `trust` object:
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"unresolved_rate": 0.0769,
"coverage_tier": "high",
"data_horizon": "2026-01-12",
"horizon_basis": "pooled",
"trust_jurisdiction_basis": "own",
"trust_jurisdiction_error_bar": 0.0,
"footprint_basis": "matched",
"flags": ["untagged_permits_present"]
}
```
| Field | Description |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `coverage_tier` | Permit coverage bucket for the row's jurisdiction: `high`, `medium`, or `low` |
| `unresolved_rate` | Share of that jurisdiction's permits of this tag that never linked to an address (0-1) |
| `data_horizon` | The most recent date past which "no permit since D" is under-observed. If your `--permit-from` is later than this, the answer is a guess about data that hasn't arrived yet |
| `horizon_basis` | How the horizon was estimated: `measured`, `pooled`, or `prior` |
| `trust_jurisdiction_basis` | Whether the trust join used the row's own jurisdiction (`own`), its ZIP's dominant one (`dominant`), or none |
| `trust_jurisdiction_error_bar` | Measured error rate of that join — `0` on an `own` basis, and the 6.13% ZIP-dominant estimate error on a `dominant` basis |
| `footprint_basis` | Whether coverage suppression could resolve the row's geography: `matched` or `unknown` |
| `flags` | Row-level caveats, e.g. `since_d_beyond_horizon`, `untagged_permits_present`, `trust_row_missing` |
For lead generation, filter to rows with `coverage_tier == "high"`, an empty `flags` array, a `data_horizon` at or after your `--permit-from`, and `trust_jurisdiction_basis == "own"`:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties search --geo-id CA --permit-tags "-solar" --limit all \
| jq '.data[] | select(.trust.coverage_tier == "high"
and (.trust.flags | length) == 0
and .trust.trust_jurisdiction_basis == "own")'
```
## meta.trust\_summaries Is an Array, One Entry Per API Page
This is the one place CLI output differs structurally from the REST API. The API returns a single `trust_summary` per page, scoped to that page's rows. The CLI's `--limit` merges pages — so it collects each page's summary into a **`meta.trust_summaries` array** rather than combining them:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties search --geo-id 92024 --permit-tags "-solar" --limit 205
```
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"meta": {
"count": 205,
"has_more": true,
"credits_used": 205,
"trust_summaries": [
{ "rows_flagged": 95, "row_weighted_unresolved_rate": 0.0416, "expected_miss_rate": 0.0416, "suppressed_scopes": 0 },
{ "rows_flagged": 100, "row_weighted_unresolved_rate": 0.0320, "expected_miss_rate": 0.0320, "suppressed_scopes": 0 },
{ "rows_flagged": 5, "row_weighted_unresolved_rate": 0.0320, "expected_miss_rate": 0.0320, "suppressed_scopes": 0 }
]
}
}
```
205 records came back as three API pages (100 + 100 + 5), so there are three summaries.
**The CLI does not aggregate these, and you shouldn't average them either.** Each rate is row-weighted over its own page, so a plain mean across pages of unequal size is wrong. The CLI has no basis on which to re-derive a correct merged figure, so it hands you the raw per-page values instead of inventing one.
| Field | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `expected_miss_rate` | The headline number: estimated probability that a returned "no permit" answer is wrong because the permit hasn't arrived yet |
| `rows_flagged` | Rows on that page carrying a trust flag |
| `row_weighted_unresolved_rate` | Row-weighted mean `unresolved_rate` across that page |
| `suppressed_scopes` | Coverage scopes removed from the result by suppression |
Read the worst page rather than the average when you need one number to act on:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties search --geo-id 92024 --permit-tags "-solar" --limit 205 \
| jq '[.meta.trust_summaries[].expected_miss_rate] | max'
```
An index in the array is **not** a page number: pages that carry no summary contribute no entry.
## Presence-Only Searches Carry No Trust Surface
Drop the `-` and both the row-level `trust` object and `meta.trust_summaries` disappear entirely:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
$ shovels properties search --geo-id 92024 --permit-tags solar --limit 1
{"data":[{"id":"...", "...": "..."}],"meta":{"count":1,"has_more":true,"credits_used":1}}
```
The key is omitted rather than returned empty, so in a script test for presence before reading it:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
jq 'if .meta.trust_summaries then [.meta.trust_summaries[].expected_miss_rate] | max else null end'
```
## What Suppression Does to Your Result
Where coverage for a work type in an area is too thin to answer honestly, those properties are **dropped from the result entirely** rather than returned as false negatives. `suppressed_scopes` reports how many scopes were removed.
This means an absence search can legitimately return fewer rows — or zero rows — in a low-coverage area, while the same query returns plenty in a well-covered one. That's suppression working, not a bug.
Shovels knows permits, not installations. "No solar permit on record" is not the same claim as "no solar panels" — unpermitted work exists. Trust fields quantify data coverage, not construction reality.
## Related Articles
* [Querying properties from the CLI](/docs/knowledge-base/cli/properties) — Full command and flag reference
* [Finding properties with no permit on record](/docs/knowledge-base/api/properties/absence-queries) — The same surface via the REST API
* [CLI output and pagination](/docs/knowledge-base/cli/output-and-pagination) — How `--limit` assembles pages
* [Why am I getting so few results?](/docs/knowledge-base/data/quality/few-results) — Coverage and suppression
# How Does CLI Authentication Work?
Source: https://docs.shovels.ai/docs/knowledge-base/cli/authentication
The Shovels CLI authenticates using your API key, provided via environment variable or config file. Learn how to configure and manage your credentials.
**The CLI uses the same API key as the Shovels REST API.** You can provide it through an environment variable or a persistent config file.
## Authentication Priority
The CLI checks for your API key in this order (first match wins):
1. `SHOVELS_API_KEY` environment variable
2. Config file at `~/.config/shovels/config.yaml`
## Option A: Environment Variable
Set your API key for the current session:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
export SHOVELS_API_KEY=your-api-key
```
This is useful for CI/CD pipelines, Docker containers, or temporary sessions.
## Option B: Config File (Recommended)
Save your API key persistently:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels config set api-key your-api-key
```
This creates or updates `~/.config/shovels/config.yaml`:
```yaml theme={"theme":{"light":"css-variables","dark":"css-variables"}}
api_key: your-api-key
```
The config file location follows the [XDG Base Directory specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html), so it respects `$XDG_CONFIG_HOME` if set.
## Verify Your Configuration
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels config show
```
This displays your resolved configuration with the API key partially masked for security.
## Additional Config Options
The config file supports these settings:
| Key | Description | Default |
| --------------- | --------------------- | --------------------------- |
| `api_key` | Your Shovels API key | (none) |
| `base_url` | API base URL override | `https://api.shovels.ai/v2` |
| `default_limit` | Default result limit | `50` |
Set any option with the `config set` command:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels config set base-url https://api.shovels.ai/v2
```
## Authentication Errors
If your API key is missing or invalid, the CLI exits with code `2` and prints a JSON error to stderr:
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"error": "API key is required. Set SHOVELS_API_KEY or run: shovels config set api-key YOUR_KEY",
"code": 2,
"error_type": "auth_error"
}
```
Some commands don't require authentication: `version`, `config show`, and `usage`. Geographic search commands (`cities search`, `counties search`, etc.) and `tags list` are also credit-free but require a valid API key.
## Getting an API Key
If you don't have an API key yet:
1. [Create a free Shovels account](https://app.shovels.ai/login?mode=register)
2. Open the [**API key** tab](https://app.shovels.ai/account?tab=apikey)
3. Copy your API key from the **API Key** field
Free accounts include a monthly credit allowance to explore the platform—see [shovels.ai/pricing](https://www.shovels.ai/pricing).
## Related Articles
* [How to access your API key](/docs/knowledge-base/api/basics/api-key-access) — Find your key in the Shovels dashboard
* [CLI installation](/docs/knowledge-base/cli/installation) — Install the CLI binary
* [CLI error codes](/docs/knowledge-base/cli/error-codes) — Understanding exit codes and error types
# What Commands Are Available in the CLI?
Source: https://docs.shovels.ai/docs/knowledge-base/cli/commands-overview
A complete overview of Shovels CLI commands for searching permits, contractors, addresses, and geographic data from your terminal.
**The CLI organizes commands into groups: permits, properties, contractors, decisions, addresses, geographic lookups, tags, schema, usage, and config.** Every command outputs JSON to stdout and supports `--help` for detailed usage.
## Command Reference
### permits
Search and retrieve building permit data.
| Subcommand | Description | Key Flags |
| ---------------- | --------------------------------------------------- | --------------------------------------------------------------------------- |
| `permits search` | Search permits by location, date, tags, and filters | `--geo-id` (required), `--permit-from` (required), `--permit-to` (required) |
| `permits get` | Retrieve 1-50 permits by ID | Positional IDs |
**Example: Search permits**
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels permits search \
--geo-id 92024 \
--permit-from 2024-01-01 \
--permit-to 2024-12-31 \
--tags solar \
--property-type residential \
--limit 50
```
### properties
Search properties with their permit history rolled up onto each record. Added in **v0.8.0**.
| Subcommand | Description | Key Flags |
| ------------------- | -------------------------------------------------------- | ------------------------------------------------ |
| `properties search` | Search properties by geographic scope and/or legal owner | `--geo-id` **or** `--legal-owner` (at least one) |
| `properties get` | Retrieve 1-50 properties by address ID | Positional IDs |
**Example: Properties with no solar permit**
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties search \
--geo-id 92024 \
--permit-tags "-solar" \
--property-type residential \
--limit 10
```
Properties are in **beta**. Unlike `permits search`, there is no `--permit-to` flag and jurisdiction geo\_ids are rejected. See [Querying properties from the CLI](/docs/knowledge-base/cli/properties).
### contractors
Search contractors and access their permits, employees, and metrics.
| Subcommand | Description | Key Flags |
| ----------------------- | ------------------------------------------ | ------------------------------------------------------------------------- |
| `contractors search` | Search contractors by location and filters | `--geo-id` (required) |
| `contractors get` | Retrieve 1-50 contractors by ID | Positional IDs |
| `contractors permits` | List permits filed by a contractor | Contractor ID (positional) |
| `contractors employees` | List employees of a contractor | Contractor ID (positional) |
| `contractors metrics` | Monthly performance metrics | `--metric-from`, `--metric-to`, `--property-type`, `--tag` (all required) |
**Example: Find electrical contractors in Austin**
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels contractors search \
--geo-id 78701 \
--permit-from 2024-01-01 \
--permit-to 2024-12-31 \
--tags electrical \
--min-permits 10
```
Contractor search supports state, county, city, jurisdiction, and ZIP code geo\_ids, but **not** address-level geo\_ids. Use `permits search` for address-level queries.
### decisions
Search municipal zoning and land-use decisions. Added in **v0.7.0**.
| Subcommand | Description | Key Flags |
| ------------------ | ------------------------------------------------------ | -------------------------------------------------- |
| `decisions search` | Search decisions by location, date range, and category | `--decision-from`, `--decision-to` (both required) |
| `decisions get` | Retrieve decisions by ID | Positional IDs |
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels decisions search --geo-id CA \
--decision-from 2024-01-01 --decision-to 2024-12-31 \
--category Rezoning --asset-class Residential
```
### addresses
Search for addresses to resolve geo\_ids, plus resident and metrics lookups.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels addresses search -q "1600 Pennsylvania Ave"
```
Returns matching addresses with their `geo_id`, formatted name, and coordinates. Also supports `addresses residents` and `addresses metrics current|monthly`.
### cities, counties, jurisdictions, states, zipcodes
Resolve geographic names to geo\_ids for use in search commands.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels cities search -q "Miami Beach"
shovels counties search -q "Los Angeles"
shovels jurisdictions search -q "San Francisco"
shovels states search -q "CA"
shovels zipcodes search -q "941"
```
`states search` matches on the 2-letter abbreviation (`CA`), not the full state name. `zipcodes search` matches on ZIP prefix and spans states, so filter the results if you need one state.
Each group also exposes a `coverage` subcommand reporting which permit fields are reliably populated for that area. The geo\_id is a **positional** argument here, and both dates are required. Cities, counties, and jurisdictions additionally support `metrics current` and `metrics monthly`.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels cities coverage Q2l0eXxGTHxNaWFtaSBCZWFjaA \
--coverage-from 2024-01-01 --coverage-to 2024-12-31
```
### schema
Print the annotated JSON response schema for any data command — **offline, with no API call and no API key required**.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels schema # List all available command paths
shovels schema properties search # Full schema for one command
```
Each schema gives `response_fields` (type, description, unit, range, enum), `meta_fields` for commands whose meta carries more than the standard keys, a jq-ready `field_index`, and `filters` mapping each CLI flag to its type. Nested objects appear as dotted paths like `trust.coverage_tier`.
This is the cheapest way for an AI agent to learn a command's output shape before spending credits. Any data command also accepts `--schema` to print its own: `shovels properties search --schema`.
### tags
List available permit tags (work type classifications).
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels tags list
```
Returns tags like `solar`, `hvac`, `roofing`, `electrical`, `pool_spa`, `new_dwelling`, `kitchen_remodel`, and more. Use these values with the `--tags` flag in search commands.
### usage
Check your API credit usage and limits.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels usage
```
### config
Manage persistent CLI settings.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels config set api-key your-key # Save API key
shovels config show # Display current config
```
### version
Print CLI version, git commit, and build date.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels version
```
## Common Search Filters
These flags are available on `permits search` and `contractors search`. `properties search` names and scopes several of them differently — see [Querying properties from the CLI](/docs/knowledge-base/cli/properties):
| Difference | `permits` / `contractors` | `properties` |
| --------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Tag flag | `--tags`, repeatable or comma-separated | `--permit-tags`, same shape. Multiple positive tags match at address grain, not on one shared permit |
| Unknown tag | Returns zero rows | Rejected with a 422 naming the tag |
| Date range | `--permit-from` **and** `--permit-to`, both required | `--permit-from` only; no upper bound exists |
| Scope | `--geo-id` required | `--geo-id` **or** `--legal-owner` |
| Attribute prefix | `--min-market-value` | `--property-min-market-value` |
| Jurisdiction geo\_ids | Accepted by `permits search` | Rejected |
### Tag Filters
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
--tags solar # Include permits tagged "solar"
--tags solar,roofing # Include solar OR roofing
--tags -electrical # Exclude electrical permits
```
Tags support a `-` prefix for exclusion. Mix includes and excludes as needed.
### Property Filters
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
--property-type residential
--min-market-value 500000
--min-building-area 2000
--min-lot-size 5000
--min-story-count 2
--min-unit-count 4
```
### Permit Filters
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
--status final # final, active, in_review, inactive
--min-job-value 100000
--min-fees 500
--min-approval-duration 30 # days
--min-construction-duration 90
--min-inspection-pr 80 # pass rate percentage
--has-contractor # only permits with a linked contractor
-q "solar panel" # text search in permit description
```
### Contractor Filters (contractor search only)
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
--contractor-name "Smith"
--contractor-license "ABC123"
--classification general,electrical
--min-permits 50
--min-job-value 1000000
```
## Global Flags
These flags apply to all commands:
| Flag | Description | Default |
| ----------------- | ----------------------------------------------------------------------------------- | ----------------- |
| `--limit` | Max records to return (1-100000 or `all`) | `50` |
| `--max-records` | Cap when using `--limit all` | `10000` |
| `--include-count` | Include total result count in response | `false` |
| `--base-url` | Override API endpoint | Config or default |
| `--no-retry` | Disable automatic retry on rate limits | `false` |
| `--timeout` | Per-request timeout (Go duration format) | `30s` |
| `--dry-run` | Print the resolved HTTP request as JSON without calling the API or spending credits | `false` |
| `--schema` | Print this command's annotated response schema (offline, no auth) | `false` |
`--dry-run` and `--schema` both short-circuit before any network call, so they're free. Use `--dry-run` to confirm how your flags map to query parameters, and `--schema` to learn the response shape.
## Getting Help
Every command supports `--help`:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels --help # Top-level help
shovels permits --help # Permits group help
shovels permits search --help # Detailed search flags and examples
```
## Related Articles
* [CLI quickstart guide](/docs/shovels-cli-quickstart) — First query in under a minute
* [Querying properties from the CLI](/docs/knowledge-base/cli/properties) — The `properties` command group
* [CLI absence searches and trust fields](/docs/knowledge-base/cli/absence-and-trust) — Finding properties with no permit on record
* [Output format and pagination](/docs/knowledge-base/cli/output-and-pagination) — Understanding JSON responses
* [Scripting and AI agents](/docs/knowledge-base/cli/scripting-and-agents) — Composing CLI commands into workflows
# What Do CLI Exit Codes and Errors Mean?
Source: https://docs.shovels.ai/docs/knowledge-base/cli/error-codes
The Shovels CLI uses structured exit codes and JSON error messages to make troubleshooting straightforward for developers, scripts, and AI agents.
**The CLI uses distinct exit codes for each error category.** Scripts and AI agents can branch on the exit code without parsing the error message.
## Exit Codes
| Code | Type | Meaning |
| ---- | -------------------- | ------------------------------------------------- |
| `0` | Success | Command completed successfully |
| `1` | Client error | Invalid flags, validation failure, or bad request |
| `2` | Auth error | Missing or invalid API key |
| `3` | Rate limited | Too many requests (HTTP 429) |
| `4` | Credits exhausted | API credit limit reached (HTTP 402) |
| `5` | Server/network error | Server failure or connectivity issue |
## Error Response Format
Errors are written to **stderr** as structured JSON:
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"error": "descriptive message explaining what went wrong",
"code": 2,
"error_type": "auth_error"
}
```
The `error_type` field provides a machine-readable classification:
| error\_type | Exit Code | Common Causes |
| ------------------ | --------- | --------------------------------------------------- |
| `client_error` | 1 | Missing required flags, invalid flag values |
| `validation_error` | 1 | Invalid date range, bad geo\_id format (HTTP 422) |
| `auth_error` | 2 | No API key configured, or key is invalid (HTTP 401) |
| `rate_limited` | 3 | Too many concurrent requests (HTTP 429) |
| `credit_exhausted` | 4 | Monthly credit limit reached (HTTP 402) |
| `server_error` | 5 | Shovels API returned a 5xx error |
| `network_error` | 5 | DNS failure, timeout, or connection refused |
## Common Errors and Solutions
### Exit 1: Client Error
**Missing required flags:**
```
$ shovels permits search --geo-id 92024
Error: required flags "permit-from", "permit-to" not set
```
Fix: Add the required `--permit-from` and `--permit-to` flags.
**Invalid date range:**
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"error": "permit_from must be before permit_to",
"code": 1,
"error_type": "validation_error"
}
```
Fix: Ensure your `--permit-from` date is earlier than `--permit-to`. Use `YYYY-MM-DD` format.
**Properties search validation:**
`properties search` validates locally before making any API call, so these cost no credits:
| Error | Cause |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `at least one of --geo-id or --legal-owner required` | No scope given |
| `--legal-owner values must not be empty` | An empty string was passed to `--legal-owner` |
| `maximum 10 --legal-owner values per request, got 11` | More than 10 owners |
| `invalid date format for --permit-from: "08-2026" (expected YYYY-MM-DD)` | Malformed date |
| `--property-min-lot-size must not be negative, got -5` | Negative range bound |
| `--property-min-year-built (2000) must not exceed --property-max-year-built (1990)` | Inverted range pair |
| `unknown flag: --permit-to` | `properties search` has no upper date bound — use `permits search` |
Auth is checked **before** local flag validation, so a missing API key surfaces as exit `2` even when your flags are also invalid. Fix the key first, then the flags.
**Properties get ID errors:**
An ID that is well-formed but has no property behind it is *not* an error — the row is omitted and the ID appears in `meta.missing` with exit `0`. Two cases do fail the whole request with exit `1`:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
$ shovels properties get BJjCWAMtccQ NOT_A_REAL_ID
{"error":"...Could not decode property id 'NOT_A_REAL_ID' at position 2. Each id must be an ADDRESS geolocation id...","code":1,"error_type":"validation_error"}
$ shovels properties get RMjg6rIIh2k
{"error":"...Property id 'RMjg6rIIh2k' at position 0 is a city, county, or jurisdiction id, not an ADDRESS id...","code":1,"error_type":"validation_error"}
```
Fix: pass only address IDs, from a `properties search` row's `id` or from `addresses search`. One bad ID takes down the entire batch, so validate before sending 50.
### Exit 2: Auth Error
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"error": "API key is required. Set SHOVELS_API_KEY or run: shovels config set api-key YOUR_KEY",
"code": 2,
"error_type": "auth_error"
}
```
Fix: Set your API key via environment variable or config file. See [CLI authentication](/docs/knowledge-base/cli/authentication).
### Exit 3: Rate Limited
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"error": "rate limited after 3 retries",
"code": 3,
"error_type": "rate_limited"
}
```
The CLI automatically retries rate-limited requests with exponential backoff and jitter (up to 3 retries). If this error appears, wait a moment and try again, or contact support if it persists.
Use `--no-retry` to disable automatic retry if you want to handle rate limits yourself in a script.
### Exit 4: Credits Exhausted
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"error": "credit limit reached",
"code": 4,
"error_type": "credit_exhausted"
}
```
Your monthly credit allowance is used up. Check your usage with `shovels usage` and contact [sales@shovels.ai](mailto:sales@shovels.ai) to increase your limit.
### Exit 5: Server or Network Error
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"error": "connection timeout after 30s",
"code": 5,
"error_type": "network_error"
}
```
Check your internet connection. If the Shovels API is down, try again later. You can adjust the timeout with `--timeout 60s`.
## Using Exit Codes in Scripts
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels permits search --geo-id 92024 \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
> results.json 2> error.json
case $? in
0) echo "Success: $(jq '.meta.count' results.json) records" ;;
2) echo "Auth failed — check your API key" ;;
3) echo "Rate limited — retrying in 60s"; sleep 60 ;;
4) echo "Out of credits — contact sales" ;;
*) echo "Error: $(cat error.json)" ;;
esac
```
## Related Articles
* [CLI authentication](/docs/knowledge-base/cli/authentication) — Configure your API key
* [Querying properties from the CLI](/docs/knowledge-base/cli/properties) — Properties flags and ID rules
* [API error handling](/docs/knowledge-base/api/errors/error-handling) — REST API error codes
* [API credit limits](/docs/knowledge-base/api/basics/credit-limits) — Understanding credit consumption
# How Do I Install the Shovels CLI?
Source: https://docs.shovels.ai/docs/knowledge-base/cli/installation
Install the Shovels CLI on macOS, Linux, or Windows using the install script or by downloading binaries directly from GitHub Releases.
**The fastest way to install is the one-line install script.** It detects your platform, downloads the correct binary, and verifies the SHA256 checksum.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -LsSf https://shovels.ai/install.sh | sh
```
The binary installs to `~/.shovels/bin` by default.
## Supported Platforms
| OS | Architecture | Release Asset |
| ------- | --------------------- | --------------------------------------- |
| macOS | Apple Silicon (arm64) | `shovels__darwin_arm64.tar.gz` |
| macOS | Intel (amd64) | `shovels__darwin_amd64.tar.gz` |
| Linux | arm64 | `shovels__linux_arm64.tar.gz` |
| Linux | amd64 | `shovels__linux_amd64.tar.gz` |
| Windows | amd64 | `shovels__windows_amd64.zip` |
Releases ship as compressed archives containing the `shovels` binary — for example `shovels_0.8.3_darwin_arm64.tar.gz`.
## Install Script Options
The install script supports environment variables for customization:
| Variable | Description | Default |
| --------------------- | --------------------------------- | ---------------- |
| `SHOVELS_VERSION` | Pin to a specific release version | Latest |
| `SHOVELS_INSTALL_DIR` | Custom installation directory | `~/.shovels/bin` |
Example with custom options:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
SHOVELS_VERSION=0.1.0 SHOVELS_INSTALL_DIR=/usr/local/bin \
curl -LsSf https://shovels.ai/install.sh | sh
```
## Manual Download
Download binaries directly from [GitHub Releases](https://github.com/ShovelsAI/shovels-cli/releases/latest). Each release includes SHA256 checksums for verification.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Example: download and verify on macOS arm64
curl -LO https://github.com/ShovelsAI/shovels-cli/releases/download/v0.8.3/shovels_0.8.3_darwin_arm64.tar.gz
curl -LO https://github.com/ShovelsAI/shovels-cli/releases/download/v0.8.3/checksums.txt
sha256sum -c checksums.txt --ignore-missing
tar -xzf shovels_0.8.3_darwin_arm64.tar.gz
chmod +x shovels
mv shovels /usr/local/bin/shovels
```
## Add to PATH
If the CLI isn't found after installation, add the install directory to your `PATH`:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Add to ~/.zshrc (macOS) or ~/.bashrc (Linux)
export PATH="$HOME/.shovels/bin:$PATH"
```
Then reload your shell:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
source ~/.zshrc # or ~/.bashrc
```
## Verify Installation
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels version
```
This prints the CLI version, git commit hash, and build date.
## Updating
Re-run the install script to get the latest version:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -LsSf https://shovels.ai/install.sh | sh
```
The CLI is a single static binary with no runtime dependencies. Updating simply replaces the binary file.
## Related Articles
* [CLI quickstart guide](/docs/shovels-cli-quickstart) — Get up and running in under a minute
* [CLI authentication](/docs/knowledge-base/cli/authentication) — Configure your API key
* [How to access your API key](/docs/knowledge-base/api/basics/api-key-access) — Get an API key from your Shovels account
# How Does CLI Output and Pagination Work?
Source: https://docs.shovels.ai/docs/knowledge-base/cli/output-and-pagination
The Shovels CLI outputs JSON to stdout with automatic pagination. Learn about response formats, the --limit flag, and credit tracking.
**All CLI output is valid JSON written to stdout. Errors go to stderr.** This makes the CLI safe to pipe into `jq`, scripts, or other tools without worrying about mixed output.
## Response Format
### Paginated Responses (search commands)
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"data": [
{ "id": "...", "description": "...", "status": "final" }
],
"meta": {
"count": 50,
"has_more": true,
"credits_used": 1,
"credits_remaining": 9999
}
}
```
### Batch Responses (get commands)
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"data": [
{ "id": "ABC", "name": "..." },
{ "id": "DEF", "name": "..." }
],
"meta": {
"count": 2,
"missing": ["UNKNOWN_ID"],
"credits_used": 1,
"credits_remaining": 9999
}
}
```
The `missing` array lists any IDs that weren't found, and is **omitted entirely** when every requested ID resolved.
`data` is always an **array** on `get` commands, including when you request a single ID. Write `jq '.data[0]'` rather than `jq '.data'` so a one-ID lookup and a fifty-ID batch parse the same way.
### Credit Fields
`credits_remaining` appears only when your account has a credit limit. On uncapped plans the key is absent while `credits_used` is still reported, so test for it before reading it:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
jq '.meta.credits_remaining // "uncapped"'
```
## Pagination with --limit
The `--limit` flag controls how many records to return. The CLI handles cursor-based pagination internally — you never need to manage cursors yourself.
| Value | Behavior |
| ---------------------- | --------------------------------------------- |
| `--limit 50` (default) | Return up to 50 records |
| `--limit 500` | Return up to 500 records |
| `--limit all` | Fetch all records (capped by `--max-records`) |
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Get first 10 results
shovels permits search --geo-id 92024 \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--limit 10
# Get all results (up to 10,000 by default)
shovels permits search --geo-id 92024 \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--limit all
```
### --max-records
When using `--limit all`, the `--max-records` flag sets the upper bound (default: 10,000, maximum: 100,000):
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels permits search --geo-id CA \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--tags solar --limit all --max-records 50000
```
### --include-count
Add `--include-count` to include the total number of matching records in the response:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels permits search --geo-id 92024 \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--tags solar --include-count --limit 1
```
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"data": ["..."],
"meta": {
"count": 1,
"has_more": true,
"total_count": {
"value": 581,
"relation": "eq"
},
"credits_used": 1,
"credits_remaining": 9999
}
}
```
Total counts are exact up to 10,000 (`"relation": "eq"`). Above 10,000, the count is approximate (`"relation": "gte"` means "at least this many").
## Per-Page Metadata: meta.trust\_summaries
Most `meta` fields are aggregated across the pages `--limit` fetched. One is not.
Absence searches on `properties search` return a trust summary **per API page**, scoped to that page's rows. Since no single summary is correct for a merged result, the CLI collects them into a `meta.trust_summaries` array instead of combining them:
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"meta": {
"count": 205,
"has_more": true,
"credits_used": 205,
"trust_summaries": [
{ "rows_flagged": 95, "expected_miss_rate": 0.0416, "suppressed_scopes": 0 },
{ "rows_flagged": 100, "expected_miss_rate": 0.0320, "suppressed_scopes": 0 },
{ "rows_flagged": 5, "expected_miss_rate": 0.0320, "suppressed_scopes": 0 }
]
}
}
```
Three entries because 205 records arrived as three pages (100 + 100 + 5). The key is omitted entirely when a query has no absence filter. See [CLI absence searches and trust fields](/docs/knowledge-base/cli/absence-and-trust).
## Credit Tracking
Every response includes `credits_used` in the `meta` object, plus `credits_remaining` on plans with a credit limit. This lets you monitor usage without making a separate API call.
To check your overall credit status:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels usage
```
## Data Types in Responses
| Field Type | Format | Example |
| -------------------------------- | -------------------------------------------- | -------------------------- |
| Dates | ISO 8601 (`YYYY-MM-DD`) | `"2024-06-15"` |
| Money amounts | Integer cents | `150000` = \$1,500.00 |
| Ratings | Float 0-5 | `4.2` |
| Pass rates | Integer 0-100 | `85` (percentage) |
| Coordinates (permits, addresses) | `address.latlng` as `[lat, lng]` float array | `[33.071202, -117.300993]` |
| Coordinates (properties) | Separate `lat` and `long` floats | `33.066977`, `-117.247426` |
| geo\_ids | Base64-encoded string | `"Q2l0eXxGTHxNaWFtaQ"` |
| Rate fields (trust) | Float 0-1 | `0.0769` = 7.69% |
Run `shovels schema ` to get every field's type and unit for a specific command, offline and without spending credits.
## Related Articles
* [CLI commands overview](/docs/knowledge-base/cli/commands-overview) — Full list of commands and flags
* [CLI absence searches and trust fields](/docs/knowledge-base/cli/absence-and-trust) — Reading `meta.trust_summaries`
* [CLI error codes](/docs/knowledge-base/cli/error-codes) — Understanding error responses
* [Scripting and AI agents](/docs/knowledge-base/cli/scripting-and-agents) — Piping CLI output into workflows
# How Do I Query Properties from the CLI?
Source: https://docs.shovels.ai/docs/knowledge-base/cli/properties
Use shovels properties search and shovels properties get to query US properties with their permit history rolled up onto each record, including owner, attribute, and absence filters.
**The `properties` command group returns one row per property, with that property's permit history already rolled up onto the record.** A single row answers "what has happened at this address" — permit counts, work-type tags, latest activity dates, contractors, job values, and property attributes.
Added in CLI **v0.8.0**; **v0.8.1** or newer is required for the permit filters below. Run `shovels version` to check yours; upgrade with `curl -LsSf https://shovels.ai/install.sh | sh`.
Properties are currently in **beta**. Query parameters, response fields, and the absence-trust surface may still change. Treat the response shape as unstable.
## Subcommands
| Subcommand | Description | Key Flags |
| ------------------- | -------------------------------------------------------- | ------------------------------------------------ |
| `properties search` | Search properties by geographic scope and/or legal owner | `--geo-id` **or** `--legal-owner` (at least one) |
| `properties get` | Retrieve 1-50 properties by address ID | Positional IDs |
## Required Scope
Every search needs a scope: `--geo-id`, `--legal-owner`, or both. Omitting both fails locally before any API call:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
$ shovels properties search --permit-tags solar
{"error":"at least one of --geo-id or --legal-owner required","code":1,"error_type":"validation_error"}
```
### --geo-id
| Format | Example | Notes |
| ------------------------ | ------------- | ----------------------------------- |
| ZIP code | `92024` | Use directly |
| ZIP+4 | `92024-1234` | Use directly |
| State | `CA` | 2-letter code |
| City, county, or address | `RMjg6rIIh2k` | Resolve first with a search command |
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties search \
--geo-id "$(shovels counties search -q 'San Diego' | jq -r '.data[0].geo_id')" \
--permit-tags solar --permit-status final --limit 10
```
Jurisdiction geo\_ids are **rejected** by this endpoint. Jurisdiction is recorded on only a minority of property records, so scoping to it would silently drop properties that were never permitted. Scope by city, county, state, or address instead.
### --legal-owner
Repeat the flag for up to 10 owners. Values are **never split on commas**, so `"SMITH, JOHN"` is one owner, not two. Matching is on the owner's canonical form, so casing variants collapse together.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Owner portfolio nationwide — no geographic scope needed
shovels properties search --legal-owner "INVITATION HOMES" --include-count --limit 2
```
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"data": [
{
"id": "DWe15mBxY8Y",
"city": "HOFFMAN ESTATES",
"state": "IL",
"legal_owner": "Invitation Homes",
"owner_type": "company_owned",
"permit_count": 3
}
],
"meta": {
"count": 2,
"has_more": true,
"total_count": { "value": 151, "relation": "eq" },
"credits_used": 2
}
}
```
`--legal-owner` is the one filter that works nationwide with no location at all.
## Permit Filters
| Flag | Description |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--permit-tags` | Canonical tags — repeat the flag or comma-separate (e.g. `--permit-tags solar --permit-tags "-roofing"`, or `--permit-tags "solar,-roofing"`). A bare tag keeps properties that have it; a `-` prefix keeps properties **without** it. Several positive tags require **every** tag, though not all on the same permit |
| `--permit-status` | Repeat the flag or comma-separate: `final`, `in_review`, `inactive`, `active`. An invalid status is caught locally, before any API call |
| `--permit-from` | Binds the tag, status, and absence filters to this date (`YYYY-MM-DD`) |
| `--permit-tags-unfinaled` | Keeps properties with an **unfinaled** permit of each named tag — repeat the flag or comma-separate |
**There is no `--permit-to` flag.** A property record keeps only the latest permit date per work type, so a closed date window can't be answered correctly. Passing it fails as an unknown flag. Use `shovels permits search` for date windows and upper bounds.
An unrecognized tag is an **error**, not an empty result: the API returns a 422 naming the bad tag. Run `shovels tags list --limit all` for the canonical set. `permits search` behaves differently — it returns zero rows for an unknown tag, which is easy to misread as "no such permits."
Both forms produce the same request. Use `--dry-run` to confirm what your flags resolved to:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
$ shovels properties search --geo-id 92024 --permit-tags solar --permit-tags roofing --dry-run
{"method":"GET","url":"...","params":{"geo_id":"92024","permit_tags":["solar","roofing"],"size":50}}
```
These three filters need CLI **v0.8.1** or newer. In v0.8.0 they were single-valued: repeating one silently kept just the last value, and the comma-separated form it documented now fails against the current API. Run `shovels version` and upgrade if you are below 0.8.1.
### Finding unfinaled work
A permit that was pulled but never finaled often signals an unfinished job — for example an installer that went out of business mid-project.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties search --geo-id CA \
--permit-tags-unfinaled solar --permit-from 2024-01-01 --limit 2
```
Each row's `last_unfinaled_date_by_tag` shows when the unfinished work started:
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"id": "BApEVBRuBBM",
"city": "PISMO BEACH",
"last_unfinaled_date_by_tag": { "solar": "2026-08-16" }
}
```
"Unfinaled" is determined from permit **status**, not from a missing `final_date`. See [Finding unfinaled permits](/docs/knowledge-base/api/properties/unfinaled-permits) for why that distinction matters.
## Property Attribute Filters
Narrow by what the property **is** rather than what happened to it.
| Flag | Unit |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--property-type` | Repeatable or comma-separated: `residential`, `commercial`, `industrial`, `agricultural`, `vacant land`, `exempt`, `miscellaneous`, `office`, `recreational` |
| `--property-min-market-value` / `--property-max-market-value` | Integer **cents** (`50000000` = \$500,000) |
| `--property-min-lot-size` / `--property-max-lot-size` | Square feet |
| `--property-min-building-area` / `--property-max-building-area` | Square feet |
| `--property-min-unit-count` / `--property-max-unit-count` | Count |
| `--property-min-year-built` / `--property-max-year-built` | Year (e.g. `1990`) |
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Older residential homes worth $500k-$1M with no solar permit
shovels properties search --geo-id CA \
--permit-tags "-solar" \
--property-type residential \
--property-min-market-value 50000000 --property-max-market-value 100000000 \
--property-max-year-built 1989 \
--include-count --limit 10
```
Attribute data covers roughly **60-70%** of properties, and a property with no value for an attribute never matches a range filter on it. Every attribute filter narrows results to the covered set — stacking several can empty a result that the same query returns rows for without them.
`story_count` is returned on every record but there is **no** `--property-*-story-count` filter; the Properties API has no story-count parameter. `permits search` does filter on story count.
### Range validation
Negative and inverted bounds are caught locally, before any API call or credit spend:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
$ shovels properties search --geo-id 92024 --property-min-lot-size -5
{"error":"--property-min-lot-size must not be negative, got -5","code":1,"error_type":"validation_error"}
$ shovels properties search --geo-id 92024 \
--property-min-year-built 2000 --property-max-year-built 1990
{"error":"--property-min-year-built (2000) must not exceed --property-max-year-built (1990)","code":1,"error_type":"validation_error"}
```
## Absence Searches
Prefix a tag with `-` to find properties with **no** permit of that type on record. Every absence answer carries trust metadata scoring its reliability:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties search --geo-id 92024 --permit-tags "-solar" --limit 10
```
Absence is the highest-value property query and has its own article — see [CLI absence searches and trust fields](/docs/knowledge-base/cli/absence-and-trust).
## properties get
Fetch specific properties by address ID. Accepts 1-50 IDs as **positional arguments**, all in one request.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties get BJjCWAMtccQ DWe15mBxY8Y
```
ID is a positional argument, not a flag. `shovels properties get --id a_123` is wrong; `shovels properties get a_123` is right.
A property's ID is its **address** geolocation ID — the same `id` returned by `properties search` and `addresses search`:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels properties get "$(shovels addresses search -q '1966 Olivenhain Rd, Encinitas CA' | jq -r '.data[0].geo_id')"
```
`addresses search` exits `1` with `No addresses found.` when nothing matches, and the empty result then fails `properties get` with a decode error. In a script, check the resolved ID before passing it on.
### How get handles bad IDs
The three failure modes are distinct, and only one of them is survivable:
| Input | Behavior |
| ------------------------------------------- | -------------------------------------------------------------- |
| Valid address ID with no property behind it | Row omitted from `data`, ID listed in `meta.missing`, exit `0` |
| String that isn't a decodable address ID | **Whole request fails**, exit `1` |
| City, county, or jurisdiction geo\_id | **Whole request fails**, exit `1` |
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
$ shovels properties get BJjCWAMtccQ BJjCWAMtccX
{"data":[{"id":"BJjCWAMtccQ", "...": "..."}],"meta":{"count":1,"missing":["BJjCWAMtccX"],"credits_used":1}}
```
`meta.missing` is absent entirely when every requested ID resolved. But one undecodable ID takes down the whole batch:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
$ shovels properties get BJjCWAMtccQ NOT_A_REAL_ID
{"error":"...Could not decode property id 'NOT_A_REAL_ID' at position 2. Each id must be an ADDRESS geolocation id...","code":1,"error_type":"validation_error"}
```
Validate IDs before batching 50 of them. A single malformed ID costs you the entire request, while a merely-unknown ID costs you nothing.
Trust metadata is a **search-only** surface: rows returned by `properties get` carry no `trust` object, even though the generated schema lists the fields.
## Response Fields
`shovels schema properties search` prints all 45 response fields offline, with types and units, and needs no API key. The rollup fields are the ones with no equivalent in `permits search`:
| Field | Description |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `permit_count` / `untagged_permit_count` | Permits linked to the address; how many carry no canonical tag |
| `tags` / `statuses` | Distinct canonical tags and permit statuses across the address's permits |
| `tag_tally` | Map of tag to permit count |
| `tag_status_pairs` | Distinct `tag:status` pairs (status folds to `unknown` when null) |
| `last_permit_date` | Latest permit start date across all permits; `null` when never permitted |
| `last_date_by_tag` / `last_date_by_status` / `last_date_by_pair` | Latest permit date keyed by tag, by status, and by `tag:status` |
| `last_unfinaled_date_by_tag` | Latest non-final permit date per tag |
| `total_job_value` | Sum of job values across the property's permits, in **cents** |
| `contractor_count` | Distinct contractors across the address's permits |
| `apn` | Assessor parcel number — returned for mapping, **not searchable** |
`apn` is on every record so you can join into your own systems, but you can't search by it: APNs are county-specific, millions collide across counties, and roughly 30% of properties have none. Resolve the address to a `geo_id` instead.
## Dry Run
Add `--dry-run` to print the resolved HTTP request without calling the API or spending credits — useful for confirming how flags map to query parameters:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
$ shovels properties search --geo-id 92024 --permit-tags "-solar" --limit 10 --dry-run
{
"method": "GET",
"url": "https://api.shovels.ai/v2/properties/search",
"params": { "geo_id": "92024", "permit_tags": ["-solar"], "size": 10 }
}
```
## Related Articles
* [CLI absence searches and trust fields](/docs/knowledge-base/cli/absence-and-trust) — Reading `trust` and `meta.trust_summaries`
* [CLI commands overview](/docs/knowledge-base/cli/commands-overview) — Full list of commands and flags
* [How to search for properties](/docs/knowledge-base/api/properties/property-search) — The same data via the REST API
* [Properties vs Permits: which endpoint?](/docs/knowledge-base/api/properties/properties-vs-permits) — What properties deliberately won't answer
* [Finding unfinaled permits](/docs/knowledge-base/api/properties/unfinaled-permits) — How "unfinaled" is determined
# How Do I Use the CLI in Scripts and AI Agents?
Source: https://docs.shovels.ai/docs/knowledge-base/cli/scripting-and-agents
The Shovels CLI is designed for composability. Learn how to pipe output with jq, build shell scripts, automate with cron, and integrate with AI coding agents.
**The CLI follows Unix conventions: JSON to stdout, errors to stderr, meaningful exit codes.** This makes it composable with `jq`, shell scripts, cron jobs, and AI coding agents like Claude Code, Cursor, and Codex.
## Piping with jq
Since all output is valid JSON, use `jq` to extract, filter, and transform results:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Count total solar permits in a ZIP code
shovels permits search --geo-id 92024 \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--tags solar --include-count --limit 1 \
| jq '.meta.total_count.value'
# Extract just contractor names and phone numbers
shovels contractors search --geo-id 78701 \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--tags electrical --limit 100 \
| jq '.data[] | {name, phone: .primary_phone}'
# Export to CSV
shovels contractors search --geo-id CA \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--tags solar --limit all \
| jq -r '.data[] | [.name, .primary_phone, .permit_count] | @csv' \
> solar_contractors_ca.csv
```
## Shell Scripts
### Resolve a city name and search permits
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
#!/bin/bash
# find-permits.sh — Search permits in a city by name
city_name="$1"
tag="$2"
year="$3"
# Resolve city to geo_id
geo_id=$(shovels cities search -q "$city_name" \
| jq -r '.data[0].geo_id')
if [ -z "$geo_id" ] || [ "$geo_id" = "null" ]; then
echo "City not found: $city_name" >&2
exit 1
fi
# Search permits
shovels permits search \
--geo-id "$geo_id" \
--permit-from "${year}-01-01" \
--permit-to "${year}-12-31" \
--tags "$tag" \
--limit all
```
Usage:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
./find-permits.sh "Miami Beach" roofing 2024 | jq '.meta.count'
```
### Batch lookup contractors by ID
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
#!/bin/bash
# Given a file with one contractor ID per line, fetch details
while IFS= read -r id; do
shovels contractors get "$id" | jq '.data'
done < contractor_ids.txt
```
## Cron Jobs
Monitor permit activity on a weekly schedule:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# crontab -e
# Run every Monday at 8 AM
0 8 * * MON /path/to/weekly-permits.sh
```
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
#!/bin/bash
# weekly-permits.sh — Count permits filed in the last 7 days
from_date=$(date -v-7d +%Y-%m-%d) # macOS
to_date=$(date +%Y-%m-%d)
count=$(shovels permits search \
--geo-id 94110 \
--permit-from "$from_date" \
--permit-to "$to_date" \
--include-count --limit 1 \
| jq '.meta.total_count.value')
echo "$count permits filed in 94110 this week"
```
## AI Agent Integration
The CLI is built with an agent-first design. AI coding agents (Claude Code, Cursor, Codex, or custom agents) can:
1. **Read `--help`** to understand available commands and flags
2. **Run commands** and parse the JSON output
3. **Branch on exit codes** to handle errors programmatically
4. **Chain commands** to build multi-step research workflows
### How an AI agent uses the CLI
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Step 1: Agent reads the help to understand the tool
shovels permits search --help
# Step 2: Agent constructs and runs a query
shovels permits search --geo-id 94110 \
--permit-from 2024-06-01 --permit-to 2024-12-31 \
--tags roofing --property-type residential --limit 20
# Step 3: Agent parses the JSON response
# Step 4: Agent follows up with related queries
shovels contractors get CONTRACTOR_ID_FROM_RESULTS
```
The CLI help text is written to be clear and specific so that an LLM can construct the correct command on the first attempt. No human-oriented decorations (colors, spinners, progress bars) interfere with parsing.
### Why CLI over MCP for agents?
The CLI avoids common MCP pain points:
* **No context bloat** — the agent only reads what it requests
* **No credential juggling** — one API key in the environment
* **No host lock-in** — works with any agent that can run shell commands
* **No protocol overhead** — plain JSON in, plain JSON out
## Error Handling in Scripts
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
result=$(shovels permits search --geo-id 92024 \
--permit-from 2024-01-01 --permit-to 2024-12-31 2>/tmp/shovels-err.json)
if [ $? -ne 0 ]; then
error_type=$(jq -r '.error_type' /tmp/shovels-err.json)
case "$error_type" in
auth_error) echo "Check your API key" ;;
rate_limited) echo "Rate limited — waiting..." && sleep 60 ;;
credit_exhausted) echo "Out of credits" ;;
*) echo "Error: $(jq -r '.error' /tmp/shovels-err.json)" ;;
esac
exit 1
fi
echo "$result" | jq '.data | length'
```
## Related Articles
* [CLI commands overview](/docs/knowledge-base/cli/commands-overview) — Full list of commands and flags
* [CLI output and pagination](/docs/knowledge-base/cli/output-and-pagination) — Understanding JSON responses
* [CLI error codes](/docs/knowledge-base/cli/error-codes) — Exit codes reference
* [Automate API calls](/docs/knowledge-base/api/basics/automate-calls) — Automating with the REST API directly
# Shovels Contact Information
Source: https://docs.shovels.ai/docs/knowledge-base/company/contact-information
Find Shovels' official contact information including mailing address, support email, and sales contacts.
Here's how to reach the Shovels team.
## Mailing Address
Shovels' official mailing address:
**3515 Mt. Diablo Blvd, Unit #51**
**Lafayette, CA 94549**
## Email Contacts
| Purpose | Email |
| --------------------- | ----------------------------------------------- |
| **Support questions** | [support@shovels.ai](mailto:support@shovels.ai) |
| **Sales inquiries** | [sales@shovels.ai](mailto:sales@shovels.ai) |
## Phone
**1-800-511-7457**
## Online Resources
* [Shovels Online](https://app.shovels.ai) - Web application
* [Documentation](https://docs.shovels.ai) - API docs and guides
* [Blog](https://www.shovels.ai/blog/) - News and updates
* [Contact Form](https://www.shovels.ai/contact) - General inquiries
## Community
Join our [Discord community](https://discord.gg/Nypja3cKDx) for discussions and support.
## Related Articles
* [Refund policy](/docs/knowledge-base/company/refund-policy)
* [What can I do with Shovels data?](/docs/knowledge-base/company/data-usage-terms)
* [How do I cancel my subscription?](/docs/knowledge-base/shovels-online/cancel-subscription)
# What Can and Can't I Do With Shovels Data?
Source: https://docs.shovels.ai/docs/knowledge-base/company/data-usage-terms
Shovels data is licensed for internal business use: research, prospecting, lead generation, and product integration. Uploading to ad platforms, reselling, or sharing externally is not permitted.
**Shovels data is licensed for your internal business use.** You can use it for internal research, prospecting, lead generation, building it into your own product (API customers), exporting and storing it internally, sharing with colleagues, and contacting contractors or property owners directly. You **cannot** upload it to ad platforms for audience targeting, resell or redistribute it, share it externally without written permission, or use it to build a competing data product.
## What You Can Do
* Use permit and contractor data for internal research, prospecting, and lead generation
* Build the data into your own product or application (API customers)
* Export and store data internally for business analysis
* Share data with colleagues within your organization
* Use the data to contact contractors or property owners directly
## What You Cannot Do
* **Upload Shovels data to ad platforms** (Facebook, Google, LinkedIn, etc.) for audience targeting or lookalike campaigns
* **Resell or redistribute** the data to third parties
* **Share data externally** outside your organization without written permission
* **Use the data to build a competing data product**
**Example:** Using a list of addresses from Shovels to create a Facebook Custom Audience or Google Customer Match list is not permitted under our Terms of Service.
## Why These Restrictions Exist
Shovels sources its data through direct relationships with local governments and jurisdictions. Our licensing agreements require that we limit how the data is used and distributed. These restrictions protect both Shovels and our customers.
## Questions About Your Use Case?
If you're unsure whether your intended use is permitted, email [support@shovels.ai](mailto:support@shovels.ai) before proceeding. We're happy to clarify.
## Full Terms of Service
For the complete terms, visit [shovels.ai/terms](https://shovels.ai/terms).
## Related Articles
* [Contact information](/docs/knowledge-base/company/contact-information)
* [Where does Shovels data come from?](/docs/knowledge-base/data/quality/data-sources)
* [Refund policy](/docs/knowledge-base/company/refund-policy)
# How Property Types Are Categorized
Source: https://docs.shovels.ai/docs/knowledge-base/company/property-types
Understand how Shovels categorizes property types in permit data using tax assessor records.
Property types and details in Shovels data are categorized using external data sources.
## Data Source
Property type information is derived from **tax assessor files**, which are included in EDL data deliveries.
## Coverage
There may be gaps in property type coverage because:
* Not all jurisdictions have comprehensive tax assessor data
* Some properties may not have type classifications
* Newer developments may not yet be in assessor records
We are continuously working to improve property type data coverage and accuracy.
## Available Classifications
Common property type classifications include:
* Residential (single-family, multi-family)
* Commercial
* Industrial
* Mixed-use
## Using Property Types
### In Shovels Online
Use the "Building" filters to narrow searches by property type.
### In the API
Use the `property_type` parameter in permit and contractor searches.
### In EDL
Property type fields are included in data deliveries with associated tax assessor information.
## Related Articles
* [Permit distribution](/docs/knowledge-base/data/permits/permit-distribution)
* [Filters and sorting](/docs/knowledge-base/shovels-online/filters-and-sorting)
# What Is Shovels' Refund Policy?
Source: https://docs.shovels.ai/docs/knowledge-base/company/refund-policy
Shovels reviews refund requests case-by-case. Email support@shovels.ai within 30 days of the charge with your account email, charge details, and reason. Approved refunds appear within 5–10 business days.
**Refund requests are reviewed case-by-case. Contact [support@shovels.ai](mailto:support@shovels.ai) within 30 days of the charge with your account email, charge amount and date, and a brief description.** The billing team responds within 2 business days, and approved refunds typically appear on your original payment method within 5–10 business days.
## Eligibility
Refund requests are considered case-by-case. We'll do our best to make it right if:
* The data you received was materially different from what was described
* You experienced a technical issue that prevented you from accessing the product
* You contacted us within **30 days** of your charge
## How to Request a Refund
Email [support@shovels.ai](mailto:support@shovels.ai) with:
* Your account email
* The charge amount and approximate date
* A brief description of what didn't meet your expectations
Our billing team reviews your request and responds within **2 business days**.
## What to Expect
* Approved refunds are returned to the original payment method
* Refunds typically appear within **5–10 business days** depending on your bank
* Partial refunds may be issued for partial usage periods
## Things We Can't Refund
* Charges older than 30 days (exceptions considered for extenuating circumstances)
* Credits or API usage already consumed
* Enterprise Data License deliveries that have already been fulfilled
## Questions?
* **Email:** [support@shovels.ai](mailto:support@shovels.ai)
* **Phone:** [1-800-511-7457](tel:+18005117457)
## Related Articles
* [How do I cancel my subscription?](/docs/knowledge-base/shovels-online/cancel-subscription)
* [How does pricing work?](/docs/knowledge-base/getting-started/pricing-structure)
* [Contact information](/docs/knowledge-base/company/contact-information)
# Contractor Contact Information
Source: https://docs.shovels.ai/docs/knowledge-base/data/contractors/contact-information
Understand the contractor contact fields in Shovels data, including address information, phone number prioritization, and how contact names relate to emails.
Shovels provides comprehensive contact information for contractors when available.
## Address Information
We provide complete USPS-standardized addresses including:
* Street address
* City
* State
* ZIP code
* Unit or suite numbers (when available)
Our addresses undergo rigorous verification against multiple sources:
* National Address Dataset (US Census)
* Open Address dataset
* Simple Maps
* ESRI
The standardized format makes it easy to integrate with mapping services, CRM systems, or marketing platforms.
## Phone Numbers
### primary\_phone vs phone
| Field | Description |
| ------------------ | ----------------------------------------------------------------------------- |
| **primary\_phone** | The most frequently used phone number across all permits for this contractor |
| **phone** | A comma-separated list of all phone numbers encountered, ordered by frequency |
**Example:** If a contractor used three different phone numbers across their permit history (one on 70% of permits, others on 15% each), the most common number is designated as `primary_phone`, while all three appear in `phone`.
## Contact Names and Emails
### Why They May Not Match
Contact names and email addresses don't always align because they're captured differently:
* **Contact names** are specific to individual permits (the name on that permit application)
* **primary\_email** is determined by analyzing frequency across all permits for a contractor
**Example:** If John Smith is listed on a specific permit, but [jane.doe@company.com](mailto:jane.doe@company.com) appears most frequently across all that contractor's permits, `primary_email` shows Jane's email.
This approach ensures you have the most actively used contact point, which increases the likelihood of successful communication.
## Job Titles
Job titles are available across all Shovels products:
* **API** - Through the employees endpoint
* **Shovels Online** - In contractor profiles
* **EDL** - As part of custom reports
Common titles include:
* Owner
* Project Manager
* Office Manager
* Trade-specific roles (Master Electrician, General Contractor)
## Related Articles
* [Contractor data overview](/docs/knowledge-base/data/contractors/contractor-data-overview)
* [Employee data via API](/docs/knowledge-base/api/contractors/employee-data)
# Contractor Data Overview
Source: https://docs.shovels.ai/docs/knowledge-base/data/contractors/contractor-data-overview
Understand the contractor data available in Shovels, including regional coverage variations and data sources.
Shovels provides comprehensive contractor data derived directly from permit records.
## Data Sources
Contractor information comes from:
* Permit application data
* State contractor licensing boards
* Business registration records
## Regional Coverage Variations
Contractor data coverage varies by region based on what jurisdictions include on permits.
### Strong Coverage Areas
* West Coast (California, Oregon, Washington)
* New York (metro area)
* Certain areas of the Midwest
### Coverage Challenges
**Upstate New York** - Coverage is not as comprehensive as the metro area.
**Massachusetts** - The township structure means permit data and contractor details aren't always readily available online.
**New Jersey** - The state doesn't typically include contractor data on permits.
These regional variations reflect different reporting requirements and data availability across jurisdictions.
## What's Included
When available, contractor records include:
* Business name and contact information
* License numbers and status
* Permit history and volume
* Performance metrics
* Employee information
## Related Articles
* [Contractor quality indicators](/docs/knowledge-base/data/contractors/quality-indicators)
* [Contact information fields](/docs/knowledge-base/data/contractors/contact-information)
* [Contractor search in API](/docs/knowledge-base/api/contractors/contractor-search)
# Why Contractor IDs Can Change
Source: https://docs.shovels.ai/docs/knowledge-base/data/contractors/id-changes
Understand why contractor IDs may occasionally change as Shovels discovers new permit data, how branch-level assignment works, and how to stay current with the contractor ID changelog.
Contractor IDs are designed to be stable, but they can occasionally change as we continuously collect new permits and discover additional details about a contractor.
## How Contractor IDs Are Assigned
Contractor IDs are assigned at the **branch level**. This means:
* A contractor with a business address in City X has a different ID than the same business name in another city
* If we cannot associate a contractor with a specific business address, they are assigned a separate ID
## When IDs Change
An ID change can happen when a subsequent permit scrape reveals new information. For example:
* A contractor was previously unassociated with a specific business address
* A new scrape reveals their address
* The contractor is matched to an existing branch record, resulting in an updated ID
These changes are infrequent but can occur as our dataset grows and data quality improves.
## Contractor ID Changelog
We maintain a **contractor ID changelog** that tracks these changes. If ID changes affect your workflow, contact us and we can share the changelog for your records.
If you cache contractor IDs and encounter not-found responses, use other record attributes (business name, address, license number) to look up the updated ID. Contact [support@shovels.ai](mailto:support@shovels.ai) if you need the full changelog.
## Related Articles
* [Contractor deduplication](/docs/knowledge-base/edl/contractor-deduplication)
* [Contractor data overview](/docs/knowledge-base/data/contractors/contractor-data-overview)
* [Data refresh frequency](/docs/knowledge-base/data/quality/refresh-frequency)
# Industry Classification Data (NAICS & SIC)
Source: https://docs.shovels.ai/docs/knowledge-base/data/contractors/industry-classification
Understand NAICS and SIC industry classification codes in Shovels contractor data, their sources, and limitations.
Shovels includes industry classification data for contractors, though this data has important limitations to understand.
## Available Classification Fields
| Field | Description |
| --------------------- | --------------------------------------------------- |
| **NAICS** | North American Industry Classification System codes |
| **SIC** | Standard Industrial Classification codes |
| **Primary\_industry** | General industry classification |
## Data Sources
Business industry classifications come from self-reported data, typically through:
* Business registration
* Insurance-related transactions
## Important Limitations
### Self-Reported Nature
Classifications are self-reported by businesses, not verified by Shovels.
### Broad Categories
Classifications are quite broad and may not precisely reflect a contractor's specialization.
### Multiple Classifications
Companies in higher-risk occupations often use multiple industry identifiers to reduce their workers' compensation insurance liability.
### No Central Authority
There is no central authority governing these categories.
The Primary\_industry field should be taken with a grain of salt due to these limitations.
## NAICS vs SIC
| System | Description |
| --------- | ---------------------------------------------- |
| **NAICS** | More modern and specific classification system |
| **SIC** | Older system, but more widely adopted |
Both are included in our data because NAICS isn't as widely adopted as SIC codes.
## Better Alternatives
For understanding what a contractor actually does, consider:
* **Permit descriptions** - What work they've actually permitted
* **Tags** - Shovels-assigned categories based on permit data
* **Permit history** - Patterns of work they've completed
## Related Articles
* [Contractor data overview](/docs/knowledge-base/data/contractors/contractor-data-overview)
* [Contractor search](/docs/knowledge-base/api/contractors/contractor-search)
# Assessing Contractor Quality Through Data
Source: https://docs.shovels.ai/docs/knowledge-base/data/contractors/quality-indicators
Learn how to use Shovels data metrics to evaluate contractor quality and performance, including permit volume, completion times, and inspection rates.
Shovels evaluates contractors based on objective data points that can serve as indicators of quality and performance.
## Key Quality Metrics
### Permit Volume
The quantity of permits pulled indicates:
* Experience level
* Business activity
* Market presence
### Jurisdictional Coverage
How many jurisdictions a contractor works in shows:
* Geographic reach
* Familiarity with different building codes
* Business scale
### Completion Timeframes
How quickly contractors complete jobs indicates:
* Efficiency
* Resource availability
* Project management capability
### Inspection Pass Rates
Track record with inspections shows:
* Work quality
* Code compliance
* Attention to detail
## Using Data for Assessment
These metrics help you evaluate contractors for:
* **Experience** - How many times they've done similar work
* **Local familiarity** - Their experience in specific cities
* **Efficiency** - How quickly they complete jobs
## Supplementing with Qualitative Data
While Shovels focuses on quantitative data points, customers often supplement with qualitative feedback from:
* Google reviews
* Yelp
* Thumbtack
* Word-of-mouth recommendations
Combine Shovels' objective permit data with customer reviews for a complete picture of contractor quality.
## Related Articles
* [Contractor data overview](/docs/knowledge-base/data/contractors/contractor-data-overview)
* [Contractor search](/docs/knowledge-base/api/contractors/contractor-search)
# Decision Data Fields
Source: https://docs.shovels.ai/docs/knowledge-base/data/decisions/data-fields
A complete reference of the Shovels Decisions dataset schema, including metadata, zoning details, property information, and involved parties. This documents the full dataset; the API exposes a subset under different names.
Each Shovels Decision record contains structured data extracted from city council and planning department meetings. This reference covers the complete **Decisions dataset** schema.
This page documents the full Decisions **dataset**, a superset of what the API returns. The API `DecisionsRead` response exposes a subset of these fields, some under different names — notably `when` → `decision_date`, `link` → `source_url`, `applicant` → `applicant_name`, `owner` → `owner_name`, `developer` → `developer_name`, `representative` → `representative_name`, `address_1`/`address_2`/`address_3` → `street` / `city` / `state`, and `size_ac`/`size_sf` → `lot_size` (the API also adds `project_value`). See the [API Reference](/api-reference) for the exact API schema.
## Core Metadata
| Field | Description |
| ------------- | -------------------------------------------------- |
| `title` | Descriptive name of the decision |
| `when` | Date the decision was made |
| `city` | Municipality where the decision applies |
| `state` | State (full name or abbreviation) |
| `category` | Decision type (Spot Rezoning, Area Rezoning, etc.) |
| `subcategory` | More specific categorization |
| `link` | URL to the official source document |
## Location Data
| Field | Description |
| ----------------- | ---------------------------------------------- |
| `address_1` | Primary property address |
| `address_2` | Secondary address (for multi-parcel decisions) |
| `address_3` | Additional address |
| `latitude` | GPS latitude coordinate |
| `longitude` | GPS longitude coordinate |
| `address_id` | Shovels address identifier |
| `city_id` | Shovels city identifier |
| `county_id` | Shovels county identifier |
| `jurisdiction_id` | Shovels jurisdiction identifier |
Geographic coordinates enable mapping and spatial analysis. The geographic identifiers (`address_id`, `city_id`, `county_id`, `jurisdiction_id`) are flat fields you can use to join decision data with other Shovels datasets. Unlike permits, decisions do not nest these in a `geo_ids` object.
## Zoning Details
| Field | Description |
| ----------------------------- | --------------------------------------- |
| `zoning_previous` | Original zoning classification code |
| `zoning_previous_description` | Detailed description of previous zoning |
| `zoning_new` | New/amended zoning classification code |
| `zoning_new_description` | Detailed description of new zoning |
**Example:**
* `zoning_previous`: "R-1" (Single-Family Residential)
* `zoning_new`: "MF-2" (Multifamily Medium Density)
## Property Information
| Field | Description |
| --------------- | ---------------------------------------------------------------------- |
| `size_ac` | Property size in acres |
| `size_sf` | Property size in square feet |
| `asset_class` | Property type (Residential, Commercial, Industrial, Multifamily, etc.) |
| `property_type` | More specific type (Retail, Office, Warehouse, etc.) |
## Regulatory Parameters
These fields capture the development rules associated with the decision:
| Field | Description |
| ------------------------- | ------------------------------------------------------------ |
| `allowed_uses` | Permitted land uses under the new zoning |
| `density` | Development density restrictions (units per acre, FAR, etc.) |
| `minimum_lot_size` | Minimum parcel size requirements |
| `maximum_building_height` | Height restrictions |
| `setbacks` | Required distances from property lines |
| `lot_coverage` | Maximum percentage of lot that can be built upon |
| `parking_requirements` | Required parking specifications |
## Involved Parties
Decisions often identify the people and organizations involved:
### Applicant
The person or entity requesting the zoning action.
| Field | Description |
| -------------------- | ---------------------------- |
| `applicant` | Company or organization name |
| `applicant_person` | Individual applicant name |
| `applicant_email` | Contact email |
| `applicant_linkedin` | LinkedIn profile URL |
| `applicant_website` | Business website |
### Property Owner
| Field | Description |
| ------------- | ------------------- |
| `owner` | Property owner name |
| `owner_email` | Contact email |
### Development Team
| Field | Description |
| ---------------- | --------------------------------------- |
| `developer` | Development company |
| `engineer` | Project engineer |
| `architect` | Design architect |
| `representative` | City council representative or district |
Each role may include associated `_email`, `_linkedin`, and `_website` fields when available.
## Descriptive Content
| Field | Description |
| ---------------- | ---------------------------------------------------- |
| `description` | Detailed explanation of the decision and its context |
| `summary` | Brief summary of the decision |
| `why_it_matters` | Analysis of the decision's significance and impact |
## Metadata Fields
| Field | Description |
| -------------------- | ------------------------------------------- |
| `newsworthiness` | Confidence score (0-1) indicating relevance |
| `last_modified_date` | When the record was last updated |
Not all fields are populated for every decision. Field availability depends on what information was included in the source documents.
## Related Articles
* [What are Shovels Decisions?](/docs/knowledge-base/data/decisions/overview)
* [Decision categories explained](/docs/knowledge-base/data/decisions/decision-categories)
* [From decision to permit](/docs/knowledge-base/data/decisions/decision-to-permit)
# Decision Categories
Source: https://docs.shovels.ai/docs/knowledge-base/data/decisions/decision-categories
Understanding the different types of zoning and land use decisions tracked by Shovels, from spot rezonings to zoning code modifications.
Shovels Decisions are categorized by the type of municipal action they represent. Understanding these categories helps you filter for the decisions most relevant to your use case.
## Primary Categories
### Spot Rezoning
A zoning change applied to a specific property or small group of parcels. Spot rezonings are typically initiated by property owners or developers seeking to change what can be built on their land.
**Examples:**
* Changing a single-family residential lot to allow multifamily development
* Converting a commercial parcel to mixed-use
* Rezoning an industrial site for residential development
**Why it matters:** Spot rezonings often signal imminent development activity on that specific property.
### Area Rezoning
A zoning change applied to a larger geographic area, often initiated by the municipality as part of a comprehensive planning effort.
**Examples:**
* Rezoning an entire corridor for transit-oriented development
* Upzoning a neighborhood to allow higher density
* Creating a new overlay district for a downtown area
**Why it matters:** Area rezonings indicate broader market shifts and can affect property values across many parcels.
### Zoning Code Modification
Changes to the zoning regulations themselves, rather than the zoning designation of specific properties. These modify what's allowed within existing zones.
**Examples:**
* Adjusting height limits in commercial zones
* Adding accessory dwelling units (ADUs) as a permitted use
* Modifying parking requirements citywide
**Why it matters:** Code modifications can unlock development potential across all properties in affected zones without individual rezonings.
### Special Use Permit
Permission to conduct a specific activity that isn't automatically allowed in a zone but may be approved with conditions. Also called conditional use permits in some jurisdictions.
**Examples:**
* Operating a restaurant with outdoor seating in a residential area
* Building a cell tower in a commercial zone
* Running a daycare facility in a mixed-use building
**Why it matters:** Special use permits often precede specific business or development activity at a known location.
## Subcategories
Decisions may also include subcategories that provide additional context:
| Subcategory | Description |
| ---------------------------------- | ------------------------------------------------------- |
| **Inclusionary Zoning** | Requirements or incentives for affordable housing units |
| **Conditional Use** | Approval contingent on meeting specific conditions |
| **Planned Unit Development (PUD)** | Flexible zoning for master-planned projects |
| **Variance** | Exception to specific zoning requirements |
| **Historic Overlay** | Special regulations for historic preservation |
## Filtering by Category
When searching for decisions, you can filter by category to focus on the types most relevant to your needs:
* **Development tracking:** Focus on Spot Rezoning and Special Use Permits
* **Market analysis:** Include Area Rezoning and Zoning Code Modifications
* **Comprehensive monitoring:** Include all categories
Category names may vary slightly by jurisdiction. Shovels normalizes these into consistent categories while preserving the original terminology in the decision details.
## Related Articles
* [What are Shovels Decisions?](/docs/knowledge-base/data/decisions/overview)
* [Decision data fields](/docs/knowledge-base/data/decisions/data-fields)
* [From decision to permit](/docs/knowledge-base/data/decisions/decision-to-permit)
# From Decision to Permit: The Development Lifecycle
Source: https://docs.shovels.ai/docs/knowledge-base/data/decisions/decision-to-permit
How Shovels Decisions connect to permits and contractor data to provide complete visibility into development projects from proposal to completion.
Shovels is the only platform that connects municipal decisions to permit data, giving you visibility into the complete development lifecycle. This article explains how these data types work together.
## The Development Timeline
A typical development project moves through several stages before construction begins:
```
City Council Discussion → Zoning Approval → Permit Application → Permit Issued → Construction → Certificate of Occupancy
↑ ↑ ↑ ↑
DECISIONS DECISIONS PERMITS PERMITS
```
### Stage 1: Proposal and Discussion
Development projects often first appear in city council agendas or planning commission meetings. At this stage, the project is being discussed but no formal action has been taken.
**What you see in Decisions:** Meeting agenda items, preliminary discussions, public hearing notices.
### Stage 2: Zoning Approval
If the project requires a zoning change, special use permit, or variance, it must be approved by the relevant municipal body. This is the core of what Shovels Decisions captures.
**What you see in Decisions:** Approved rezonings, conditional use permits, variances, and zoning code modifications.
### Stage 3: Permit Application and Issuance
After zoning is in place, the developer applies for building permits. This is where Shovels permit data begins tracking the project.
**What you see in Permits:** Filed applications, permit approvals, contractor assignments, inspection schedules.
### Stage 4: Construction and Completion
As construction proceeds, permits are updated with status changes, inspections, and eventually final approval.
**What you see in Permits:** Status updates, final inspections, certificates of occupancy.
## Time Advantage
The gap between a zoning decision and permit filing varies by project complexity:
| Project Type | Typical Gap |
| --------------------------- | ----------- |
| Minor commercial renovation | 2-4 weeks |
| New single-family home | 1-3 months |
| Multifamily development | 3-6 months |
| Large commercial project | 6-12 months |
By monitoring decisions, you gain visibility into projects during this gap—before they appear in permit data.
## Connecting Decisions to Permits
Several fields help you link decisions to eventual permits:
### Geographic Matching
* **Address:** The property address in a decision should match the permit address
* **Coordinates:** Latitude/longitude enable spatial matching when addresses differ slightly
* **Geo IDs:** Standardized geographic identifiers allow precise joins
### Entity Matching
* **Applicant/Owner:** The decision applicant often appears as the permit applicant or property owner
* **Developer:** Development company names may appear in both records
* **Contractor:** Contractors assigned to permits may be associated with the developer from the decision
### Timeline Correlation
Permits filed within the expected window after a relevant decision at the same location are likely related to that project.
## Use Cases
### Early Lead Generation
Identify projects at the decision stage to reach potential customers before competitors who only monitor permits.
**Example:** A roofing contractor monitors area rezonings for multifamily developments, then contacts developers months before permit applications are filed.
### Site Selection Intelligence
Track zoning decisions to understand where development is being approved and what types of projects municipalities are favoring.
**Example:** A data center operator monitors zoning code modifications related to power infrastructure and industrial uses to identify favorable jurisdictions.
### Competitive Analysis
Monitor decisions involving specific developers or property owners to track their pipeline of upcoming projects.
**Example:** A subcontractor tracks decisions listing their target general contractors as applicants to anticipate bidding opportunities.
### Market Timing
Area rezonings and code modifications signal shifts in what can be built, affecting property values before any construction begins.
**Example:** An investor monitors upzoning decisions to identify neighborhoods where development potential—and land values—are increasing.
The combination of Decisions, Permits, and Contractor data makes Shovels the intelligence layer for the built world—from first proposal to final certificate of occupancy.
## Related Articles
* [What are Shovels Decisions?](/docs/knowledge-base/data/decisions/overview)
* [Decision categories explained](/docs/knowledge-base/data/decisions/decision-categories)
* [Decision data fields](/docs/knowledge-base/data/decisions/data-fields)
* [How permits are tracked through their lifecycle](/docs/knowledge-base/data/permits/permit-lifecycle)
# What are Shovels Decisions?
Source: https://docs.shovels.ai/docs/knowledge-base/data/decisions/overview
Shovels Decisions track municipal zoning and land use decisions from city councils and planning boards, giving you earlier visibility into development activity.
**Shovels Decisions are structured records of municipal zoning and land use decisions extracted from city council and planning department meetings across the United States.** This data gives you visibility into development activity months before permits are filed.
## The ReZone Acquisition
Shovels Decisions originated from our acquisition of ReZone, an AI company that tracked city and local government meeting decisions. ReZone was founded on a simple thesis: real estate is—and will always be—a local business. The direction of our built environment isn't driven by big decisions in Washington, but by thousands of smaller decisions made every month by city councils and planning boards.
By acquiring ReZone, Shovels became the only company to unify meeting records with permit data, delivering the earliest possible insight into development activity.
## What Decisions Capture
Each decision record documents a zoning or land use action taken by a local government, including:
* **Rezoning approvals** — Changes to how a property can be used
* **Special use permits** — Permissions for specific activities within a zone
* **Zoning code modifications** — Changes to the rules themselves
* **Variance approvals** — Exceptions to existing zoning requirements
For each decision, we capture the property location, the zoning change details, involved parties (applicants, developers, owners), regulatory parameters, and links to source documents.
## Why Decisions Matter
Traditional permit data tells you what's being built. Decisions tell you what's being *planned*—often months earlier.
### Earlier Project Visibility
Zoning decisions typically precede permit applications by weeks or months. A rezoning approval for a multifamily development signals future permit activity before any application is filed.
### Complete Development Lifecycle
Combined with Shovels permit and contractor data, Decisions provide a complete view of development projects from first city council discussion through certificate of occupancy.
### Market Intelligence
Track infrastructure decisions, density changes, and development approvals to anticipate market movements before ground is broken.
Decisions are particularly valuable for enterprise customers making major infrastructure decisions, including site selection for data centers, fiber deployment planning, and real estate development.
## How Decisions Complement Permits
| Data Type | What It Tells You | When It's Available |
| --------------- | ------------------------------------------- | --------------------------------------- |
| **Decisions** | What's being proposed and approved | Earliest stage—city council discussions |
| **Permits** | What's being built and by whom | After approval—construction phase |
| **Contractors** | Who's doing the work and their track record | Throughout construction |
Together, these data types let you track a project from inception to completion.
## Related Articles
* [Decision categories explained](/docs/knowledge-base/data/decisions/decision-categories)
* [Decision data fields](/docs/knowledge-base/data/decisions/data-fields)
* [From decision to permit](/docs/knowledge-base/data/decisions/decision-to-permit)
* [How to search for decisions (API)](/docs/knowledge-base/api/decisions/searching-decisions)
* [Where Decisions are available](/docs/knowledge-base/api/decisions/decisions-availability)
# Latitude and Longitude Data
Source: https://docs.shovels.ai/docs/knowledge-base/data/geographic/coordinates-geocoding
Understand how Shovels generates accurate geographic coordinates through our proprietary geocoding process using multiple authoritative data sources.
The latitude and longitude coordinates in Shovels' data are derived from our proprietary geocoding process.
## How It Works
Rather than relying on a single third-party service, we perform geocoding ourselves using multiple reference sources to ensure accuracy.
## Data Sources
Our process cross-references addresses against several authoritative datasets:
* **National Address Dataset** from the US Census
* **Open Address dataset**
* **Simple Maps**
* **ESRI**
This multi-source approach allows us to achieve higher accuracy rates than depending on any single geocoding service.
## Why Accurate Geocoding Matters
Accurate geocoding is particularly important in the construction industry, where precise location information can impact:
* Permit jurisdiction determination
* Zoning requirements
* Service area analysis
* Market visualization
## What You Can Do With Coordinates
Our coordinates enable customers to:
* Perform spatial analysis
* Create map visualizations
* Calculate distances
* Identify geographic patterns in construction activity
## Part of Address Standardization
The geocoding process is part of our broader address standardization pipeline, which ensures:
* Consistent formatting across the database
* Accurate location data regardless of original permit formatting
* Reliable geographic identification
Address geocoding is done in-house at Shovels to ensure accuracy and consistency across our entire database.
## Related Articles
* [Data verification methods](/docs/knowledge-base/data/quality/verification-methods)
* [Coverage areas](/docs/knowledge-base/data/geographic/coverage-areas)
# What Areas Does Shovels Cover?
Source: https://docs.shovels.ai/docs/knowledge-base/data/geographic/coverage-areas
Shovels covers ~2,000 US jurisdictions representing ~85% of the US population. Data goes back to 2010+. We add 10-30 new jurisdictions monthly.
**Shovels covers approximately 2,000 jurisdictions representing about 85% of the US population.** Our data extends back to at least 2010 for all jurisdictions, with many going further. We add 10-30 new jurisdictions each month, and 5-10 million new permits with each twice-monthly refresh.
## Current Coverage
* **\~2,000 jurisdictions** covered
* **\~85% of the US population** represented
* **10-30 new jurisdictions** added each month
* **5-10 million new permits** added each refresh cycle, twice a month
## Power Law Distribution
Coverage follows a power law distribution across the approximately 20,000 jurisdictions in the United States:
* About **85% of the US population** resides in just **15% of jurisdictions**
* Shovels has strategically focused on covering these high-density areas first
We continue to expand our coverage into the "long tail" of smaller jurisdictions while maintaining focus on population centers.
## Data Sources
We gather permit data from a variety of sources:
### Primary Sources
* **Local jurisdiction websites** - City and county building department online portals
* **Municipal open data portals** - Dedicated open data initiatives
* **Building department APIs** - Official jurisdiction APIs when available
* **Public records requests** - For jurisdictions without digital access
### Supplementary Sources
* **State contractor licensing boards** - To verify contractor information
* **Address standardization databases** - Multiple sources for geocoding and standardization
* **Tax assessor records** - To enhance property information
## Historical Data
Our data coverage extends back to **at least 2010** for all jurisdictions, with many going further back.
In some cases, jurisdictions don't pull historical data into their online systems, which can limit how far back certain records go.
## Why Some Permits Might Be Missing
Even in covered jurisdictions, some permits may not appear due to:
* **Digitization gaps** - Not all jurisdictions have digitized historical records
* **Data availability** - Some permits lack required fields or are incomplete
* **Processing delays** - New permits take time to enter our system
For a detailed explanation, see [Permit Availability](/docs/foundations-understanding-permits) in our Foundations section.
## Find Coverage Information
* [List of all covered jurisdictions](https://www.shovels.ai/blog/list-of-all-building-permit-jurisdictions/)
* [Coverage Dashboard](https://www.shovels.ai/coverage)
## Related Articles
* [Understanding jurisdictions](/docs/knowledge-base/data/geographic/jurisdictions)
* [Data refresh frequency](/docs/knowledge-base/data/quality/refresh-frequency)
* [Why am I getting so few results?](/docs/knowledge-base/data/quality/few-results)
* [Permit Availability explained](/docs/foundations-understanding-permits)
# What is a Jurisdiction (AHJ)?
Source: https://docs.shovels.ai/docs/knowledge-base/data/geographic/jurisdictions
A jurisdiction is the Authority Having Jurisdiction (AHJ)—typically a city or county government—that issues building permits. The US has ~20,000 AHJs.
**A jurisdiction is the Authority Having Jurisdiction (AHJ)—the governmental body responsible for issuing building permits in a specific area, typically a city or county government.** The United States has approximately 20,000 jurisdictions. Shovels covers about 2,000 of them, representing \~85% of the US population.
## What is an AHJ?
An AHJ is typically either:
* A **county government**
* A **city government**
## Why Jurisdictions Matter
Jurisdictions play a crucial role in the construction permitting process:
* **Establish and enforce building codes**
* **Conduct inspections**
* **Maintain official records** of construction activities
Understanding which jurisdiction governs a particular property is essential for contractors and service providers, as it determines:
* Which rules apply
* Which office must approve permits
* Specific requirements and processes
## Jurisdiction Coverage
### How Many Exist
Nationwide, there are approximately **20,000 different jurisdictions**, each with their own:
* Requirements
* Processes
* Data formats
### Shovels Coverage
Shovels has standardized data from over **2,000 jurisdictions**, covering approximately **85% of the US population**.
### Power Law Distribution
Our coverage follows a power law distribution:
* About 15% of jurisdictions represent \~85% of the US population
* The remaining 85% of jurisdictions cover just \~15% of the population
This is why we prioritize larger jurisdictions while continuously expanding into smaller ones.
## Finding Jurisdiction Information
### Complete List
Shovels maintains a comprehensive list of all covered jurisdictions, available on our blog:
[List of All Building Permit Jurisdictions](https://www.shovels.ai/blog/list-of-all-building-permit-jurisdictions/)
### Updates
The list is regularly updated as we add new coverage areas. We add **10-30 new jurisdictions** each month.
## Learn More
* [AHJ in Construction](https://www.shovels.ai/blog/ahj-in-construction/) - Blog post explaining AHJs in detail
* [Geographic coverage](/docs/knowledge-base/data/geographic/coverage-areas)
# Residential vs Commercial Permit Distribution
Source: https://docs.shovels.ai/docs/knowledge-base/data/permits/permit-distribution
Understand the breakdown of residential versus commercial permits in Shovels' data coverage.
Shovels' data includes permits across all asset classes, providing comprehensive coverage of construction activity.
## Distribution
Our data has a broad distribution of approximately:
* **60% residential permits**
* **40% commercial permits**
This comprehensive coverage ensures that customers have access to permit data across the full spectrum of construction and renovation activities.
## Filtering by Property Type
You can filter permits by property type in both:
* **Shovels Online** - Use the Building filters
* **Shovels API** - Use the `property_type` parameter
## Related Articles
* [Permit search](/docs/knowledge-base/api/permits/permit-search)
* [Filters and sorting](/docs/knowledge-base/shovels-online/filters-and-sorting)
# How Permits Are Tracked Through Their Lifecycle
Source: https://docs.shovels.ai/docs/knowledge-base/data/permits/permit-lifecycle
Understand how Shovels tracks individual permits from filing to completion using unique IDs and status updates without creating duplicates.
Each permit in the Shovels database receives a unique permit ID that remains persistent throughout its entire lifecycle.
## How It Works
As updates are made to a permit—such as status changes, valuation adjustments, or contractor assignments—we update the data while maintaining the same permit ID.
This approach ensures:
* A continuous record of the permit's progression
* No duplicate entries from status updates
* Reliable tracking across data refreshes
## Permit Lifecycle Stages
The permit lifecycle typically flows through several stages:
| Stage | Status | Date Field |
| ----------------- | ----------- | ------------ |
| Filing | `in_review` | `file_date` |
| Approval | `active` | `issue_date` |
| Completion | `final` | `final_date` |
| Abandoned/Revoked | `inactive` | - |
## Status Updates
For each status change, we update the corresponding date fields while maintaining the same unique identifier. This allows users to track a permit's complete history without piecing together fragments across multiple records.
Status changes reach Shovels with the **twice-monthly** data refresh rather than in real time, so a permit carries its previous status until the refresh that captures the change. See [how often data is refreshed](/docs/knowledge-base/data/quality/refresh-frequency).
## Deduplication
Our deduplication process ensures that even when jurisdictions report the same permit multiple times with different statuses, Shovels presents a single, up-to-date record with the most current information.
If a permit has a final status but no final date, it typically indicates an **over-the-counter permit**—one that was approved immediately without the need for a follow-up inspection.
## Related Articles
* [Permit statuses explained](/docs/knowledge-base/data/permits/permit-statuses)
* [Tracking multiple permits](/docs/knowledge-base/data/permits/permit-tracking)
* [Understanding start and end dates](/docs/knowledge-base/data/permits/start-end-dates)
* [How often is data refreshed?](/docs/knowledge-base/data/quality/refresh-frequency)
# What Do Permit Statuses Mean in Shovels?
Source: https://docs.shovels.ai/docs/knowledge-base/data/permits/permit-statuses
Shovels has 4 permit statuses: in_review (filed, awaiting approval), active (approved), final (completed), and inactive (stalled/expired). Learn what each means.
**Shovels uses four permit statuses: `in_review` (filed, awaiting approval), `active` (approved, work can proceed), `final` (completed, passed inspection), and `inactive` (stalled, expired, or abandoned).** These statuses track a permit's progression through its lifecycle.
## Permit Statuses in Shovels
There are **four** primary statuses that permits can have:
### in\_review
From the moment the permit is filed with the local jurisdiction until it is approved.
* **Defined by:** `file_date`
* **Meaning:** Permit application is being reviewed
### active
From the moment the permit is approved until project completion or some other intermediate restriction.
* **Defined by:** `issue_date`
* **Meaning:** Permit has been approved, work can proceed
### final
When the permit is completed and submitted back to the jurisdiction upon job completion.
* **Defined by:** `final_date`
* **Meaning:** Project is complete, passed final inspection
### inactive
If any part of the process stalls or gets restricted from progression.
**Reasons a permit may become inactive:**
* Failing to meet project inspections
* Permit expires before completion
* [Inactivity for more than 6 months (180 days)](https://ecode360.com/6567722#6567722)
* Other jurisdiction-specific reasons
Disqualifying reasons and reporting timeframes vary by jurisdiction and local regulation.
## How Current a Status Is
Statuses are refreshed with the dataset **twice a month**, so a permit's status reflects what its jurisdiction reported as of the most recent refresh. A permit that was approved or finaled since then keeps its earlier status until the next refresh picks up the change.
See [how often data is refreshed](/docs/knowledge-base/data/quality/refresh-frequency) for the cadence and the typical reporting lag between a jurisdiction issuing a permit and it appearing in Shovels.
## Special Cases
### "Unknown" and "None" Statuses
Sometimes permits have "unknown" or "none" statuses. This can happen due to:
* Variances in jurisdiction record keeping
* Lack of data
* Dates not provided by the jurisdiction
The exact meanings may vary depending on the context or jurisdiction.
### Final Status Without Final Date
If a permit has a final status but no final date, it typically indicates an **over-the-counter permit**—one that was approved immediately without needing a follow-up inspection.
Common over-the-counter permits include:
* Solar installations
* Simple plumbing work
* Minor electrical work
This can vary by city and county—some jurisdictions process more permit types over-the-counter than others.
## Learn More
* [Shovels 101: Permit Statuses](https://www.shovels.ai/blog/shovels-101-permit-statuses/) - Blog post with detailed explanations
* [Permit lifecycle](/docs/knowledge-base/data/permits/permit-lifecycle)
* [How often is data refreshed?](/docs/knowledge-base/data/quality/refresh-frequency)
* [Checking project completion](/docs/knowledge-base/data/permits/project-completion)
# Tracking Multiple Permits at a Property
Source: https://docs.shovels.ai/docs/knowledge-base/data/permits/permit-tracking
Learn how Shovels uses a system of unique IDs to track and link multiple permits at the same location, enabling comprehensive property analysis.
Shovels uses a robust system of unique IDs to track multiple permits at a single property.
## How Our ID System Works
### Permit IDs
Each permit receives a **unique permit ID** that remains consistent throughout its lifecycle—from application to completion.
### Address IDs
Each address gets a **unique address ID** the first time it appears in our system. This allows you to easily connect all permits associated with that location.
### Contractor IDs
Each contractor receives a **unique contractor ID** when they first appear on any permit.
## Connecting Related Data
These IDs act as reference points (foreign keys) that enable you to:
* Track all permits at a specific property
* Find all permits by a specific contractor
* Join related permits, addresses, and contractors in analysis
Use the address ID to find all permits associated with a specific property, even if the address is written differently across different permits.
## Use Cases
This system is particularly valuable for:
* **Tracking property improvements over time**
* **Identifying contractors who frequently work at the same location**
* **Analyzing permitting patterns for specific properties**
* **Building relationships between different data points**
## ID Persistence
Our IDs are designed to be stable:
* Permit IDs remain the same as status changes
* Address IDs persist even with formatting variations
* Contractor IDs stay consistent across all their permits
The Shovels ID is distinct from the `permit_no` (permit number) assigned by the jurisdiction. Different jurisdictions may coincidentally assign the same permit number, so always use the Shovels ID for unique identification.
## Related Articles
* [Permit lifecycle](/docs/knowledge-base/data/permits/permit-lifecycle)
* [Unique identifiers explained](/docs/knowledge-base/data/permits/unique-identifiers)
* [Data Dictionary](https://www.shovels.ai/data-dictionary)
# Why Some Permits Don't Have Addresses
Source: https://docs.shovels.ai/docs/knowledge-base/data/permits/permits-without-addresses
Understand why some permits in Shovels data lack addresses, particularly for new construction on undeveloped land or newly subdivided parcels.
Yes, Shovels captures permits that don't have an associated address. This is a common occurrence for certain types of construction projects.
## Why Permits Lack Addresses
Address IDs are only assigned if physical addresses can be confirmed via internal and third-party databases.
Common reasons for missing addresses:
### New Construction
Permits for new construction are often submitted before official addresses exist or streets are named. Construction needs to begin before an official address is assigned by the post office.
### Newly Subdivided Parcels
When land is subdivided, permits may be pulled before addresses are created for the new lots.
### Undeveloped Land
Projects on undeveloped land where no street address has been established.
## What's Included Without Addresses
For permits without addresses, Shovels still provides:
* Unique permit ID
* State information
* Jurisdiction information
What's **not** included:
* Address ID
* Physical address
* Geographic identifiers (county, city codes)
## Value of Address-less Permits
These permits are valuable because they often represent:
* New construction projects at their earliest stages
* Early intelligence about upcoming construction activity
* Development opportunities
For users interested in new development opportunities, permits without addresses can provide early signals about upcoming construction.
## Address Assignment Over Time
As projects progress and official addresses are assigned:
1. The jurisdiction updates their records
2. We capture the new address information
3. Records are linked, allowing continuous tracking throughout the construction lifecycle
## Related Articles
* [Permit tracking](/docs/knowledge-base/data/permits/permit-tracking)
* [Permit lifecycle](/docs/knowledge-base/data/permits/permit-lifecycle)
# How to Check Project Completion and Inspection Status
Source: https://docs.shovels.ai/docs/knowledge-base/data/permits/project-completion
Learn how to determine if a construction project was completed and passed inspection by checking the permit status in Shovels data.
To determine whether a project was completed and passed inspection, check the permit status.
## Checking Completion Status
A permit with status **"final"** indicates:
* All work has been completed
* The project has passed inspection
* The permit lifecycle is complete
```
status: "final"
final_date: "2024-06-15"
```
## What the Status Tells You
| Status | Project State |
| ----------- | --------------------------------------- |
| `in_review` | Application submitted, not yet approved |
| `active` | Approved, work may be in progress |
| `final` | Completed and passed inspection |
| `inactive` | Stalled, expired, or abandoned |
## Additional Information
Permit records can provide various details about projects, but availability depends on what the jurisdiction includes:
* **System size** - Depends on permit description detail
* **Inspection results** - Reflected in status changes
* **Timeline** - Captured in date fields
Historical permit records can be accessed, with some jurisdictions providing data going back 25+ years.
## Related Articles
* [Permit statuses explained](/docs/knowledge-base/data/permits/permit-statuses)
* [Permit lifecycle](/docs/knowledge-base/data/permits/permit-lifecycle)
# Understanding Start and End Dates
Source: https://docs.shovels.ai/docs/knowledge-base/data/permits/start-end-dates
Learn how Shovels defines start_date and end_date fields for permits and how they help you understand permit timelines.
The start date and end date fields in Shovels data help you understand the timeline of each permit.
## Definitions
| Field | Definition |
| --------------- | ------------------------------------ |
| **start\_date** | The first date recorded for a permit |
| **end\_date** | The last date recorded for a permit |
These date fields are **generated by Shovels** based on the permit data we collect from jurisdictions.
## How They're Used
Start and end dates help you:
* Calculate total permit duration
* Understand project timelines
* Compare permit processing across jurisdictions
* Identify stalled or delayed projects
## Other Important Date Fields
Shovels also tracks specific milestone dates:
| Field | Description |
| ------------ | ---------------------------------------- |
| `file_date` | When the permit application was filed |
| `issue_date` | When the permit was approved/issued |
| `final_date` | When the project passed final inspection |
For more detailed information on permit timelines and statuses, see our [blog post on permit statuses](https://www.shovels.ai/blog/shovels-101-permit-statuses/).
## Related Articles
* [Permit statuses](/docs/knowledge-base/data/permits/permit-statuses)
* [Permit lifecycle](/docs/knowledge-base/data/permits/permit-lifecycle)
# Understanding Permit Unique Identifiers
Source: https://docs.shovels.ai/docs/knowledge-base/data/permits/unique-identifiers
Learn the difference between Shovels' unique permit ID and the jurisdiction-assigned permit number, and why using the correct identifier matters.
Each permit in Shovels has a unique identifier that distinguishes it from all other permits in our database.
## Shovels ID vs Permit Number
| Field | Description | Uniqueness |
| -------------- | ---------------------------- | ------------------------------------ |
| **ID** | Generated by Shovels | Globally unique across all permits |
| **permit\_no** | Assigned by the jurisdiction | Only unique within that jurisdiction |
## Why This Matters
Different jurisdictions may coincidentally assign the same `permit_no` to different permits. For example:
* Los Angeles might issue permit #12345
* San Francisco might also issue permit #12345
These are completely different permits, but they share the same permit number.
Always use the Shovels `ID` field to organize data by unique permit identifiers, not `permit_no`.
## Using Shovels IDs
The Shovels ID:
* Remains persistent throughout the permit lifecycle
* Never duplicates across jurisdictions
* Enables reliable data joins and tracking
* Stays consistent across data refreshes
## Related Articles
* [Permit tracking](/docs/knowledge-base/data/permits/permit-tracking)
* [Data Dictionary](https://www.shovels.ai/data-dictionary)
# Where Does Shovels Get Its Data?
Source: https://docs.shovels.ai/docs/knowledge-base/data/quality/data-sources
Shovels collects permit data directly from city and county jurisdictions through government relationships, online portals, and public records requests—never from third parties.
**Shovels collects permit data directly from city and county jurisdictions—we don't purchase data from third-party vendors.** We gather data through relationships with local governments, integration with online permitting portals, and public records requests. This direct sourcing ensures data quality and freshness.
## Primary Collection Methods
### Direct from Jurisdictions
We collect data through:
* **Relationships with local governments**
* **Integration with online permitting portals**
* **Public records requests** where needed
### Digital Methods
* Jurisdiction website scraping
* Municipal open data portal integration
* Building department API connections
### Offline Methods
* Calling jurisdiction offices
* Submitting formal records requests
We get data straight from the source—we don't purchase permit data from other vendors.
## Supplementary Sources
### Address Standardization
We cross-reference addresses against:
* National Address Dataset (US Census)
* Open Address dataset
* Simple Maps
* ESRI
### Contractor Verification
We match contractor information against:
* Publicly available state license files
* Business registration records
### Property Information
We enhance property data using:
* Tax assessor records
## Why Direct Sourcing Matters
By collecting directly from jurisdictions, we:
* Get the most current data available
* Control data quality from the start
* Standardize across different formats
* Maintain consistent update schedules
## Related Articles
* [Data verification methods](/docs/knowledge-base/data/quality/verification-methods)
* [Data refresh frequency](/docs/knowledge-base/data/quality/refresh-frequency)
* [Geographic coverage](/docs/knowledge-base/data/geographic/coverage-areas)
# Why Am I Getting So Few Results?
Source: https://docs.shovels.ai/docs/knowledge-base/data/quality/few-results
Common reasons Shovels searches return fewer results than expected: limited jurisdiction coverage, narrow filters, recent permits not yet indexed, or limited digitization.
**If your search is returning fewer results than expected, the most common causes are limited jurisdiction coverage, filters that are too narrow, recent permits not yet indexed (1–2 month lag), or limited digitization in the local jurisdiction.** Most have easy workarounds, and Shovels Online flags the coverage-related ones before you run the search.
## Check the Coverage Signals First
When you pick a location and a complete date range, Shovels Online checks how well each filterable field is actually reported in that area for that period — and tells you before you run the search.
| Signal | What it means | What to do |
| ---------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------- |
| No warning | The field is reliably reported for this area and period | Filter normally |
| A hint on the filter | Only some permits report this field; the hint gives the percentage | Still usable, but expect an incomplete result set |
| The filter is disabled | Permits in this area don't report the field for the selected period | Applying it would exclude nearly every result, so it can't be applied |
Disabled filters are left out of the search even when a value is still showing in the panel, so a greyed-out filter never quietly narrows your results.
If the area and period contain no permits at all, Shovels Online blocks the search and asks you to change the location or dates instead of returning an empty list.
When a banner appears above your results warning that matches may be incomplete, its **Review** action jumps straight to the filter responsible and highlights it.
These signals are specific to the area *and* the date range you selected. The same filter can be reliable in one city and unavailable in another, or reliable for recent years and unavailable further back.
## 1. The Area Has Limited Coverage
Shovels covers approximately **2,000 jurisdictions** representing about **85% of the US population**. Some cities and counties are not yet in our system, and coverage density varies by region.
**What to do:**
* Check the [Coverage Dashboard](https://www.shovels.ai/coverage) to see what's available in your area
* Try broadening your search to county or state level
* Coverage expands monthly — an area with thin data today may improve in coming months
## 2. Your Filters Are Too Narrow
Shovels Online uses **AND** logic, so the more filters you apply, the smaller your result set. A search for a specific permit type, in a specific city, within a specific date range can return very few results even in well-covered areas.
**What to do:**
* Remove one filter at a time and see where results increase
* Broaden the date range
* Search at zip code or county level instead of city
* Try a permit type tag (e.g. `solar`) instead of a keyword
The search system does not account for typos or terminology variations. Contractors may use different abbreviations (like "SFR" instead of "single family residence"), so try alternatives.
## 3. Recent Permits May Not Be Indexed Yet
Shovels refreshes data twice a month, and there is a typical **1–2 month lag** between when a permit is issued and when it appears in Shovels. Permits filed in the last 4–8 weeks may not be visible yet.
**What to do:**
* If you're looking for very recent activity, check back after the next refresh
* For time-sensitive needs, contact [support@shovels.ai](mailto:support@shovels.ai) and we can check the pipeline
## 4. The Jurisdiction Uses Limited Digitization
Even within covered jurisdictions, some local governments have incomplete or inconsistently formatted permit records. This can result in sparse data for specific cities or permit types.
**What to do:**
* Email [support@shovels.ai](mailto:support@shovels.ai) with the specific jurisdiction — we can investigate and flag it for improvement
## Still Not Finding What You Need?
Email [support@shovels.ai](mailto:support@shovels.ai) with:
* The specific area or jurisdiction you're searching
* The permit type or contractor you're looking for
* What results you expected vs. what you got
We'll investigate and let you know what's happening.
## Related Articles
* [What areas does Shovels cover?](/docs/knowledge-base/data/geographic/coverage-areas)
* [How often is data refreshed?](/docs/knowledge-base/data/quality/refresh-frequency)
* [Understanding jurisdictions](/docs/knowledge-base/data/geographic/jurisdictions)
* [Search functionality](/docs/knowledge-base/shovels-online/search-functionality)
* [Filters and sorting](/docs/knowledge-base/shovels-online/filters-and-sorting)
# Historical Data Coverage
Source: https://docs.shovels.ai/docs/knowledge-base/data/quality/historical-data
Learn how far back Shovels' historical permit data extends and what affects historical coverage by jurisdiction.
Shovels' data coverage extends back to provide substantial historical context for analysis.
## Coverage Timeframe
Our data extends back to **at least 2010** for all jurisdictions, with many jurisdictions offering data that goes even further back.
## Why 2010?
This timeframe was a technical architecture decision based on:
* Majority of customer feedback
* Common use cases requiring \~10-15 years of history
* Data availability from jurisdictions
## Limitations
In some cases, jurisdictions don't pull historical data into their online systems, which can limit how far back certain records go.
Our coverage of the past decade-plus provides sufficient historical context for most analysis needs, including tracking long-term trends and contractor history.
## What You Can Do With Historical Data
* Track property improvement history
* Analyze contractor permit patterns over time
* Understand market trends
* Research long-term construction activity
## Related Articles
* [Geographic coverage](/docs/knowledge-base/data/geographic/coverage-areas)
* [Data refresh frequency](/docs/knowledge-base/data/quality/refresh-frequency)
# Data Labeling and Annotation Process
Source: https://docs.shovels.ai/docs/knowledge-base/data/quality/labeling-process
Understand how Shovels ensures high-quality, accurately classified permit data through our rigorous annotation and validation methodology.
Shovels employs a rigorous data labeling and annotation process to ensure high-quality, accurately classified permit data.
## Our Approach
### Multiple Independent Annotators
Each record is labeled by **multiple independent annotators**. When their responses diverge, we manually review and resolve the discrepancies.
### Validation Sample Size
The validation sample size is proportionate to each category's representation in the dataset:
* Typically **1-5% of overall data**
* Ensures adequate validation points for every category
## Golden Dataset Methodology
A key aspect of our methodology is having annotators **independently solve the task** rather than validate model outputs.
This approach:
* **Prevents annotator bias**
* **Creates a "golden dataset"** of correct answers
* **Enables benchmarking** of new model outputs across iterations without requiring fresh human validation each time
## Why This Matters
This approach is particularly effective for accurately classifying permit descriptions, which often contain:
* Industry-specific terminology
* Abbreviations
* Inconsistent formatting
## Accuracy Results
Our case study on using specialist participants for data labeling shows how we achieved **98% accuracy** in our classifications by incorporating a panel of experts from the construction industry.
Learn more in our [blog post on data labeling](https://www.shovels.ai/blog/) with construction industry specialists.
## Related Articles
* [Data verification methods](/docs/knowledge-base/data/quality/verification-methods)
* [Key differentiators](/docs/knowledge-base/getting-started/key-differentiators)
# How Often Does Shovels Update Its Data?
Source: https://docs.shovels.ai/docs/knowledge-base/data/quality/refresh-frequency
Shovels refreshes data twice a month, adding 5-10 million new permits and 1-5 million status updates each cycle. IDs are generally stable but can occasionally change.
**Shovels refreshes data twice a month, adding 5-10 million new permit records and 1-5 million status updates nationwide each cycle.** IDs are generally stable across updates but can occasionally change as underlying data evolves.
## Update Schedule
Data is refreshed **twice a month**. The underlying database is updated first, then made available through the API and Shovels Online. This process can take up to a week after the initial database refresh.
## Volume Per Cycle
Nationwide, each refresh cycle adds:
* **5-10 million new records** to the dataset
* **1-5 million status updates** to existing records
## How Updates Work
When a permit status or other data field changes:
1. The jurisdiction updates it in their platform
2. We access the updated information
3. We clean up and standardize the data
4. Updates are released twice a month
## ID Stability
Shovels IDs are generated by hashing key attributes of each record. This means IDs are deterministic and stable as long as the underlying attributes remain the same. However, IDs can change when those attributes are updated:
* **Permit IDs** can change if the jurisdiction updates metadata that feeds into the hash (e.g., corrected permit details or jurisdiction boundary changes)
* **Address IDs** can change if jurisdiction boundaries shift or address attributes are corrected
* **Contractor IDs** can change as we discover new permit data that reveals additional details about a contractor (see [why contractor IDs can change](/docs/knowledge-base/data/contractors/id-changes))
In practice, these changes are infrequent. Most IDs remain stable across refreshes.
If you cache IDs and encounter not-found responses, use other record attributes (permit number, address, business name) to look up the updated ID. We maintain changelogs — contact [support@shovels.ai](mailto:support@shovels.ai) if ID changes affect your workflow.
## Related Articles
* [EDL deliveries](/docs/knowledge-base/edl/monthly-deliveries)
* [Data sources](/docs/knowledge-base/data/quality/data-sources)
* [Why am I getting so few results?](/docs/knowledge-base/data/quality/few-results)
# How Shovels Verifies Data Accuracy
Source: https://docs.shovels.ai/docs/knowledge-base/data/quality/verification-methods
Learn about the methods Shovels uses to verify data accuracy, including address standardization and contractor validation.
Shovels verifies data accuracy through multiple external sources to ensure reliability.
## Address Verification
For address standardization, we cross-reference details using **four different address sources**:
* National Address Dataset from the US Census
* Open Address dataset
* Simple Maps
* ESRI
This multi-source approach ensures:
* Consistent formatting
* Accurate geocoding
* Reliable location identification
## Contractor Verification
We match contractor information against:
* **Publicly available state license files**
* **Business registration records**
This ensures contractors in our database are:
* Properly licensed
* Legitimate operators in their respective fields
## Data Labeling Process
For permit classification, we employ a rigorous annotation process:
* Multiple independent annotators label each record
* Manual review resolves divergent responses
* Validation sample size is 1-5% of overall data
Our approach has achieved **98% accuracy** in classifications, validated by construction industry experts.
## Golden Dataset Methodology
Annotators independently solve tasks rather than validating model outputs. This:
* Prevents annotator bias
* Creates a "golden dataset" of correct answers
* Enables benchmarking of new model outputs without fresh validation
## Related Articles
* [Data labeling process](/docs/knowledge-base/data/quality/labeling-process)
* [Data sources](/docs/knowledge-base/data/quality/data-sources)
# Understanding the Homeowner Field
Source: https://docs.shovels.ai/docs/knowledge-base/data/residents/homeowner-field
Learn how the probabilistic homeowner feature in Shovels resident data is determined and validated through consensus methodology.
The probabilistic homeowner feature in Shovels' residents data indicates whether a person is likely the property owner.
## Data Source
The homeowner data is sourced from our data co-op members and represents a **consensus-driven determination** of property ownership.
## Validation Methods
### Consensus
We require **3+ different data signals** to confirm data linkages, with reliability ensured through:
* Exhaustive validation methods
* Confirmation from actual usage
* Multiple data point verification
For example, the presence of a unit number is considered as part of this compilation.
### Conformation
This process ensures that data within the "Consensus" realm:
* Represents frequent occurrences
* Adheres to proper formatting and data standards
* Cannot be invalidated using existing signals
## Using the Homeowner Field
### In EDL Data
Filter for homeowners using:
```
HOMEOWNER = 'Y'
```
### Combined with Other Features
You can use additional features to identify homeownership patterns:
* Income range
* Children present
* Net worth
Combining the homeowner field with demographic indicators helps identify trends at certain demographic levels.
## Consumer Attributes Dataset
We also offer additional features in our "Consumer Attributes" dataset including:
* Number of kids
* Grandparent in household
* Other household composition data
## Related Articles
* [Resident data overview](/docs/knowledge-base/data/residents/resident-data)
* [Resident email contact information](/docs/knowledge-base/edl/resident-contact-info)
# Understanding Shovels Resident Data
Source: https://docs.shovels.ai/docs/knowledge-base/data/residents/resident-data
Learn how Shovels assembles and validates resident demographic data, including data sources, available features, and how to access it via API.
Shovels provides resident data features including demographic information compiled from multiple sources.
## Available Data Features
### Demographics
* Gender
* Age range
* Income range
### Additional Consumer Attributes
* Number of kids
* Grandparent in household
* Net worth
* And more
## Data Sources
Shovels' resident data comes from multiple non-LinkedIn sources. We assemble data about individuals via common linkages between:
* Person's name
* Workplace
* Personal address
* Business or personal contact information
## Data Compilation Process
Our process uses **consensus and observation date methodology** to select the most likely and current data points for each individual.
### Consensus Validation
We require 3+ different data signals to confirm data linkages, with reliability ensured through exhaustive validation methods and confirmation from actual usage.
### Conformation
This process ensures data within the "Consensus" realm not only represents frequent occurrences but also adheres to proper formatting and data standards.
The data includes both directly observed information and modeled/probabilistic data.
## Accessing Resident Data
### Via API
To access resident contact information, pass the `address_id` from a permit object into the [residents endpoint](/api-reference/addresses/get-residents):
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
GET /v2/addresses/{address_id}/residents
```
This returns contact info like name, phone, and email for occupants associated with the address.
### Via EDL
EDL customers have access to:
* `PERSONAL_EMAILS` - Personal email addresses
* `BUSINESS_EMAIL` - Business email addresses
* `HOMEOWNER` field - Filter for `HOMEOWNER = Y`
## Using Demographics for Homeowner Analysis
You can use available features like:
* Income range
* Children present
* Net worth
To identify trends that could help determine homeownership likelihood at certain demographic levels.
## Related Articles
* [Homeowner field explained](/docs/knowledge-base/data/residents/homeowner-field)
* [Data Dictionary](https://www.shovels.ai/data-dictionary)
# Contractor Deduplication in EDL
Source: https://docs.shovels.ai/docs/knowledge-base/edl/contractor-deduplication
Understand how contractor IDs are deduplicated in Shovels data, including state-level deduplication and contractor group relationships.
Shovels assigns unique contractor IDs and works to deduplicate contractors across the permits we collect.
## How Deduplication Works
We assign a **unique contractor\_ID** to any contractor we see in our system and work to ensure this ID is:
* Deduplicated within the dataset
* A unique identifier for each contractor
## Contractor IDs in Permits
In our permits table, we include the `contractor_ID` whenever there is an associated contractor.
## Contractor Groups
If we believe a unique contractor is associated with a larger group or parent company, we also associate that `contractor_ID` with a `contractor group_ID`.
**Example:** If we see a local plumber and believe they are a local branch of a regional plumbing company, both would share the same `group_ID`.
## State-Level Deduplication
Currently, we deduplicate contractors **per state**. This means:
* The same contractor ID will **not** appear across different states
* If ABC Plumbing Inc does business in both CA and AZ, it will have **2 unique contractor\_IDs**
We are working on creating a national contractor ID system. Check our blog for the latest announcements.
## The is\_representative Field
For EDL deliveries, the `is_representative` field indicates whether a contractor record is the main representative of a `group_id`.
| Field | Meaning |
| --------------------------- | ----------------------------------------------------------------------------- |
| `is_representative = TRUE` | This record contains the canonical/preferred information for the entire group |
| `is_representative = FALSE` | This is a related contractor in the group |
### Why This Matters
This feature helps:
* Prevent double-counting of organizations operating under multiple names
* Avoid reaching out to the same organization multiple times
* Enable more accurate market analysis
## Related Articles
* [is\_representative field explained](/docs/knowledge-base/edl/is-representative-field)
* [EDL overview](/docs/knowledge-base/edl/overview)
# EDL Data Formats and File Sizes
Source: https://docs.shovels.ai/docs/knowledge-base/edl/data-formats
Learn about the available data formats and typical file sizes for Shovels Enterprise Data License deliveries.
Shovels EDL data can be delivered in various formats to meet your team's needs.
## Available Formats
| Format | Description |
| ----------- | ---------------------------------------------------- |
| **Parquet** | Preferred format, optimized for efficient processing |
| **CSV** | Universal compatibility |
| **JSON** | For systems that prefer JSON |
## Typical File Size
Shovels data is typically delivered as **15-20 GB of Parquet files**, optimized for efficient data processing and analysis in modern data environments.
## Delivery Destinations
Data can be pushed directly to:
* **Snowflake**
* **Databricks**
* **BigQuery**
Or delivered as files for manual import.
## Why Parquet?
Parquet is the preferred format because it:
* Compresses efficiently
* Supports columnar storage
* Works well with modern data tools
* Enables fast query performance
## Choosing Your Format
The preferred format depends on your team's:
* Data infrastructure
* Analysis tools
* Processing requirements
Contact your account representative to discuss which format works best for your use case.
## Related Articles
* [EDL overview](/docs/knowledge-base/edl/overview)
* [EDL deliveries](/docs/knowledge-base/edl/monthly-deliveries)
# Understanding the is_representative Field
Source: https://docs.shovels.ai/docs/knowledge-base/edl/is-representative-field
Learn how the is_representative field in EDL data identifies the main contractor record within a group of related contractors.
In Shovels' Enterprise Data License (EDL) deliveries, the `is_representative` field helps you understand contractor group relationships.
## What It Means
The `is_representative` field is a boolean that indicates whether a contractor record is the **main representative** of a `contractor group_id`.
| Value | Meaning |
| ------- | ----------------------------------------------------------------------------- |
| `TRUE` | This record contains the canonical/preferred information for the entire group |
| `FALSE` | This is a related contractor in the group, not the primary record |
## How Group IDs Work
The `group_id` is a unique identifier that links together multiple contractors operating under the same parent organization.
**Example:** A national HVAC company might pull permits under regional subsidiaries. Our grouping algorithm:
1. Identifies these relationships
2. Links them with a shared `group_id`
3. Designates one record as the representative
## Why This Matters
### Prevent Double-Counting
When analyzing large contractor datasets, `is_representative` helps prevent counting the same organization multiple times under different names or licenses.
### Avoid Duplicate Outreach
Use the representative record to avoid contacting the same organization through different subsidiary entries.
### Accurate Market Analysis
Get a true count of unique contractor organizations, not just contractor records.
## Using is\_representative
```sql theme={"theme":{"light":"css-variables","dark":"css-variables"}}
-- Get unique contractor organizations (one per group)
SELECT * FROM contractors
WHERE is_representative = TRUE
-- Get all contractors including subsidiaries
SELECT * FROM contractors
```
## Related Articles
* [Contractor deduplication](/docs/knowledge-base/edl/contractor-deduplication)
* [EDL overview](/docs/knowledge-base/edl/overview)
# Requesting Manual Reports
Source: https://docs.shovels.ai/docs/knowledge-base/edl/manual-reports
Learn how to request custom manual reports from Shovels support when you need specific data beyond standard EDL deliveries.
Users can request manual reports from the Shovels support team for specific data needs.
## How to Request a Manual Report
### Contact Support
Reach out to the Shovels support team via:
* Email: [sales@shovels.ai](mailto:sales@shovels.ai)
* [Contact Us form](https://www.shovels.ai/contact)
* Phone
### Specify Your Needs
When requesting a report, clearly describe:
* **Type of report** you need
* **Specific data points** required
* **Desired format** (CSV, Parquet, etc.)
* **Geographic scope** (states, cities, etc.)
* **Date ranges** if applicable
### Provide Examples
If possible, share an example of the report format you're looking for to help create the report to your specifications.
## Example Use Cases
* **Contractor-specific data** - "I need a list of all solar permits pulled by Vivint or Solar City"
* **Historical data** - "I need an all-time history report, not just the last 12 months"
* **Custom filters** - "Commercial permits for motels, hotels, and restaurants in California"
## Current Limitations
* The web app allows access to data only for the last 12 months unless manually adjusted
* There may be limits on export sizes, so request smaller batches or use specific filters if needed
## Best Practices
1. Be as specific as possible about your data requirements
2. Include examples when you can
3. Specify any particular format requirements
4. Indicate urgency if applicable
As the platform continues to evolve, more self-service report generation capabilities are expected in the future.
## Related Articles
* [EDL overview](/docs/knowledge-base/edl/overview)
* [Sample records for verification](/docs/knowledge-base/edl/sample-records)
# How EDL Deliveries Work
Source: https://docs.shovels.ai/docs/knowledge-base/edl/monthly-deliveries
Understand how Shovels delivers twice-monthly data updates to EDL customers using the overwrite model while maintaining ID consistency.
For Enterprise Data License (EDL) customers, Shovels employs an **overwrite approach** when pushing data to your data warehouse.
## How It Works
Each delivery is treated as a **fresh table**, completely replacing the previous delivery's data with the updated dataset.
### Benefits
This approach simplifies data management by:
* Eliminating the need to merge incremental updates
* Providing a complete, consistent dataset each delivery
* Avoiding complex change-tracking logic
## ID Stability
**Critically important:** Our IDs remain static and don't change from release to release:
* Permit IDs
* Address IDs
* Contractor IDs
This consistency ensures that any scripts, analyses, or processes relying on these IDs won't be impacted by a refresh.
The stability of our ID system allows customers to build reliable data pipelines and maintain referential integrity.
## What's Included
Each delivery includes:
* All historical data
* New permits added since last delivery
* Updates to existing records (status changes, etc.)
This provides a complete and current view of the construction permitting landscape.
## Tracking Changes
For customers who need to track changes between deliveries, we recommend:
* Using the `first_seen_date` field to identify new records
* Comparing status change indicators between deliveries
* Maintaining a copy of previous deliveries for comparison
## Delivery Schedule
Standard delivery is **twice a month**, on the same schedule as our main database. Custom delivery cadences are available for enterprise contracts with specific SLA requirements — contact [sales@shovels.ai](mailto:sales@shovels.ai) to discuss one.
## Related Articles
* [Record tracking](/docs/knowledge-base/edl/record-tracking)
* [EDL overview](/docs/knowledge-base/edl/overview)
# What is Shovels EDL (Enterprise Data License)?
Source: https://docs.shovels.ai/docs/knowledge-base/edl/overview
Shovels EDL delivers complete permit/contractor datasets to Snowflake, BigQuery, or Databricks. Best for bulk analysis with more fields than API.
**The Shovels Enterprise Data License (EDL) delivers complete permit and contractor datasets directly to your data warehouse (Snowflake, BigQuery, Databricks) or as Parquet files.** EDL includes more fields than the API and is best for bulk data analysis, while the API is better for real-time lookups and integrations.
## When to Choose EDL
The **EDL** is ideal when you need:
* Every contractor across multiple counties
* All available data fields
* Data delivered directly to your data warehouse
* Complete dataset updates twice a month
* Complex SQL joins and queries
The **API** is better for:
* Looking up specific addresses and contractors
* Product integrations
* Local lead lists
* Real-time queries
## Data Delivery Options
### Destinations
EDL data can be pushed directly into:
* Snowflake
* Databricks
* BigQuery
* Or delivered as Parquet files
### File Format and Size
Data is typically delivered as **15-20 GB of Parquet files**, optimized for efficient data processing in modern data environments.
Other available formats:
* Parquet (preferred)
* CSV
* JSON
## What's Included
EDL provides access to:
* Complete permit dataset
* Full contractor database
* Resident data
* All available fields (more than API)
* Historical data
## Key Features
### More Fields Than API
Flat file deliveries contain significantly more data fields than API access, including:
* Applicant email
* Owner email
* Additional property details
### No Latency Issues
Direct data delivery eliminates API latency concerns for large-scale analysis.
### Twice-Monthly Updates
Each delivery includes all historical data plus new permits and updates, providing a complete and current view.
## Ongoing Support
Yes, ongoing support is available through regular calls to help interpret and work through the data as needed.
## Getting Started
Contact [sales@shovels.ai](mailto:sales@shovels.ai) to discuss your enterprise data needs.
## Related Articles
* [EDL deliveries explained](/docs/knowledge-base/edl/monthly-deliveries)
* [Record tracking](/docs/knowledge-base/edl/record-tracking)
* [Pricing structure](/docs/knowledge-base/getting-started/pricing-structure)
# Tracking New vs Modified Records in EDL
Source: https://docs.shovels.ai/docs/knowledge-base/edl/record-tracking
Learn how to use first_seen_date and other fields to identify new versus pre-existing records in EDL data deliveries.
For EDL customers receiving twice-monthly data overwrites, tracking whether a record is new versus pre-existing and modified can be accomplished using the `first_seen_date` field.
## Using first\_seen\_date
The `first_seen_date` field in the PERMITS table indicates when Shovels first discovered the permit in our data collection process.
### Identifying New Records
Compare `first_seen_date` with your previous data delivery:
* Records where `first_seen_date` falls **after** your last update are **new**
* Records where `first_seen_date` falls **before** your last update are **existing**
### Example Logic
```sql theme={"theme":{"light":"css-variables","dark":"css-variables"}}
-- New permits since last delivery (Jan 15)
SELECT * FROM permits
WHERE first_seen_date > '2024-01-15'
```
## Tracking Modifications
Currently, there is no specific flag indicating changes in records. To identify modifications to existing records:
1. **Maintain previous deliveries** - Keep a copy of your previous data
2. **Compare key fields** - Focus on fields that might change:
* Status
* Valuation
* Dates
* Other attributes
3. **Build comparison logic** - Create queries that identify differences
We're actively working on enhancing our data delivery system to include explicit change tracking flags in future releases.
## Best Practices
1. Store the `first_seen_date` for your comparison baseline
2. Maintain at least one previous delivery for modification tracking
3. Focus comparisons on fields relevant to your use case
4. Consider building automated comparison pipelines
## Related Articles
* [EDL deliveries](/docs/knowledge-base/edl/monthly-deliveries)
* [EDL overview](/docs/knowledge-base/edl/overview)
# Getting Resident Contact Information in EDL
Source: https://docs.shovels.ai/docs/knowledge-base/edl/resident-contact-info
Learn how to access resident and homeowner email contact information through Shovels EDL data, including which tables and fields to use.
Resident and homeowner email contact information is available through multiple fields in EDL data.
## Where to Find Contact Information
### PERMITS Table
| Field | Description |
| ----------------- | ------------------------------------------------------------------------------------------------- |
| `APPLICANT_EMAIL` | Email of the permit applicant (may be homeowner, builder, or contractor depending on who applied) |
| `OWNER_EMAIL` | Property owner email at time of permit submission |
Note: Owner email reflects ownership at the time of permit submission, which may have changed if the permit is old.
### RESIDENTS Table
| Field | Description |
| ----------------- | ---------------------------------------------------- |
| `PERSONAL_EMAILS` | Personal email addresses for residents |
| `BUSINESS_EMAIL` | Business email addresses |
| `HOMEOWNER` | Filter for `HOMEOWNER = Y` to get homeowner contacts |
## Identifying Homeowners
To get homeowner-specific contact information:
```sql theme={"theme":{"light":"css-variables","dark":"css-variables"}}
SELECT personal_emails, business_email, name, phone
FROM residents
WHERE homeowner = 'Y'
```
## API Access
For API users, access resident contact info by:
1. Get the `address_id` from a permit object
2. Pass it to the [residents endpoint](/api-reference/addresses/get-residents)
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
GET /v2/addresses/{address_id}/residents
```
This returns contact info including name, phone, and email for occupants associated with the address.
## EDL vs API Differences
| Data Point | EDL | API |
| --------------- | --- | --- |
| Applicant email | Yes | No |
| Owner email | Yes | No |
| Personal emails | Yes | Yes |
| Business email | Yes | Yes |
## Related Articles
* [Resident data overview](/docs/knowledge-base/data/residents/resident-data)
* [Homeowner field](/docs/knowledge-base/data/residents/homeowner-field)
* [EDL overview](/docs/knowledge-base/edl/overview)
# Requesting Sample Records
Source: https://docs.shovels.ai/docs/knowledge-base/edl/sample-records
Learn how to request sample records from Shovels to verify that our data fits your specific use case before committing to a subscription.
If you're interested in verifying data for a specific use case, you can request sample records from Shovels.
## How to Request Sample Records
### 1. Specify Your Use Case
Clearly outline the type of data you need:
* Geographic scope (states, cities, counties)
* Property types (residential, commercial, industrial)
* Permit types (solar, HVAC, electrical, etc.)
* Date ranges
### 2. Contact Support
Reach out to customer support with your request:
* Email: [sales@shovels.ai](mailto:sales@shovels.ai)
* [Contact form](https://www.shovels.ai/contact)
### 3. API Option
If you're considering using the API, you can also explore the data yourself on the Free plan:
* [Create a free account](https://app.shovels.ai/login?mode=register)
* Get 250 free API requests to test
## Example Requests
When making your request, be as specific as possible:
* "Could I have sample records of recent commercial permits for motels, hotels, and restaurants in California?"
* "I need sample solar installation permits from Texas for the last 6 months"
* "Can I see sample contractor records for HVAC contractors in Florida?"
## What to Expect
Sample records typically include:
* A representative subset of the data
* All available fields for your use case
* Recent and relevant records
The more specific your request, the more relevant the samples we can provide.
## Related Articles
* [EDL overview](/docs/knowledge-base/edl/overview)
* [Manual reports](/docs/knowledge-base/edl/manual-reports)
* [Free plan guide](/docs/knowledge-base/getting-started/free-trial-guide)
# Should I Use the API or an Enterprise Data License?
Source: https://docs.shovels.ai/docs/knowledge-base/getting-started/api-vs-edl
The Shovels API retrieves records on demand and is metered per record. An Enterprise Data License delivers the full dataset to your warehouse on a schedule, with fields the API doesn't expose.
**Use the API when your application asks for records as it needs them. Use an Enterprise Data License (EDL) when your team wants the whole dataset sitting in your own warehouse.** The difference is not how much data you can get — it's whether Shovels answers your queries or hands you the tables.
## The Short Version
| | Shovels API | Enterprise Data License |
| -------------------- | ---------------------------------------------- | ----------------------------------------------------- |
| **How you get data** | Request records as you need them | The full dataset delivered to your warehouse |
| **Where it lives** | Shovels' servers | Snowflake, BigQuery, Databricks, or parquet files |
| **How it's priced** | Credits, one per record | A contract sized to your needs |
| **Fields** | The documented API schema | More, including fields the API doesn't expose |
| **Freshness** | Current as of the last refresh, on request | Delivered twice a month, or a custom cadence |
| **Best for** | Product features, lookups, automated workflows | Bulk analysis, modelling, joins against your own data |
## Choose the API When
* You are building a product feature and need permits for one address, contractor, or city at a time
* Your queries are unpredictable — you don't know in advance which records you'll want
* You want to start today without a contract, on any plan including Free
* Your volumes are measured in thousands of records rather than the whole country
The API is metered per record, so cost tracks usage. That works well when usage is spiky or narrow, and badly when you intend to pull everything.
## Choose an EDL When
* You need **every** record in a category — every contractor across a state, every permit in a metro — rather than a selection
* You want to join Shovels data against your own tables in SQL
* You need fields the API doesn't expose, such as applicant and owner email addresses and additional property detail
* You are training models or running analysis where paging through an API would be the bottleneck
* You want the data in Snowflake, BigQuery, Databricks, or as parquet files in your own object storage
## The Practical Tells
A few signals that usually settle it:
* **If you find yourself planning to page through the API to download everything**, you want an EDL. That is the shape the API is worst at.
* **If you need one record in response to a user action**, you want the API. A warehouse copy can't answer a live lookup.
* **If you need a field you can't find in the API reference**, ask about an EDL — the delivered dataset is wider.
* **If you're not sure yet**, start on the API. It costs nothing to try on the Free plan, and what you learn about your own access pattern is the best input to an EDL conversation.
Plenty of customers use both: the API inside a product, and an EDL feeding the analytics that sit behind it.
## Getting Started
The API is available on every plan — [create an account](https://app.shovels.ai/login?mode=register) and your key is on the API key tab.
An EDL is a contract, so it starts with a conversation. Email [sales@shovels.ai](mailto:sales@shovels.ai); a sample dataset can be delivered into your own warehouse environment so your team can evaluate coverage and schema before committing.
## Related Articles
* [What is Shovels EDL?](/docs/knowledge-base/edl/overview)
* [How EDL deliveries work](/docs/knowledge-base/edl/monthly-deliveries)
* [Which Shovels plan is right for me?](/docs/knowledge-base/getting-started/choosing-a-plan)
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
# Which Shovels Plan Is Right for Me?
Source: https://docs.shovels.ai/docs/knowledge-base/getting-started/choosing-a-plan
Pick a Shovels plan by what you need: how far back your data goes, whether you export, how many records you pull each month, and how many seats you need.
**Most people can pick a plan by answering four questions: how far back you need data, whether you need to export it, how many records you pull each month, and how many people need access.** Every plan includes every interface, so you are not choosing between the web app, the API, the CLI, and Charlie — you get all of them either way.
## Start With These Four Questions
### 1. How far back do you need permits to go?
In Shovels Online, the Free plan covers the **last 12 months**. Paid plans open the full history — on average 25 years or more, depending on the jurisdiction.
Through the API this limit doesn't apply: every plan, including Free, can query the full historical dataset.
### 2. Do you need to export records?
CSV export from Shovels Online **requires a paid plan**. On Free you can search, filter, view details, and use the map, but the export action is unavailable.
If your work ends in a spreadsheet, a CRM, or a list you hand to someone else, you need a paid plan. If you are answering questions on screen, Free may be enough.
### 3. How many records will you pull each month?
Plans are sized in credits, and one credit is one record — spent when you retrieve records through the API or export them from Shovels Online. Browsing and asking Charlie don't spend credits.
Estimate the records, not the queries: a single export of 5,000 contractors costs 5,000 credits, while a month of searching costs nothing. See [shovels.ai/pricing](https://www.shovels.ai/pricing) for each plan's monthly allowance.
### 4. How many people need their own login?
Free and Basic are single-seat. Pro covers a small team, and Enterprise is sized to your organisation. Current seat counts are on [shovels.ai/pricing](https://www.shovels.ai/pricing).
## What Doesn't Vary by Plan
These are the same whichever plan you choose, so they shouldn't drive the decision:
* **Every interface.** Shovels Online, the API, the CLI, and Charlie are included on all plans and share one credit balance.
* **The full dataset.** No dataset is gated behind a higher plan.
* **Data freshness.** Every plan gets the same twice-monthly refresh.
* **Security posture.** Every plan runs on the same SOC 2 Type II audited infrastructure.
## When a Plan Isn't the Answer
GIS layers are also outside the plan lineup. Hosted feature layers for ArcGIS or QGIS are scoped and provisioned by the Shovels GIS team rather than switched on by a plan — [talk to us](https://www.shovels.ai/contact) about the geographies and permit types you need.
If you need the **entire dataset in your own warehouse** — every permit, contractor, property, and resident record, including fields the API doesn't expose — that is an [Enterprise Data License](/docs/knowledge-base/edl/overview), not a plan. It is a contract rather than a subscription, so it starts with a conversation: [sales@shovels.ai](mailto:sales@shovels.ai).
See [Should I use the API or an EDL?](/docs/knowledge-base/getting-started/api-vs-edl) if you're weighing the two.
## Still Not Sure?
Start on Free. It doesn't expire, needs no credit card, and you can upgrade once you know which limit you hit first — that limit usually tells you which plan you need.
## Related Articles
* [How much does Shovels cost?](/docs/knowledge-base/getting-started/pricing-structure)
* [What's included in the Free plan?](/docs/knowledge-base/getting-started/free-trial-guide)
* [Should I use the API or an EDL?](/docs/knowledge-base/getting-started/api-vs-edl)
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
# What's Included in the Shovels Free Plan?
Source: https://docs.shovels.ai/docs/knowledge-base/getting-started/free-trial-guide
The Shovels Free plan includes permit and contractor search, all filters, and 500 credits per month. No credit card required.
**The Shovels Free plan includes full permit and contractor search, all filters, and 500 credits per month.** No credit card required. Paid plans unlock CSV downloads and larger credit allowances.
**One credit is one record.** Every plan is metered the same way—each record returned counts as one credit, on Free and paid plans alike.
## Features Available on the Free Plan
### Permit & Contractor Search
Users can access the **permit** and **contractor** search functionality from the left side of the user interface.
### Data Availability
* **Shovels Online**: The Free plan includes access to the last 12 months of permit and contractor data. Paid plans unlock all historical data (on average, 25+ years).
* **API**: The Free plan includes access to the full historical dataset—no date restrictions.
### Filters and Sorting
Users can utilize various filters to refine their searches. Filters are available for categories such as:
* Financial
* Building
* Permit
* Contractor
Exporting search results to CSV requires a paid plan. Paid plans can export the full set of matching records and sort them in Excel or Google Sheets.
### Data Refresh Rate
Shovels data is refreshed twice a month. See [data refresh frequency](/docs/knowledge-base/data/quality/refresh-frequency) for details.
## Getting Started
1. [Create a free account](https://app.shovels.ai/login?mode=register) to get started
2. Use the search functionality to explore permits and contractors
3. Apply filters to narrow down your results
4. Explore contractor profiles and permit details
The Free plan is a great way to evaluate Shovels' data quality and coverage before committing to a paid plan.
## Next Steps
* [Learn how Shovels Online works](/docs/knowledge-base/shovels-online/how-it-works)
* [Try Charlie](/docs/knowledge-base/shovels-online/charlie) — the AI agent in Shovels Online
* [Understand our pricing structure](/docs/knowledge-base/getting-started/pricing-structure)
* Contact [sales@shovels.ai](mailto:sales@shovels.ai) for API access or enterprise products
# What Makes Shovels Different From Other Permit Data Providers?
Source: https://docs.shovels.ai/docs/knowledge-base/getting-started/key-differentiators
Shovels differentiates with: 1) USPS-standardized addresses, 2) contractor grouping linking related businesses, 3) 98% AI accuracy, 4) sophisticated deduplication, 5) every interface included on every plan.
**Shovels stands out from other permit data providers through five key differentiators: (1) USPS-standardized, geocoded addresses, (2) comprehensive contractor data with parent-company grouping, (3) 98% classification accuracy using AI validated by industry experts, (4) sophisticated deduplication that maintains consistent IDs across updates, and (5) every way of reaching the data included on every plan.** We source directly from jurisdictions—never from third-party resellers.
## Key Differentiators
### 1. Very Clean Data
Our addresses are standardized to USPS format and geotagged with coordinates. We cross-reference against multiple authoritative datasets:
* National Address Dataset from the US Census
* Open Address dataset
* Simple Maps
* ESRI
### 2. Comprehensive Contractor Data
Our contractor data is derived directly from permit metadata, including **contractor grouping** that links contractors under the same parent organization. This helps you understand relationships between:
* Local branches and regional offices
* DBA names and parent companies
* Related business entities
### 3. Modern Data Infrastructure
We use AI and machine learning with a documented **98% accuracy** rate validated by construction industry experts. Our rigorous data labeling process involves:
* Multiple independent annotators
* Manual review for divergent responses
* Golden datasets for benchmarking
### 4. Sophisticated Deduplication
Our system maintains a unique identifier for each permit and updates records as permit statuses change, rather than creating duplicate entries. This means:
* Consistent permit IDs throughout the lifecycle
* No duplicate records from status updates
* Reliable tracking across data refreshes
### 5. Every Interface Included
You are not buying a seat in one tool. Every plan includes the same ways of reaching the data, and they draw on one shared credit balance—so you can move between them without changing plans or negotiating an add-on:
* **Shovels Online** — search, filter, map, and export in the browser
* **Shovels API** — REST endpoints for integrations and automated workflows
* **Shovels CLI** — a single binary with JSON output, built for terminals and AI agents
* **Charlie** — the AI agent inside Shovels Online, opened with the **Agent** button
See [choosing an interface](/docs/knowledge-base/getting-started/pricing-structure) for which one fits a given job.
Hosted **GIS layers** for ArcGIS and QGIS sit outside the plan lineup: they are scoped and provisioned by our GIS team, and are also available as an Enterprise Data License delivery format.
Learn more about our data quality approach in our [data labeling process](/docs/knowledge-base/data/quality/labeling-process) article.
## Data Sources
We source data directly from jurisdictions through:
* Direct relationships with local governments
* Integration with online permitting portals
* Public records requests where needed
We don't purchase data from other sources—we get it straight from the source.
# How Much Does Shovels Cost?
Source: https://docs.shovels.ai/docs/knowledge-base/getting-started/pricing-structure
Shovels Online and the API share credit-based plans—Free, Basic, Pro, and Enterprise. See current prices at shovels.ai/pricing; Enterprise Data License (EDL) pricing is custom.
**Shovels Online and the Shovels API share one credit-based plan lineup: Free, Basic, Pro, and Enterprise.** Choose Shovels Online for quick lookups, the API for integrations, or an Enterprise Data License (EDL) for bulk analysis. EDL pricing is custom—contact [sales@shovels.ai](mailto:sales@shovels.ai).
## Plans
* **Free** — explore the data before you commit.
* **Basic** — individuals and independent research.
* **Pro** — small teams that need more credits and seats.
* **Enterprise** — custom credits, seats, and support for larger organizations.
For the current price, monthly credits, and seats on each plan, see [shovels.ai/pricing](https://www.shovels.ai/pricing), then [create an account](https://app.shovels.ai/login?mode=register) or [sign in](https://app.shovels.ai/login) to subscribe.
## How Credits Work
Shovels plans are credit-based: one credit is one record returned. See [How do API credits work?](/docs/knowledge-base/api/basics/request-counts) for how credits are counted and when they reset.
## Enterprise Data License (EDL)
For Enterprise Data options, please contact [sales@shovels.ai](mailto:sales@shovels.ai) to discuss your custom needs.
## Choosing an Interface
Every interface below is included on every plan, and they all draw on the same monthly credit balance—so you can move between them without changing plans.
| Interface | Best For | What It Is |
| ------------------ | -------------------------- | ------------------------------------------- |
| **Shovels Online** | Quick lookups, no code | Web search, filters, map, and CSV export |
| **Shovels API** | Product integrations | REST endpoints for programmatic queries |
| **Shovels CLI** | Terminals and AI agents | Single binary, JSON output, no dependencies |
| **Charlie** | Questions in plain English | The AI agent inside Shovels Online |
**When to choose the API vs an EDL:**
* The **API** is great for looking up addresses and contractors, product integrations, and local lead lists
* An **EDL** is ideal when you need every contractor across multiple counties with all available fields
## Contact Sales
For questions about pricing or to discuss your specific needs:
* Email: [sales@shovels.ai](mailto:sales@shovels.ai)
* Phone: [1-800-511-7457](tel:+18005117457)
## Related Articles
* [How do I cancel my subscription?](/docs/knowledge-base/shovels-online/cancel-subscription)
* [Refund policy](/docs/knowledge-base/company/refund-policy)
* [What's included in the Free plan?](/docs/knowledge-base/getting-started/free-trial-guide)
# Shovels Glossary
Source: https://docs.shovels.ai/docs/knowledge-base/glossary
Definitions of key terms used in Shovels permit, contractor, and decision data: geo_id, AHJ, jurisdiction, permit status, contractor ID, zoning, rezoning, and more.
A comprehensive glossary of terms used throughout Shovels documentation, API, and data products.
## A
### Absence Search
A property search for the properties that have **no** permit of a given work type on record, written as a `-`-prefixed tag (e.g. `permit_tags=-solar` in the API, `--permit-tags "-solar"` in the CLI) on the Search Properties endpoint. Because a property can look permit-free due to data coverage rather than reality, every absence answer carries [trust fields](#trust-fields) scoring its reliability.
### Address ID
A unique identifier assigned by Shovels to each distinct address in our database. The address ID remains consistent even if the address formatting varies across different permits, enabling you to link all permits at a single property.
### AHJ (Authority Having Jurisdiction)
The governmental body responsible for issuing building permits in a specific geographic area. An AHJ is typically a city or county government that establishes building codes, conducts inspections, and maintains permit records. The United States has approximately 20,000 AHJs.
### API Key
A unique authentication token required to access the Shovels API. Include it in the `X-API-Key` header of every request. Access your key at [app.shovels.ai/account](https://app.shovels.ai/account?tab=apikey).
### Active (Permit Status)
A permit status indicating the permit has been approved and issued. Work can proceed. Defined by the `issue_date` field.
### Area Rezoning
A zoning change applied to a larger geographic area, often initiated by municipalities as part of comprehensive planning efforts. Area rezonings indicate broader market shifts and can affect property values across many parcels.
## C
### Certificate of Occupancy
The final approval issued by a jurisdiction certifying that a completed building complies with applicable codes and is safe to occupy. It marks the end of the development lifecycle that begins with a zoning decision and continues through permitting and construction.
### Charlie
The AI agent inside [Shovels Online](https://app.shovels.ai), opened with the **Agent** button. Charlie answers natural-language questions about permit and contractor data using the full Shovels dataset.
### Conditional Use Permit
Another term for a Special Use Permit—permission to conduct a specific activity that isn't automatically allowed in a zone but may be approved with conditions. Terminology varies by jurisdiction.
### Contractor ID
A unique identifier assigned by Shovels to each contractor. Contractor IDs are deduplicated within each state, meaning the same contractor operating in multiple states will have separate IDs per state.
### Contractor Group ID
An identifier linking multiple contractors that operate under the same parent organization. For example, regional branches of a national company share the same group ID.
### CLI Config File
A YAML configuration file at `~/.config/shovels/config.yaml` that stores persistent CLI settings like your API key and default preferences. Created with `shovels config set`.
### Coverage Tier
A permit coverage bucket for a jurisdiction—`high`, `medium`, or `low`—reported on every [absence search](#absence-search) row as `trust.coverage_tier`. Higher tiers mean a "no permit on record" answer is more trustworthy for that area.
## D
### Data Horizon
The most recent date past which "no permit since D" is under-observed for a given row (`trust.data_horizon`). If your `permit_from` / `--permit-from` is later than the horizon, the absence answer is a guess about data that hasn't arrived yet. Never null.
### Decision (Shovels Decision)
A structured record of a municipal zoning or land use decision extracted from city council and planning department meetings. Decisions capture rezoning approvals, special use permits, variances, and zoning code modifications—often months before permits are filed.
### Decision Category
The classification of a zoning action type. Primary categories include Spot Rezoning, Area Rezoning, Zoning Code Modification, and Special Use Permit. Subcategories provide additional context such as Inclusionary Zoning, Variance, or Planned Unit Development (PUD).
### Decision Date
The date a municipal zoning or land use decision was made (`decision_date` in the API). Decisions are filtered by date range using the required `decision_from` and `decision_to` parameters when searching.
### Decision ID
A unique identifier assigned by Shovels to each decision record (`id` in the API). Use it with the Get Decisions By ID endpoint to retrieve specific decisions.
### Deduplication
The process of identifying and consolidating duplicate records. Shovels deduplicates permits (maintaining one record per permit even with status updates) and contractors (linking related business entities).
### --dry-run
A CLI flag that prints the resolved HTTP request as JSON—method, URL, and query parameters—without calling the API or consuming credits. Useful for confirming how flags map to API parameters before spending anything.
## E
### EDL (Enterprise Data License)
Shovels' bulk data product for enterprise customers. EDL delivers complete datasets to data warehouses like Snowflake, BigQuery, or Databricks, with more fields than the API and twice-monthly refresh cycles.
### Exit Code (CLI)
A numeric status code returned when a CLI command completes. Used by scripts and AI agents to determine success (0) or error type (1-5): client error, auth error, rate limited, credits exhausted, or server error.
### Expected Miss Rate
The headline trust number on an [absence search](#absence-search): the estimated probability that a returned "no permit on record" answer is wrong because the permit hasn't arrived yet. Reported per API page as `trust_summary.expected_miss_rate`, and in the CLI inside each entry of the [`meta.trust_summaries`](/docs/knowledge-base/cli/absence-and-trust) array.
## F
### File Date
The date when a permit application was submitted to the jurisdiction. This marks the beginning of the permit lifecycle.
### Final (Permit Status)
A permit status indicating the project is complete and has passed final inspection. Defined by the `final_date` field.
### Final Date
The date when a permit received final approval after passing all required inspections.
### First Seen Date
The date when Shovels first discovered a permit in our data collection process. Used by EDL customers to identify new records in each delivery.
## G
### geo\_id
A unified geographic identifier used by Shovels to link addresses to permit data. A geo\_id can represent different geographic levels: a state (`CA`), zip code (`94103`), city, county, or specific address. Use the Address Search API to obtain a geo\_id for any location.
### Geocoding
The process of converting addresses into geographic coordinates (latitude and longitude). Shovels performs geocoding in-house using multiple reference sources including the National Address Dataset, Open Address, Simple Maps, and ESRI.
## I
### Inactive (Permit Status)
A permit status indicating the permit process has stalled. Reasons include failed inspections, permit expiration, or inactivity for more than 180 days.
### Inclusionary Zoning
Zoning requirements or incentives that encourage or require a share of new residential development to be set aside as affordable housing. Appears as a subcategory in Shovels Decision data.
### In Review (Permit Status)
A permit status indicating the application has been submitted and is awaiting approval. Defined by the `file_date` field.
### is\_representative
A boolean field in EDL data indicating whether a contractor record is the primary representative of a contractor group. When `TRUE`, this record contains the canonical information for the entire group.
### Issue Date
The date when a permit was approved and issued by the jurisdiction, allowing construction work to begin.
## J
### Jurisdiction
The geographic area governed by a specific AHJ. Shovels covers approximately 2,000 jurisdictions representing about 85% of the US population.
## L
### Legal Owner
The recorded owner of a property (`legal_owner`), matched on its canonical form so casing variants collapse together. The only Properties filter that works nationwide with no location: pass up to 10 owner names to retrieve a portfolio across the US.
### --limit all
A CLI flag that fetches all available records instead of the default 50-record limit. Automatically handles pagination with an upper bound set by `--max-records` (default: 10,000, max: 100,000).
## N
### NAICS (North American Industry Classification System)
A modern industry classification system used to categorize businesses. More specific than SIC codes but less widely adopted. Available in Shovels contractor data.
### Newsworthiness
A confidence score (0-1) in Shovels Decision data indicating the relevance and significance of a zoning decision. Higher scores suggest decisions with greater potential impact on development activity.
## O
### Over-the-Counter Permit
A permit approved immediately without requiring follow-up inspections. Common for simple projects like solar installations or minor plumbing work. These permits may have a "final" status without a final date.
### Overlay District
A zoning layer applied on top of existing base zoning to add requirements or allowances for a defined area, such as historic preservation, design standards, or transit-oriented development. Overlay districts appear as a subcategory in Shovels Decision data.
## P
### Parquet
A columnar data storage format optimized for analytics. Shovels EDL data is typically delivered as 15-20 GB of Parquet files.
### Permit ID
A unique identifier generated by Shovels for each permit. Unlike permit numbers (which may duplicate across jurisdictions), Shovels permit IDs are globally unique and persistent throughout the permit lifecycle.
### Permit Number (permit\_no)
The permit number assigned by the issuing jurisdiction. Not globally unique—different jurisdictions may assign the same number to different permits. Use Shovels' permit ID for unique identification.
### Planning Commission
A municipal body that reviews zoning, land use, and development proposals and makes decisions or recommendations to a city council. Many Shovels Decisions originate from planning commission meetings.
### Primary Email
The most frequently used email address across all permits for a given contractor. Determined by analyzing frequency across the contractor's permit history.
### Primary Phone
The most frequently used phone number across all permits for a given contractor.
### Property ID
The unique identifier of a property in the Shovels Properties API—the same identifier as the property's address ID. Use it with the Get Properties By ID endpoint, or `shovels properties get`, to retrieve up to 50 property records per call.
### PUD (Planned Unit Development)
A flexible zoning classification for master-planned projects that allows developers to negotiate custom development standards. PUDs often appear as a subcategory in Shovels Decision data.
## R
### API Credit Limit
The number of records you can retrieve per month on your plan. Each record returned counts as one credit, on every plan including Free. See [shovels.ai/pricing](https://www.shovels.ai/pricing) for per-plan allowances and [API credit limits](/docs/knowledge-base/api/basics/credit-limits) for full information.
### API Rate Limit
A technical limit on how quickly you can make API requests. Rate limits protect system stability and ensure fair access for all users. If you exceed the rate limit, you'll receive a 429 Too Many Requests response. See [API rate limits](/docs/knowledge-base/api/basics/rate-limits) for details.
## S
### Schema Command (CLI)
`shovels schema [command-path]` prints the annotated JSON response schema for any data command—field types, descriptions, units, ranges, enums, a jq-ready `field_index`, and the flag-to-parameter map. Runs entirely offline with no API key and no credit cost. Any data command also accepts `--schema` to print its own.
### SIC (Standard Industrial Classification)
An older industry classification system used to categorize businesses. More widely adopted than NAICS codes. Available in Shovels contractor data.
### Special Use Permit
Permission granted by a municipality to conduct a specific activity that isn't automatically allowed in a zone but may be approved with conditions. Also called conditional use permits in some jurisdictions. Special use permits often precede specific business or development activity at a known location.
### Shovels CLI
A single-binary command-line tool for querying U.S. building permit, property, contractor, and decision data. Outputs JSON to stdout and handles pagination, rate limits, and credit tracking automatically. No runtime dependencies required.
### Shovels Online
The web-based application for searching and downloading permit and contractor data. Access at [app.shovels.ai](https://app.shovels.ai).
### Spot Rezoning
A zoning change applied to a specific property or small group of parcels, typically initiated by property owners or developers seeking to change what can be built on their land. Spot rezonings often signal imminent development activity on that specific property.
### Start Date
The first date recorded for a permit in Shovels' system. Generated by Shovels based on collected permit data.
### Suppressed Scopes
The count of coverage scopes removed from an [absence search](#absence-search) result (`suppressed_scopes`). Where coverage for a work type in an area is too thin to answer honestly, those properties are dropped entirely rather than returned as false negatives—so a low-coverage area can legitimately return few or no absence rows.
## T
### Tags
Shovels-assigned categories that classify permits by work type (e.g., solar, HVAC, electrical, roofing). Use the `/list/tags` endpoint to get valid tag values.
### Trust Fields
Confidence metadata attached to every [absence search](#absence-search) answer in the Properties API. Per row: [coverage tier](#coverage-tier), [unresolved rate](#unresolved-rate), [data horizon](#data-horizon), the jurisdiction-join basis and its error bar, and row-level flags. Per page: [`expected_miss_rate`](#expected-miss-rate) and [`suppressed_scopes`](#suppressed-scopes). See [Absence search](/docs/knowledge-base/api/properties/absence-queries).
### trust\_summaries
A CLI-only `meta` array carrying one trust summary per API page fetched, in fetch order. Because `--limit` merges pages and each page's rates are row-weighted over its own rows, no single summary is correct for a merged result—so the CLI reports them unaggregated rather than inventing a combined figure. Omitted entirely when a query has no absence filter. See [CLI absence searches and trust fields](/docs/knowledge-base/cli/absence-and-trust).
## U
### Unfinaled Permit
A permit that was pulled but never finaled, determined from permit **status** rather than dates: a work type is unfinaled at a property when its latest non-final permit is more recent than its latest final permit of that type. Searchable via `permit_tags_unfinaled` on the Search Properties endpoint, or `--permit-tags-unfinaled` in the CLI.
### Unknown (Permit Status)
A permit status indicating insufficient data to determine the current state. May result from incomplete jurisdiction records or missing date information.
### Unresolved Rate
The share (0-1) of a jurisdiction's permits of a given work type that never linked to an address (`trust.unresolved_rate`). A high unresolved rate means permits of that type exist in the area but can't be attributed to specific properties, weakening any "no permit on record" claim.
### Upzoning
A zoning change that increases the allowed development intensity of a property or area—for example, permitting higher density or taller buildings. Upzonings can raise land values before any construction begins.
## V
### Variance
An exception to specific zoning requirements granted by a municipality. Variances allow property owners to deviate from regulations such as setbacks, height limits, or lot coverage when strict compliance would cause undue hardship.
## Z
### Zoning Code Modification
Changes to zoning regulations themselves, rather than the zoning designation of specific properties. These modify what's allowed within existing zones (e.g., adjusting height limits, adding ADUs as permitted use, modifying parking requirements) and can unlock development potential across all properties in affected zones.
***
## Related Resources
* [Data Dictionary](https://www.shovels.ai/data-dictionary) - Complete field definitions
* [Quick Answers](/docs/knowledge-base/quick-answers) - Fast answers to common questions
* [API Reference](/api-reference) - Full API documentation
# Shovels Knowledge Base
Source: https://docs.shovels.ai/docs/knowledge-base/index
Find answers to common questions about Shovels building permit and contractor data, API usage, Shovels Online, and Enterprise Data License products.
Welcome to the Shovels Knowledge Base. Find quick answers to common questions about our products, data, and services.
Fast, concise answers to the most frequently asked questions.
Definitions of key terms: geo\_id, AHJ, permit status, and more.
## Browse by Topic
New to Shovels? Learn about the Free plan, pricing, and what makes us different.
Install and use the command-line tool for permits, contractors, and scripting.
Learn how to search for permits and contractors using our web application.
Get help with API authentication, endpoints, credit limits, and troubleshooting.
Understand our permit and contractor data, coverage areas, and data quality.
Learn about bulk data deliveries, record tracking, and enterprise features.
Find our mailing address, support email, and sales contacts.
## Popular Questions
### Getting Started
* [What makes Shovels different?](/docs/knowledge-base/getting-started/key-differentiators)
* [How does pricing work?](/docs/knowledge-base/getting-started/pricing-structure)
* [What's included in the Free plan?](/docs/knowledge-base/getting-started/free-trial-guide)
* [What is Charlie?](/docs/knowledge-base/shovels-online/charlie)
### Using the CLI
* [How do I install the CLI?](/docs/knowledge-base/cli/installation)
* [How do I configure my API key?](/docs/knowledge-base/cli/authentication)
* [What commands are available?](/docs/knowledge-base/cli/commands-overview)
* [How do I get all results?](/docs/knowledge-base/cli/output-and-pagination)
* [How do I use the CLI with AI agents?](/docs/knowledge-base/cli/scripting-and-agents)
### Using the API
* [How do I access my API key?](/docs/knowledge-base/api/basics/api-key-access)
* [How do API credits work?](/docs/knowledge-base/api/basics/request-counts)
* [How do I search for permits?](/docs/knowledge-base/api/permits/permit-search)
* [How do I search for properties?](/docs/knowledge-base/api/properties/property-search)
* [What does a 422 error mean?](/docs/knowledge-base/api/errors/422-error)
### Understanding Our Data
* [What is a jurisdiction?](/docs/knowledge-base/data/geographic/jurisdictions)
* [How are permits tracked?](/docs/knowledge-base/data/permits/permit-lifecycle)
* [What do permit statuses mean?](/docs/knowledge-base/data/permits/permit-statuses)
* [Where does your data come from?](/docs/knowledge-base/data/quality/data-sources)
* [Why am I getting so few results?](/docs/knowledge-base/data/quality/few-results)
### Account & Billing
* [How do I cancel my subscription?](/docs/knowledge-base/shovels-online/cancel-subscription)
* [What is Shovels' refund policy?](/docs/knowledge-base/company/refund-policy)
* [What can I do with Shovels data?](/docs/knowledge-base/company/data-usage-terms)
### Enterprise Data (EDL)
* [When should I use EDL vs API?](/docs/knowledge-base/edl/overview)
* [How do EDL deliveries work?](/docs/knowledge-base/edl/monthly-deliveries)
* [How do I track new vs modified records?](/docs/knowledge-base/edl/record-tracking)
## Need More Help?
Full API reference with interactive playground
Complete field definitions and data schema
Email our support team
Connect with the Shovels community
# Quick Answers
Source: https://docs.shovels.ai/docs/knowledge-base/quick-answers
Fast answers to the most common Shovels questions about API limits, data coverage, pricing, permit data, contractors, and more.
Fast, concise answers to the most frequently asked questions about Shovels.
***
## Pricing & Plans
### How much does Shovels cost?
Shovels Online and API pricing starts with a Free plan. View current plans at [shovels.ai/pricing](https://www.shovels.ai/pricing). Enterprise Data License (EDL) pricing requires contacting [sales@shovels.ai](mailto:sales@shovels.ai).
### Is there a free plan?
Yes. The Free plan has no time limit and needs no credit card. In Shovels Online it covers the last 12 months of data; through the API it covers the full historical dataset. See [shovels.ai/pricing](https://www.shovels.ai/pricing) for its monthly credit allowance.
### How do I cancel my subscription?
Log in at [app.shovels.ai/account](https://app.shovels.ai/account) → **Manage subscription** → Cancel in the Stripe billing portal. Access continues until the end of the billing period. See [How to cancel](/docs/knowledge-base/shovels-online/cancel-subscription).
### What is Shovels' refund policy?
Refunds are reviewed case-by-case for charges within the last 30 days. Email [support@shovels.ai](mailto:support@shovels.ai) with your account email and charge details. See [Refund Policy](/docs/knowledge-base/company/refund-policy).
### What can I do with Shovels data?
Internal business use: research, prospecting, lead generation, and product integration. You cannot upload it to ad platforms (Facebook, Google, etc.), resell, or redistribute it. See [Data usage terms](/docs/knowledge-base/company/data-usage-terms).
### What's the difference between API and EDL?
**API**: Best for lookups, integrations, and local lead lists.
**EDL**: Best for bulk data analysis. Full dataset delivered to your data warehouse.
***
## API Basics
### What is my API limit?
Every plan is metered in credits, where one credit is one record returned. The Free plan includes **500 credits per month**; see [shovels.ai/pricing](https://www.shovels.ai/pricing) for paid plan allowances.
### How many records per API call?
Search endpoints return up to 100 records per page (default: 10). Detail endpoints accept up to 50 IDs per call.
### How do API credits work?
Each record returned counts against your credits. A search returning 100 permits uses 100 credits; a single permit lookup uses 1 credit.
### Where do I find my API key?
Log in at [app.shovels.ai/account](https://app.shovels.ai/account?tab=apikey) and find it on the **API key** tab.
### What does a 422 error mean?
A required parameter is missing. Most commonly, you need to resolve an address to a geo\_id first using the Address Search endpoint.
***
## CLI (Command Line)
### How do I install the CLI?
Run the install script: `curl -LsSf https://shovels.ai/install.sh | sh`. The script downloads the correct binary for your platform and installs it to `~/.shovels/bin`. Add to PATH if needed.
### How do I configure my API key?
Two options: (1) Environment variable: `export SHOVELS_API_KEY=your-key`, or (2) Config file (persistent): `shovels config set api-key your-key`. Verify with `shovels config show`.
### What's the difference between CLI and API?
The CLI wraps the Shovels REST API but handles authentication headers, cursor pagination, rate-limit retries, and credit tracking automatically. Use the CLI from terminals, shell scripts, or AI agents. Use the API directly for application integrations.
### How do I get all results instead of just 50?
Use `--limit all`. The CLI automatically handles pagination and fetches up to 10,000 records by default (configurable with `--max-records`).
### What commands are available?
Main commands: `permits search`, `permits get`, `properties search`, `properties get`, `contractors search`, `contractors get`, `contractors permits`, `contractors employees`, `contractors metrics`, `decisions search`, `decisions get`, `addresses search`, `cities search`, `tags list`, `schema`, `usage`, `config`. Run `shovels --help` for full list.
### How do I query properties from the CLI?
Use `shovels properties search` (added in v0.8.0). It needs `--geo-id`, `--legal-owner`, or both — not both a geo and a date range like `permits search`. Example: `shovels properties search --geo-id 92024 --permit-tags "-solar" --limit 10`. See [Querying properties from the CLI](/docs/knowledge-base/cli/properties).
### How do I find properties with no permit of a given type in the CLI?
Prefix the tag with `-` inside `--permit-tags`: `--permit-tags "-solar"`. Combine presence and absence in one value, e.g. `--permit-tags "roofing,-solar"`. Every absence row carries a `trust` object. See [Absence searches and trust fields](/docs/knowledge-base/cli/absence-and-trust).
### Why does my properties result have several trust\_summaries?
Because each entry covers one API page. `--limit 205` fetches three pages, so you get three summaries. The CLI deliberately doesn't merge them — each rate is row-weighted over its own page, so averaging them would be wrong. Read the worst page instead: `jq '[.meta.trust_summaries[].expected_miss_rate] | max'`.
### Why is there no --permit-to on properties search?
A property record keeps only the latest permit date per work type, so a closed date window can't be answered correctly. Only "ever" and "since date D" (`--permit-from`) are expressible. Use `shovels permits search` for date windows.
### How do I search one owner's properties nationwide?
`shovels properties search --legal-owner "INVITATION HOMES"` — no `--geo-id` needed. Repeat the flag for up to 10 owners; values are never split on commas, so `"SMITH, JOHN"` stays one owner.
### Why did properties get fail when only one ID was bad?
An ID that's well-formed but unknown is fine — it's omitted and listed in `meta.missing` with exit `0`. But an ID that can't be decoded as an address ID, or a city/county/jurisdiction geo\_id, fails the whole request with exit `1`. Validate before batching 50.
### How do I see a command's response fields without spending credits?
`shovels schema properties search` prints every field with its type, unit, and description, plus a jq-ready `field_index`. It runs offline and needs no API key. `--dry-run` similarly prints the resolved HTTP request without calling the API.
### Can I use the CLI with scripts and AI agents?
Yes. The CLI outputs JSON to stdout and errors to stderr with meaningful exit codes (0=success, 1=client error, 2=auth error, 3=rate limited, 4=credits exhausted, 5=server error). Perfect for piping to `jq` or scripting.
### What does exit code 2 mean?
Exit code 2 is an authentication error—your API key is missing or invalid. Set it with `shovels config set api-key YOUR_KEY` or via the `SHOVELS_API_KEY` environment variable.
***
## Data Coverage
### What areas does Shovels cover?
Approximately 2,000 jurisdictions covering about 85% of the US population.
### How far back does the data go?
At least 2010 for all jurisdictions. Many jurisdictions have data going back further.
### How often is data updated?
Monthly. We add 5-10 million new records and 1-5 million status updates each month.
### Why am I getting so few results?
Common causes: limited jurisdiction coverage, filters too narrow, recent permits not yet indexed (1–2 month lag), or limited digitization in the local jurisdiction. See [Why am I getting so few results?](/docs/knowledge-base/data/quality/few-results).
### Where does Shovels get its data?
Directly from jurisdictions through relationships with local governments, online permitting portals, and public records requests. We don't purchase data from other vendors.
***
## Permits
### What is a geo\_id?
A unique geographic identifier that can represent a state (`CA`), zip code (`94103`), city, county, or specific address. Use the Address Search API to get a geo\_id.
### What are the permit statuses?
* **in\_review**: Application submitted, awaiting approval
* **active**: Approved, work can proceed
* **final**: Completed, passed inspection
* **inactive**: Stalled, expired, or abandoned
### Why do some permits have no address?
New construction permits are often filed before official addresses exist. These permits have state and jurisdiction info but no address ID.
### What's the difference between permit ID and permit number?
**Permit ID**: Globally unique identifier generated by Shovels.
**Permit number**: Assigned by the jurisdiction—may duplicate across different cities.
***
## Properties
### What is the Properties API?
Two beta endpoints covering roughly 159 million US properties. Each record is one property with its permit history summarized on it: permit counts, work-type tags, latest activity dates, contractors, job values, and property attributes. See [How to search for properties](/docs/knowledge-base/api/properties/property-search).
### How do I find properties with no permit of a given type?
Prefix the tag with `-` in `permit_tags` (e.g. `permit_tags=-solar`). Every absence answer carries trust fields—including the page-level `expected_miss_rate`—that tell you how confident the "no permit on record" claim is. See [Absence search](/docs/knowledge-base/api/properties/absence-queries).
### Can I search properties by a date window or "before" a date?
No. A property record keeps only the latest permit date per work type, so only "ever" and "since date D" queries are supported. For date windows or upper bounds, use [Search Permits](/api-reference/permits/search-permits). See [Properties vs Permits](/docs/knowledge-base/api/properties/properties-vs-permits).
### Can I search properties by owner?
Yes—`legal_owner` is the one filter that works nationwide with no location. Pass up to 10 owner names to get their properties across the US.
### Can I search properties by parcel number (APN)?
No. APN is returned on every record for mapping into your own systems, but it's not searchable: APNs are county-specific, millions collide across counties, and about 30% of properties have none.
### Can I search properties by jurisdiction?
No. Jurisdiction is recorded on only a minority of property records, so scoping to it would silently drop properties that were never permitted. Jurisdiction geo\_ids are rejected — scope by city, county, state, ZIP, or address instead.
### Why do my property attribute filters return nothing?
Attribute data (value, size, year, units, type) covers roughly 60-70% of properties, and a property with no value for an attribute never matches a range filter on it. Stacking several filters can empty a result that returns plenty of rows without them. Loosen or drop one filter at a time.
### Can I query properties from the CLI?
Yes, as of CLI v0.8.0: `shovels properties search` and `shovels properties get`. See [Querying properties from the CLI](/docs/knowledge-base/cli/properties).
***
## Decisions
### What are Shovels Decisions?
Structured records of municipal zoning and land use decisions from city councils and planning boards across the US. Decisions give you earlier visibility into development activity—often months before permits are filed.
### What types of decisions does Shovels track?
Four primary categories: **Spot Rezoning** (changes to specific properties), **Area Rezoning** (broader geographic changes), **Zoning Code Modifications** (changes to the rules themselves), and **Special Use Permits** (conditional approvals for specific activities).
### How do Decisions relate to permits?
Decisions precede permits in the development lifecycle. A rezoning approval or special use permit typically comes weeks to months before the developer applies for building permits.
### How early can I see projects with Decisions?
Depends on project complexity: 2-4 weeks for minor commercial renovations, 1-3 months for new homes, 3-6 months for multifamily, and 6-12 months for large commercial projects.
### What information is in a decision record?
Property location and coordinates, zoning changes (previous and new), involved parties (applicant, owner, developer, representative), project value and lot size, allowed uses, a "why it matters" summary, and a link to the source meeting record.
### Which plans include Decisions?
All of them. Decisions are included on every plan, Free and paid, at no additional charge. Decisions are currently in beta.
### How do I query Decisions in the API?
Use the [Search Decisions](/docs/knowledge-base/api/decisions/searching-decisions) endpoint. It requires a date range (`decision_from`, `decision_to`) and a `geo_id`. To fetch specific records, use [Get Decisions By ID](/docs/knowledge-base/api/decisions/decisions-by-id) with up to 50 IDs.
### What's the Decisions coverage?
At launch, Decisions covered 600+ cities and nearly 200,000 records—about half the jurisdictions where Shovels has permit coverage—and continues to expand. See the [Coverage Dashboard](https://www.shovels.ai/coverage).
***
## Contractors
### How is contractor data organized?
Each contractor has a unique contractor ID (deduplicated within each state). Related contractors share a group ID linking them to parent organizations.
### What contact information is available?
Business name, address, phone numbers (primary and all known), email addresses, license numbers, and employee data (via API endpoint).
### Are contractors deduplicated across states?
No. The same contractor operating in multiple states has separate IDs per state. National deduplication is in development.
***
## Shovels Online
### What can I do on the Free plan?
Search permits and contractors, use filters, and explore the last 12 months of data. CSV downloads require a paid plan.
### What is Charlie?
Charlie is the AI agent inside Shovels Online, opened with the **Agent** button. Ask permit data questions in plain English instead of building a search with filters. Included on every plan, with usage limits that scale with your plan. See [What is Charlie?](/docs/knowledge-base/shovels-online/charlie).
### How does search work?
Searches use AND logic—results match ALL selected criteria. You can search by geography, date range, permit type, keywords, and more.
### How many results can I browse per search?
Ten on the Free plan, 100 on paid plans. The match count is not limited—a search can report tens of thousands of matches while you browse the first 10 or 100. To work with more, export to CSV or query the API (both paid, one credit per record).
***
## Enterprise (EDL)
### What formats are available?
Parquet (preferred), CSV, or JSON. Typical delivery size is 15-20 GB.
### Where can data be delivered?
Snowflake, BigQuery, Databricks, or as downloadable files.
### How do I track new records in EDL deliveries?
Use the `first_seen_date` field. Records with `first_seen_date` after your last delivery are new.
***
## Contact
### How do I contact Shovels?
* **Support**: [support@shovels.ai](mailto:support@shovels.ai)
* **Sales**: [sales@shovels.ai](mailto:sales@shovels.ai)
* **Phone**: 1-800-511-7457
* **Address**: 3515 Mt. Diablo Blvd, Unit #51, Lafayette, CA 94549
***
## Related Resources
* [Full Glossary](/docs/knowledge-base/glossary) - Detailed term definitions
* [API Reference](/api-reference) - Complete API documentation
* [Data Dictionary](https://www.shovels.ai/data-dictionary) - Field definitions
# How Do I Cancel My Shovels Subscription?
Source: https://docs.shovels.ai/docs/knowledge-base/shovels-online/cancel-subscription
Cancel your Shovels subscription anytime from the Manage subscription tab of your account, in the Stripe billing portal. Access continues until the end of the billing period; the account then reverts to the Free plan.
**You can cancel your Shovels subscription at any time directly from your account — no need to contact support.** Log in at [app.shovels.ai/account](https://app.shovels.ai/account), open the **Manage subscription** tab, and confirm the cancellation in the Stripe billing portal. Your access continues until the end of the current billing period, and you won't be charged again.
## How to Cancel
1. Log in at [app.shovels.ai/account](https://app.shovels.ai/account)
2. Open the **Manage subscription** tab
3. Click **Manage Subscription** to open the Stripe billing portal
4. Select **Cancel Subscription** and confirm
## What Happens After You Cancel
* You retain full access until your billing period ends
* You will not be charged again after cancellation
* Your data and account remain accessible until the period ends, then revert to the Free plan
* Your API key remains active on the Free plan
## Cancelling an Enterprise Data License (EDL)
EDL agreements are managed separately. To cancel or modify an EDL, contact [sales@shovels.ai](mailto:sales@shovels.ai) directly.
## Want to Pause Instead?
If you're not ready to fully cancel, email [support@shovels.ai](mailto:support@shovels.ai) — we may be able to help with a pause or adjusted plan depending on your situation.
## Refunds
See our [Refund Policy](/docs/knowledge-base/company/refund-policy) for refund eligibility and how to request one.
## Questions?
* **Email:** [support@shovels.ai](mailto:support@shovels.ai)
* **Phone:** [1-800-511-7457](tel:+18005117457)
## Related Articles
* [Refund policy](/docs/knowledge-base/company/refund-policy)
* [How does pricing work?](/docs/knowledge-base/getting-started/pricing-structure)
* [What's included in the Free plan?](/docs/knowledge-base/getting-started/free-trial-guide)
# What Is Charlie?
Source: https://docs.shovels.ai/docs/knowledge-base/shovels-online/charlie
Charlie is the AI chat assistant inside Shovels Online. Ask permit data questions in plain English and get answers without building a search.
**Charlie is the AI agent inside [Shovels Online](https://app.shovels.ai), where the button is labelled Agent.** Ask questions about permit data in plain English and get answers back in the app — no filters, no downloads, no code required. Charlie queries the full Shovels dataset.
Charlie is part of Shovels Online rather than a separate tool, so any account with an active plan can start asking. There is no separate signup.
## What Charlie Can Do
Charlie answers complex, natural-language questions that would otherwise take several filter selections to assemble by hand.
**Example questions:**
* "Find all solar permits filed in Connecticut by Freedom Forever in the last 12 months"
* "Which contractors pulled the most HVAC permits in Austin, TX last year?"
* "How many ADU permits were issued in Los Angeles County in 2024?"
* "Show me roofing contractors in Phoenix with more than 50 permits filed"
* "What's the average job value for new construction permits in Denver?"
## When to Use Charlie
Charlie and the filter-based search answer different kinds of questions. Ask Charlie when:
* You have a specific research question (contractor, geography, permit type)
* You want to verify data before pulling a full export
* You're not sure which filters apply
* You want to explore the data conversationally
For bulk exports or recurring data pulls, the [search and download tools](/docs/knowledge-base/shovels-online/how-it-works) in Shovels Online or the [API](/docs/knowledge-base/api/basics/api-key-access) will serve you better.
## Access and Limits
Charlie is included on every plan, with usage limits that scale with your plan. If you reach a limit, Charlie will let you know.
## Getting Started
Charlie is available in [Shovels Online](https://app.shovels.ai) once you sign in with your Shovels account. Look for the **Agent** button above your search results.
## Related Articles
* [How Shovels Online works](/docs/knowledge-base/shovels-online/how-it-works)
* [What's included in the Free plan?](/docs/knowledge-base/getting-started/free-trial-guide)
* [How does pricing work?](/docs/knowledge-base/getting-started/pricing-structure)
* [How do I access my API key?](/docs/knowledge-base/api/basics/api-key-access)
# Contractor Data Downloads
Source: https://docs.shovels.ai/docs/knowledge-base/shovels-online/contractor-downloads
Learn what data fields are included in Shovels contractor downloads, including business details, contact information, performance metrics, and licensing status.
Shovels contractor downloads provide comprehensive contractor intelligence with 44 data fields covering business information, performance metrics, licensing status, and project history.
See a sample contractor download CSV file
## What's Included
Each record represents a licensed contractor with detailed business information, performance metrics, and permit history tracked by Shovels.
### Business Identity and Contact Information
**Identification:**
* Unique IDs
* License numbers
* Individual and business names
* Business types and classifications
* DBA (doing business as) names
**Contact details:**
* Primary and secondary phone numbers
* Email addresses
* Websites
* Full address information
* Geographic coordinates
### Business Intelligence and Market Data
* SIC and NAICS industry codes
* LinkedIn profiles
* Revenue estimates
* Employee counts
* Primary industry classifications
* Review counts and average ratings
### Performance Metrics and Project History
**Project analytics:**
* Average job values
* Total job values
* Permit counts
**Quality indicators:**
* Average construction durations
* Inspection pass rates
### Licensing and Status Tracking
**License management:**
* Issue dates
* Expiration dates
* Activation dates
* Inactivation dates
**Activity status:**
* Final, active, unknown, and inactive classifications
* Status tracked across multiple states
* First seen dates (contractor tenure)
## Use Cases
This contractor intelligence enables you to:
* Identify qualified contractors
* Assess performance history
* Verify licensing status
* Make informed decisions for construction projects
* Build business partnerships
## How to Download
1. Run your contractor search in Shovels Online
2. Apply desired filters
3. Click the download button
4. Select CSV format
CSV export requires a paid plan; the export action is unavailable on the Free plan. Exports cover the full set of matching records with no per-export cap, and each exported record spends one credit.
## Related Articles
* [Permit downloads](/docs/knowledge-base/shovels-online/permit-downloads)
* [Filters and sorting](/docs/knowledge-base/shovels-online/filters-and-sorting)
* [Data Dictionary](/docs/data-dictionary-online)
# Using Filters in Shovels Online
Source: https://docs.shovels.ai/docs/knowledge-base/shovels-online/filters-and-sorting
Learn how to use filters in Shovels Online to refine your permit and contractor searches, and understand current filter behavior.
Filters in Shovels Online help you narrow down search results to find exactly what you need.
## Available Filter Categories
* **Financial** - Filter by job value, fees
* **Building** - Filter by property type, building characteristics
* **Permit** - Filter by status, type, date range
* **Contractor** - Filter by contractor attributes
## How Filters Work
When using filters, the platform shows contractors based on permits that:
1. Were pulled within your specified timeframe
2. Meet ALL your filter criteria (AND logic)
## Important: Permit Count Behavior
The count of permits displayed alongside each contractor remains static regardless of filters applied.
**Example:** If a contractor has pulled 100 permits total but only 10 meet your filter criteria, the platform still shows "100 permits" next to their name.
**Workaround:** Click into individual contractor profiles to see the detailed breakdown of permits matching your specific criteria.
## Troubleshooting: Greyed-Out "Apply Filters" Button
If the "Apply filters" button is greyed out, it typically indicates:
### 1. Address/Filter Details Not Resolved
The system requires a valid geography selection. If your address or filters don't match the database, the button stays inactive.
### 2. No Results for Filters
If the information is correct but no permits match your filters, the button may be greyed out.
### Steps to Fix
1. **Check required fields:**
* Ensure geolocation is set (state, county, city, jurisdiction, zip code, or address)
2. **Verify dates and geography:**
* Make sure both date fields and a geographical area are set
* These are typically the first fields on the top left
3. **Broaden your filters:**
* Try removing some filters to see if results appear
* Double-check spelling and format
## Best Practices
1. Start with broader filters, then narrow down
2. Use the date range filter to focus on recent activity
3. Check contractor profiles for detailed permit breakdowns
4. Download results to CSV for advanced sorting in Excel or Google Sheets
## Related Articles
* [Search functionality](/docs/knowledge-base/shovels-online/search-functionality)
* [Permit downloads](/docs/knowledge-base/shovels-online/permit-downloads)
* [Contractor downloads](/docs/knowledge-base/shovels-online/contractor-downloads)
# How Shovels Online Works
Source: https://docs.shovels.ai/docs/knowledge-base/shovels-online/how-it-works
Learn how to use Shovels Online to search for building permits and contractors anywhere in the USA, including search functionality, permit statuses, and data export options.
Shovels Online is a powerful web-based tool to help you find building permits and contractors anywhere in the USA.
## Search Functionality
### How Does the Search Work?
The Shovels dashboard uses an **"AND" search logic**. When you select multiple search criteria, the results show permits that match ALL selected criteria, not just one or the other.
### Keyword Search
You can search by description keywords. This is useful for finding:
* Specific brand names (like Tesla)
* Building materials
* Equipment types (such as heat pumps)
The search system does not account for typos or terminology variations. Contractors may use different abbreviations (like "SFR" instead of "single family residence"), so keep this in mind when searching.
### How Many Results Can I Browse?
A search always reports how many records match in total, but each plan limits how many of those you can page through:
| Plan | Results you can browse per search |
| ---------- | --------------------------------- |
| Free | 10 |
| Paid plans | 100 |
The match count itself is not limited, so a search can report tens of thousands of matches while you browse the first 10 or 100. Once you reach the limit, narrow your filters—by city instead of state, or a shorter date range—to bring the result set into view. Paid plans can also choose how many results appear per page.
To work with more records than you can browse, export the results to CSV or query the API. Both require a paid plan and are metered in credits, at one credit per record. See [shovels.ai/pricing](https://www.shovels.ai/pricing) for plan details.
## Permit Statuses
Permits in Shovels Online can have the following statuses:
| Status | Meaning |
| --------------- | --------------------------------------------------- |
| **Applied For** | The permit is in review |
| **Active** | The permit has been approved |
| **Finaled** | The permit is fully completed |
| **Inactive** | No date changes observed for this permit in a while |
Learn more in our [permit status details](/docs/knowledge-base/data/permits/permit-statuses) article.
## Contractor Search
Search for contractors based on the types of permits they've pulled. The contractor search provides:
* Business names
* Total lifetime number of permits pulled
* Detailed contractor information (for subscribers)
* Contact information
* History of permits pulled
Not all permits include contractor information. Some jurisdictions don't require contractors to provide their contact details.
## Permit Search
When viewing permits, you'll see:
* Permit status
* Address (when available)
* Detailed information about the work being done
Note that some permits may not have addresses—this is common with new construction permits where an address hasn't been assigned yet.
## Data Export
Paid plans can export search results as CSV files, covering the full set of matching records. Export is unavailable on the Free plan. Each exported record spends one credit.
## Contact Information
* **Support questions:** [support@shovels.ai](mailto:support@shovels.ai)
* **Sales inquiries:** [sales@shovels.ai](mailto:sales@shovels.ai)
## Related Articles
* [Free plan guide](/docs/knowledge-base/getting-started/free-trial-guide)
* [Search functionality](/docs/knowledge-base/shovels-online/search-functionality)
* [Filters and sorting](/docs/knowledge-base/shovels-online/filters-and-sorting)
* [Why am I getting so few results?](/docs/knowledge-base/data/quality/few-results)
* [How do I cancel my subscription?](/docs/knowledge-base/shovels-online/cancel-subscription)
# Permit Data Downloads
Source: https://docs.shovels.ai/docs/knowledge-base/shovels-online/permit-downloads
Learn what data fields are included in Shovels permit downloads, including permit details, timelines, property characteristics, and geographic information.
Shovels permit record downloads include 40 data fields covering permit details, timelines, property characteristics, and geographic information.
See a sample permit download CSV file
## What's Included
Each record represents a single permit application with detailed information spanning the entire permit lifecycle, property characteristics, and geographic identifiers.
### Permit Information
Core permit details including:
* Unique identifiers
* Descriptions
* Permit numbers
* Jurisdictions
* Job values
* Types and subtypes
* Associated fees
* Current status
### Timeline and Duration
Comprehensive date fields capturing the permit journey:
* File date
* Issue date
* Final date
* Start date
* End date
Duration metrics track:
* Total project time
* Construction phases
* Approval processes
* Inspection pass rates
### Location and Property Details
**Geographic data:**
* Street addresses
* Cities and counties
* ZIP codes
* Latitude/longitude coordinates
* Census tracts
* Congressional districts
**Property characteristics:**
* Ownership information
* Assessed market values
* Lot sizes
* Building areas
* Story counts
* Unit counts
* Year built
### Additional Metadata
* Contractor IDs for tracking key industry players
* Tags for flexible categorization and filtering
## How to Download
1. Run your permit search in Shovels Online
2. Apply desired filters
3. Click the download button
4. Select CSV format
**Exports cover the full set of matching records.** There is no per-export record cap, so you do not need to split a large search into smaller queries. Each exported record spends one credit—check your remaining balance before exporting a large result set.
CSV export requires a paid plan; the export action is unavailable on the Free plan. Retrieving records through the API is metered the same way, one credit per record.
## Related Articles
* [Contractor downloads](/docs/knowledge-base/shovels-online/contractor-downloads)
* [Filters and sorting](/docs/knowledge-base/shovels-online/filters-and-sorting)
* [Data Dictionary](/docs/data-dictionary-online)
# Search Functionality in Shovels Online
Source: https://docs.shovels.ai/docs/knowledge-base/shovels-online/search-functionality
Understand how permit and contractor search work in Shovels Online, including AND logic, keyword search, and the differences between search types.
Shovels Online provides powerful search capabilities for finding permits and contractors.
## Permit Search vs Contractor Search
| Search Type | Returns |
| --------------------- | --------------------------------------------------------------------------------------------------------- |
| **Permit Search** | Permits pulled on specific addresses in the geographic area you searched |
| **Contractor Search** | B2B contact information, license data, and classification data about contractors who pulled those permits |
Not every permit will have an associated contractor. This depends on whether the jurisdiction requires contractor information on permit applications.
## Search Logic
The dashboard uses **"AND" search logic**. When you select multiple criteria, results match ALL selected criteria, not just one.
**Example:** If you search for:
* Location: San Francisco
* Permit type: Solar
* Date range: 2024
You'll get permits that match **all three** criteria.
## Keyword Search
Search by description keywords to find:
* Brand names (e.g., "Tesla", "Carrier")
* Building materials
* Equipment types (e.g., "heat pump", "HVAC")
* Specific work types
### Search Tips
* The system doesn't account for typos
* Try different terminology variations
* Contractors use different abbreviations:
* "SFR" = Single Family Residence
* "A/C" = Air Conditioning
* "WH" = Water Heater
## What Permit Search Shows
For each permit, you'll see:
* Permit status
* Address (when available)
* Description of work
* Filing and completion dates
* Associated contractor (if available)
## What Contractor Search Shows
For each contractor:
* Business name
* Total lifetime permits pulled
* Contact information (subscribers)
* License status
* Permit history
## Related Articles
* [How Shovels Online works](/docs/knowledge-base/shovels-online/how-it-works)
* [Filters and sorting](/docs/knowledge-base/shovels-online/filters-and-sorting)
# Introducing the Shovels API
Source: https://docs.shovels.ai/docs/shovels-api-introduction
Comprehensive guide to the Shovels REST API, including authentication, pagination, error handling, and getting started information.
The Shovels REST API makes it easy for technology developers in the property, climate, and construction industries to access detailed information about building permits, contractors and construction activity. Our API is designed to be intuitive and fast. We look forward to seeing what you build with it: [let us know](https://docs.google.com/forms/d/e/1FAIpQLSfs5Z6NZyPdnRpL96bMduH95OhfFZgLz9Hkc0-Y7pukUxSLxQ/viewform) and we'll check it out!
The [API Reference](/docs/api-reference-about) is generated directly from our OpenAPI
specification, so it always matches the live API.
## Key Features
The API offers access to three primary objects: **Permits**, **Contractors**, and **Properties**.
* **Permits**: Official documents issued by city or county authorities required before commencing construction or alterations to a building.
* **Contractors**: Skilled professionals in the building trades who undertake permitted construction projects on various properties.
* **Properties** *(beta)*: US properties with their permit history summarized on one record—including searching for properties with **no** permit of a given type on record.
And some additional resources:
* **Lists**: Endpoints for predefined values like tags and property types for query parameters.
* **Addresses**: Endpoint for searching and resolving valid US addresses.
* **Meta**: Endpoints for metadata about the API and the data behind it.
## Use Cases
The use cases are endless, but the most common we see today are:
* **Sales and Marketing**: Generate lead lists with precision, at scale.
* **Development**: Build direct product integrations to bring Shovels data into your own applications.
* **Market Research**: Programmatically return and export Shovels data into your own tools, dashboards, and reports.
## Getting Started
To begin using the API, please contact our sales team at [sales@shovels.ai](mailto:sales@shovels.ai) or grab a [free API key](https://app.shovels.ai/login?mode=register).
The Free plan includes a monthly credit allowance—see [shovels.ai/pricing](https://www.shovels.ai/pricing). If you need a larger one, reach out to [sales@shovels.ai](mailto:sales@shovels.ai) or call us at [1-800-511-7457](tel:+18005117457).
### API Key
For any API request, you'll need to include your API key. This is available in your [Account → API key](https://app.shovels.ai/account?tab=apikey).
### API Access on the Free Plan
We want you to be able to adequately test our API before making your decision. API access is included on every plan, including Free—there is no separate API trial to start.
The Free plan has no time limit and needs no credit card. Its credits are shared with Shovels Online: each record an API call returns counts as one credit, so a search returning 100 permits uses 100 credits.
**One credit is one record.** Every plan is metered the same way, on Free and paid plans alike. See [How do API credits work?](/docs/knowledge-base/api/basics/request-counts) for details on the credit system.
If you need a larger monthly credit allowance, please reach out to [Sales](mailto:sales@shovels.ai).
## Authentication
Authentication is required for any API request that you make, whether it's in the API Playground or in your own scripts.
Our API uses a straightforward header-based authentication method: X-API-Key: YOUR\_API\_KEY\_HERE.
Here's an example request:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET "https://api.shovels.ai/v2/meta/release" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
If you have any issues with your API key, credit limits, or other authentication issues, please reach out to [Support](mailto:support@shovels.ai).
### Acceptable Use Policy
We expect that you will respect our platform and avoid frivolous requests. We are constantly monitoring usage and will enforce rate limits on an individual basis as needed.
If you feel that your API key is being rate limited, please reach out to [Support](mailto:support@shovels.ai) for clarification.
## API Details
### Quick Overview
A few quick details about our API:
| Type | Description |
| ------------------ | --------------------------------------------------------------- |
| **SSL Only** | We require that all requests be made over SSL. |
| **UTF-8 Encoding** | We use UTF-8 encoding for all requests and responses. |
| **Method** | HTTP GET for all read calls. |
| **Date Format** | All dates in the API are `strings` in the format: `YYYY-MM-DD`. |
### Credit Tracking
Every API response includes headers to track your credit usage:
| Header | Description |
| --------------------- | ---------------------------------------------- |
| `X-Credits-Request` | Credits consumed by this request |
| `X-Credits-Limit` | Your total credit limit (omitted if unlimited) |
| `X-Credits-Remaining` | Credits remaining (omitted if unlimited) |
You can also check your usage programmatically via the `/v2/usage` endpoint:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET "https://api.shovels.ai/v2/usage" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
Credits reset monthly on your subscription or upgrade date.
### Response Types
The API supports the following response codes:
| Code | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
| **200 OK** | Everything worked as expected. |
| **400 Bad Request** | There was something wrong with your request. Double-check your input. |
| **401 Unauthorized** | You need to log in to access this. Make sure your API key is correct. |
| **402 Payment Required** | Your credit limit has been exceeded. Contact sales for higher limits. |
| **403 Forbidden** | You don't have permission to access this. |
| **404 Not Found** | We couldn't find what you're looking for. Check the URL or resource ID. |
| **422 Unprocessable Entity** | There's an issue with the data you sent. Check [Error Handling](#error-handling) if you get this error. |
| **429 Too Many Requests** | You're sending requests too quickly. Slow down and try again later. |
| **500 Internal Server Error** | Yikes, something went wrong on our end. Please let us know at [support@shovels.ai](mailto:support@shovels.ai) |
### Data Format
The API returns data in JSON format, either as paginated collections or single objects.
Paginated responses use cursor-based pagination by default:
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"items": [...],
"size": 10,
"next_cursor": "eyJkYXR..." | null
}
```
Where objects are returned as an array in the `items` field. When `next_cursor` is `null`, there are no more results.
### Pagination
The API uses cursor-based pagination:
#### Cursor-Based Pagination
Cursor-based pagination uses an opaque cursor token to maintain your position in the result set, offering better performance, consistency, and stability, especially for large datasets.
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"items": [...],
"size": 10,
"next_cursor": "eyJkYXR.lIjoiMjA.yMy0"
}
```
To use cursor-based pagination:
* For the first page: Simply make a request without any pagination parameters
* For subsequent pages: Include the `next_cursor` value from the previous response using the `cursor` parameter
Each page returns **10 records** by default. Set the `size` parameter to return up to **100** per page.
Example:
```
GET /v2/permits/search
GET /v2/permits/search?cursor=eyJkYXR.lIjoiMjA.yMy0
```
When there are no more results, the `next_cursor` value will be `null`.
### Versioning
The current version of the API is v2, which is reflected in the endpoints URL structure: `/v2/`. We plan to evolve our API by releasing new versions to ensure backward compatibility while maintaining a steady pace of continuous improvements.
## API Reference
If you're looking for the Shovels API Reference, then you're in the right place. The API Reference tab in this documentation hub outlines all of our endpoints, parameters, requirements, and responses.
Additionally, there are examples of both requests and responses to help you get started.
## Using the Shovels API Playground
The easiest way to get started with the Shovels API is to use the API Playground. This is a web-based tool that allows you to test out the API without having to write any code.
### Authentication
In order to use the playground, you'll need to authenticate with your API key.
Enter your API Key in the "Authentication" drop down (as shown below), and proceed to the next step.
If you have forgotten your API key, or need one in the first place, this is available in your [Account → API key](https://app.shovels.ai/account?tab=apikey).
### Server
Ensure that the "Server" drop down is set to `api.shovels.ai/v2`.
### Required fields
Different endpoints will have different required fields. Select the "Query" dropdown to see all available fields. Required fields are indicated with a red asterisk (as shown below).
Generally speaking, the required fields are the start and end dates for the query, and the corresponding `geo_id` to point the query to the right place. If the query is for a specific object, like an individual permit, address, or contractor, then you'll only need the corresponding `id` field.
### Parameters
In order to fine-tune your query results, you can add parameters to the request.
A full list of the parameters available (with definitions) for each endpoint can be found in the "Query Parameters" section, beneath the "Query Dropdown" (as shown below).
## Tips and Tricks
Using a REST API is generally the same everywhere, but here are a few things that may help you with our Shovels API specifically.
### Use Optional Parameters
Permit data goes deep, and our data-cleaning allows you to go even deeper.
Read through all the options in the "Query Parameters" and see if there are any that will help narrow down your query. Not every parameter will have complete fill rates (as in, that field may be null), but it's best to take a bottom-up approach of creating as fine and detailed a query as possible, with all the extra data points you could be interested in.
If that specific query doesn't return the results you need, in either quality or quantity, then you can slowly whittle away at these extra parameters until you get what you need.
### Break Down Your Queries
Our endpoints are answers to straightforward questions, and sometimes the query you may want to build isn't possible with a single endpoint.
In such cases, a sequential approach of chaining endpoint queries together can be effective. In others, where the required field is a value, like a specific `contractor_id` or `address_id`, then it's mandatory to first retrieve the foundational data point before getting the insights for it.
An example of this can be found in our Tutorial section.
### Use the API Request Examples
If you're new to using REST APIs, or even if you're a seasoned veteran, it can be helpful to see an example of the exact syntax and formatting of the request, to give you the piece of mind that it's formatted correctly.
To the right of each endpoint in our API Playground there is a request example in a variety of languages. These will either give you a good starting point for building your own request, or can be directly copy and pasted in simple use cases.
## Error Handling
Proper error messages and HTTP codes are provided to help you troubleshoot issues effectively. Refer to the [Response Types](#response-types) section for an overview of HTTP error codes and how to handle them. Below we describe how to interpret HTTP 422 code.
### 422 Unprocessable Entity
A 422 Unprocessable Entity response occurs when the server understands the request but cannot process it due to invalid data. This helps you identify issues with your input.
The response includes:
* **loc**: The location of the error. The first value indicates the location and the second specifies the problematic field. Common values for the first value include:
* `body`: The error is in the request body.
* `query`: The error is in the query parameters.
* `path`: The error is in the URL path.
* `header`: The error is in the request headers.
* **msg**: A message describing the error.
* **type**: The type of error.
Here are some examples:
```json Request Body Error theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"loc": ["body", "start_date"],
"msg": "Field required",
"type": "value_error.missing"
}
```
```json Query Parameter Error theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"loc": ["query", "size"],
"msg": "Input should be a valid integer",
"type": "int_parsing"
}
```
```json Path Parameter Error theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"loc": ["path", "contractor_id"],
"msg": "Invalid ID format",
"type": "value_error.invalid_id"
}
```
```json Header Error theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"loc": ["header", "X-API-Key"],
"msg": "API key is missing",
"type": "value_error.missing"
}
```
If these examples don't help, please reach out to [Support](mailto:support@shovels.ai) for assistance.
## Troubleshooting
We will outline below a few of the main methodologies for analyzing what happened to a REST API request, and what to do from there.
### Missing Data and Empty Fields
A common thought after receiving a `404` error is to wonder if the query itself was incorrect, or somehow misconfigured.
While that may be the case, it's also important to consider that there may be no data available at this time.
We talk about this in much more detail under the Shovels Foundations section, but to be succinct: there are many complications in obtaining complete and total permit data, from variability in digitization practices to required permit fields in individual jurisdictions. While we do our best, we aren't there yet.
**If you find that we're missing key data for your needs, please reach out to [Support](mailto:support@shovels.ai) and we'll confirm for you whether we have it or not, and if not, add it to our roadmap to get it soon.**
### Release Notes
Another possibility, especially for existing queries that failed, are that the endpoints and data structure may have changed.
Any such changes are documented in our [Release Notes](/release-notes).
Please note that such breaking changes are exceedingly rare, and we do our best to minimize them wherever possible. If you need any assistance, or notice that existing queries are suddenly failing, please reach out to [Support](mailto:support@shovels.ai) for assistance.
If you have any questions, please don't hesitate to reach out to [Support](mailto:support@shovels.ai).
# Shovels API Troubleshooting
Source: https://docs.shovels.ai/docs/shovels-api-troubleshooting
Common issues and how to fix them when using the Shovels API.
## General Troubleshooting Workflow
When encountering an issue with the Shovels API, whether it's authentication, execution of the query, or parsing the response, we have a few steps we recommend taking to make sure that everything is working as expected.
We'll describe these steps in detail here:
1. **Check your API Key**: Ensure that your API key is accurate, and matches what you see in your [Account → API key](https://app.shovels.ai/account?tab=apikey).
2. **Check the Required Fields for the Endpoint**: The required fields may vary from endpoint to endpoint, so ensure that they're all included.
3. **Confirm the Request Server**: Make sure that you're using the full `api.shovels.ai/v2` URL, and not just `/v2`.
4. **Double Check the Request Syntax**: This can vary widely based on the method you use to execute the request, but errors in query configuration can cause issues. Sometimes, the Request Examples in our [API Reference docs](/api-reference/) can help with this.
* We're happy to help with this, just send us the full request (within reason)and we'll see what we can sort out.
5. **Check for Conflicting Parameters (for `404` errors)**: For scenarios where you receive a `404` error unexpectedly, it can sometimes come from conflicting parameters which create a query with no possible results.
* For example, including both `property_type = "residential"` and \`permit\_min\_fees = "X,XXX"' may result in an incomplete search due to the frequent under-reporting of permit fees on residential projects.
6. **Ask for Help**: If you're still having trouble, let us know what you've tried in an email ticket, and we'll do our best to help.
## Common Issues
This section will highlight some of the more common issues encountered by our users when using the Shovels API.
If we missed anything, or you have further questions that these answers don't address, please email us at [support@shovels.ai](mailto:support@shovels.ai).
### I'm getting a different HTTP error than you list in your documentation. What do I do?
Occasionally, the system may respond to a query with a less-commonly used HTTP error code.
The first place to look will be the [API Reference > Troubleshooting > Error Codes](/docs/shovels-api-introduction#error-codes) to confirm whether we've documented that specific code or not.
If not, then please check the [Mozilla Documentation for HTTP Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to get more insight into the root cause of this error.
If you still can't fix the issue, then please email us at [support@shovels.ai](mailto:support@shovels.ai) and we'll do our best to help.
Please include the following in the ticket to our Support team:
* The email address of the user who made the request (if it's different from the one you're emailing from)
* The HTTP error code you received
* The API request itself (with as much context as possible)
* What steps you've taken already in your own troubleshooting
* Any additional context that might help us understand the issue
### Why are there so many `null` values in my response?
We talk about this in more detail in our Shovels Foundations section, but there's a ton of variability in the required fields for permits across all the different permit jurisdictions across the country, and so it's hard to make a unified set of fields that will have data for every permit.
We put as much of the data included in the permit as possible, and try to fill in the gaps based on other data we have (sometimes the data we'd expect to see in an individual field is included in the `Description` instead, so we try to extract and move that to the right place).
If you have any questions or think that there's crucial information missing from a specific permit, contractor, or other object in our database, please let us know and we'll investigate for you.
Happy Digging!
# Shovels CLI Quickstart Guide
Source: https://docs.shovels.ai/docs/shovels-cli-quickstart
Install the Shovels CLI, configure your API key, and run your first permit search in under a minute.
**The Shovels CLI is a single-binary command-line tool for querying U.S. building permit and contractor data.** It outputs JSON to stdout, handles pagination automatically, and retries on rate limits. No runtime dependencies required.
**CLI vs. API vs. Online:** The CLI wraps the same Shovels REST API but handles auth headers, cursor pagination, rate-limit retries, and credit tracking for you. Use it from your terminal, in shell scripts, or from AI agents.
Choose your environment to get started:
## Install
Run the install script to download the latest release:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -LsSf https://shovels.ai/install.sh | sh
```
The script detects your OS and architecture, downloads the correct binary from GitHub Releases, verifies the SHA256 checksum, and installs to `~/.shovels/bin`.
Add `~/.shovels/bin` to your `PATH` if the installer doesn't do it automatically. For most shells, add `export PATH="$HOME/.shovels/bin:$PATH"` to your shell profile.
Verify the installation:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels version
```
## Configure Your API Key
You need a Shovels API key. If you don't have one, [create a free account](https://app.shovels.ai/login?mode=register) to get a key on the Free plan.
There are two ways to provide your API key:
### Option A: Environment variable
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
export SHOVELS_API_KEY=your-api-key
```
### Option B: Config file (persistent)
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels config set api-key your-api-key
```
This saves your key to `~/.config/shovels/config.yaml` so you don't need to export it every session.
Confirm your config is set:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels config show
```
## Verify the Setup
Check that the CLI can connect to the API and authenticate:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels usage
```
You should see a JSON response with your credit usage, confirming the CLI is installed and configured correctly.
## Run Your First Query
Search for solar permits in Encinitas, CA (ZIP 92024) from 2024:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels permits search \
--geo-id 92024 \
--permit-from 2024-01-01 \
--permit-to 2024-12-31 \
--tags solar \
--limit 5
```
You'll see JSON output like this:
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"data": [
{
"id": "...",
"description": "INSTALL ROOF MOUNTED SOLAR PV SYSTEM...",
"status": "final",
"tags": ["solar"],
"address": {
"street": "123 Main St",
"city": "Encinitas",
"state": "CA",
"zip_code": "92024"
}
}
],
"meta": {
"count": 5,
"has_more": true,
"credits_used": 1,
"credits_remaining": 249
}
}
```
Every response includes a `meta` object with credit tracking so you always know your usage.
## Understanding geo\_id
Most search commands require a `--geo-id` flag. A geo\_id can be:
* **A ZIP code** — Use it directly (e.g., `92024`)
* **A state** — Use the 2-letter code (e.g., `CA`)
* **A city, county, or jurisdiction** — Use the base64-encoded ID from a search
To find a geo\_id for a city:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels cities search -q "Miami Beach"
```
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"data": [
{
"geo_id": "Q2l0eXxGTHxNaWFtaSBCZWFjaA",
"name": "Miami Beach, FL"
}
]
}
```
Use that `geo_id` in subsequent searches:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels permits search \
--geo-id Q2l0eXxGTHxNaWFtaSBCZWFjaA \
--permit-from 2024-01-01 \
--permit-to 2024-12-31 \
--tags roofing
```
## Explore More Commands
The CLI covers permits, contractors, addresses, and geographic lookups:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Search contractors by location and specialty
shovels contractors search \
--geo-id 78701 \
--permit-from 2024-01-01 \
--permit-to 2024-12-31 \
--tags electrical
# Look up a specific contractor's permits
shovels contractors permits CONTRACTOR_ID
# Search for an address
shovels addresses search -q "1600 Pennsylvania Ave"
# Check your API credit usage
shovels usage
# List available permit tags
shovels tags list
```
Every command has built-in help:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels permits search --help
```
## Pipe It
The CLI outputs JSON to stdout, so it works with `jq` and other Unix tools:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
# Count solar permits in a ZIP code
shovels permits search --geo-id 92024 \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--tags solar --include-count --limit 1 \
| jq '.meta.total_count.value'
# Export contractor names and permit counts to CSV
shovels contractors search --geo-id CA \
--permit-from 2024-01-01 --permit-to 2024-12-31 \
--tags solar --limit 100 \
| jq -r '.data[] | [.name, .permit_count] | @csv' \
> solar_contractors.csv
```
## Next Steps
* [CLI installation options](/docs/knowledge-base/cli/installation) — Platform-specific details and version pinning
* [CLI commands overview](/docs/knowledge-base/cli/commands-overview) — Full list of commands and flags
* [Output format and pagination](/docs/knowledge-base/cli/output-and-pagination) — Understanding responses and `--limit all`
* [Scripting and AI agents](/docs/knowledge-base/cli/scripting-and-agents) — Composing the CLI into workflows
* [CLI error codes](/docs/knowledge-base/cli/error-codes) — Exit codes and troubleshooting
## Overview
You can set up the Shovels CLI inside [Claude Cowork](https://claude.ai/cowork) and query building permit data directly from your workspace — no command-line experience required. Just add one file to your Cowork folder and Claude handles the rest.
## What You'll Need
**Download the Shovels CLI binary** — Go to [github.com/ShovelsAI/shovels-cli/releases/latest](https://github.com/ShovelsAI/shovels-cli/releases/latest) and download the `linux_arm64` release (the file ending in `.tar.gz`). Save it to your Cowork folder.
You'll also need your Shovels API key handy. If you don't have one, [create a free account](https://app.shovels.ai/login?mode=register) to get a key on the Free plan.
Only the `linux_arm64` binary works in Cowork. The Cowork environment runs on Linux (arm64 architecture), so make sure you download that specific version from the releases page — not the macOS, Windows, or amd64 versions.
## Getting Started
Once the `.tar.gz` file is in your Cowork folder, just ask Claude:
> *"Set up the Shovels CLI for me."*
Claude will:
1. **Find and extract the binary** — Claude locates the archive in your folder, unpacks it, and installs the `shovels` binary to `~/.local/bin`.
2. **Ask for your API key** — Claude will prompt you for your Shovels API key. Just paste it into the chat. Claude uses it to configure the CLI by running `shovels config set api-key`.
3. **Verify everything works** — Claude runs `shovels usage` to confirm the CLI can connect and authenticate. You should see a response with your credit usage, meaning you're all set.
That's it — you're ready to start querying Shovels data. Try asking Claude something like *"How many permits were pulled in Austin last month?"* and it will use the CLI to find the answer.
## Troubleshooting
If something doesn't go as expected, tell Claude what happened and it can usually fix it. Common issues include:
* **CLI not found after install** — Claude re-adds `~/.local/bin` to the session's `PATH`.
* **Permission error on the binary** — Claude runs `chmod +x` to fix it.
* **401 Unauthorized error** — Claude re-runs the config step with your API key.
* **No `.tar.gz` file found** — Make sure you downloaded the `linux_arm64` release and placed it in your Cowork folder.
# FAQ
Source: https://docs.shovels.ai/docs/shovels-faq
Frequently asked questions about the Shovels platform.
## Shovels Online
### Why can I only download 1000 records?
For performance reasons, we limit the number of records you can export at a single time, for a single search, to 1000 records.
There is one workaround for this: break your query down into smaller segments (eg, change from `state` to `city` or `zip code`) and then run each one separately. Combined, these can generate a CSV with more than 1000 records.
On the other hand, the Shovels API has no result limit, so you can export as many records as needed.
### Why doesn't the Map Search show all the results?
For similar performance reasons as above, we limit the number of results displayed on the map to 50, which is the maximum page size.
In order to view the next page of results in the map format, switch back to the "List View" and paginate as usual, and then switch back to "Map View".
### Why isn't a permit showing up, even though I know it exists?
We talk about this issue in depth [here](/docs/shovels-online-troubleshooting#i-know-for-sure-that-a-permit-exists-why-cant-i-find-it), but the short of it is this:
* There are over 20k permit jurisdictions nationwide, and we don't have access to all of them (though, this number includes an extremely long tail of small rural areas).
* When we do have a jurisdiction in our system, there are varying levels of permit and file digitization, which limits how much data we can process.
* Sometimes, the digitized permits we do get are incomplete or garbled, and thus unusable (this is the rarest case, but does happen).
If you're seeing less-than-desired results, [contact us](mailto:support@shovels.ai) and we'll investigate which of the above reasons is the likely culprit, and how we might fix it for you.
## Shovels API
### What's the difference between using the API vs Shovels Online?
The main difference is scale and depth of the data provided.
With the **Shovels API**, you can programmatically access the same high level data (objects like permits, addresses, contractors, etc), but with deeper data and fields for each one.
You can also export more records at a time, with greater control and flexibility with filters and parameters.
Beyond that, using the API allows for much more complex use cases, such as direct product integrations or query automation, which isn't possible with **Shovels Online**.
### What's the best way to interact with the API?
Like any REST API, there are a wide variety of ways to use it: which one is best depends on your use case.
I'll outline a few of the most common ways we see our users interact with the API today:
* **Using the API Playground**: our [API Reference documentation](/api-reference) contains a "playground", where you can read through the schema and fields, test out queries, and view responses in real time. Authenticate with your API key and use the "Try it" button to get started.
* **cURL HTTP requests**: [cURL](https://everything.curl.dev/) is a command-line tool using `bash` or `zsh` that's great for making basic HTTP requests (among many others). Our [API Reference playground](/api-reference) has example queries in a variety of languages, including cURL.
* **Python scripts**: For more in-depth needs, especially in conjunction with other tools or tasks, Python is a great tool to extract the data you need from the Shovels API and then do something else with it.
* **Postman**: [Postman](https://www.postman.com/) is a popular tool for developing and testing APIs, and is often used as an isolated environment prior to integrating it with an existing application or tool.
At the end of the day, there's no right way to use it, and if you have any questions don't hesitate to [reach out to us for help!](mailto:support@shovels.ai)
### How do I know how recent the data is?
We refresh our data twice a month, but depending on your platform it may take some time for the actual data to arrive.
Here's a rough timeline of what our data delivery looks like on any given month:
1. The underlying database (for all **Enterprise Data License** table shares) is updated with the latest data.
2. We then ensure that it's ready and compatible with the API schema. *This can take a few days, up to a week.*
1.You can check the status of this via our API and the `GET /meta/release` endpoint.
3. Once the data is available in the API, it's also available in **Shovels Online**.
This timeline is subject to significant change based on a variety of parallel changes, such as API endpoints, schema changes, etc. These changes take time to pan out downstream, so we appreciate your understanding for any delays.
## Shovels EDL (Enterprise Data License)
### How do I know when my table share is updated?
This will vary based on delivery method (whether you're getting a table share to your data warehouse or Amazon S3 bucket), but in general there will be a timestamp in the file or folder metadata indicating when it was last updated.
### New data has been added since my Custom Report was last run. Can I get my report re-run?
It will depend on the data included in the original report, and the nature of the new data that we've added to our system.
The safest bet will be to [Contact Sales](mailto:sales@shovels.ai) as we handle these case-by-case.
## Still Have Questions?
Let us know if there's anything else we can help you with! Our Support team is available to help you with any question you might have, just email us at [support@shovels.ai](mailto:support@shovels.ai).
# Shovels Online Quickstart Guide
Source: https://docs.shovels.ai/docs/shovels-online-quickstart-guide
This guide will help you get started with Shovels Online, the web-based application for exploring the Shovels dataset.
**Shovels Online**, compared with our other platform offerings of the **Shovels API** and **Shovels EDL** (Enterprise Data License), is best for ad hoc and codeless exploration of US construction data, specifically **Contractors** and the **Related Permits**.
Our coverage and data is not perfect, and we're reliant on individual permit jurisdictions all over the country to digitize their records. We're always working to improve the coverage and data fidelity, so if you're looking for a specific record (and you are certain that there's a permit record for it), then please get in touch and we'll do our best to help.
## Creating an Account
If you haven't already, go to the [Create Account](https://app.shovels.ai/login?mode=register) page and enter in your details.
If you already have an account, then just log in at [app.shovels.ai](https://app.shovels.ai/login).
For security purposes, we'll send a verification email with a magic link to get you started.
### Logging In
Once you've verified your email, you'll be directed to log in. Once successful, you'll officially begin your Free Trial and see the Shovels Online Search page.
### Free Trial Usage
We provide a 7 day Free Trial for all new users, which will allow you to explore the platform, gather some sample data, and get a hang of the data we provide.
We also understand that you may need additional time to test and vet the system. If your free trial expires, please reach out to [Sales](mailto:sales@shovels.ai) and we'll be happy to help.
#### Free Plan API Key
Every account is also provided with an API Key. It draws on the same monthly credit balance as Shovels Online, so a record you retrieve through the API counts the same as a record you export from the web app. The Free plan does not expire.
To find your API key, click on your email address in the top right corner. From the account profile page, click the "API Key" tab to view your key, as well as some helpful resources for getting started.
### Troubleshooting Login Issues
If you're having issue logging in, and the "Reset Password" option isn't working, please reach out to [Support](mailto:support@shovels.ai) and we'll help right away.
## The Tour
Now that you're logged in, here's a quick guided tour of the Shovels Online platform.
### Search Filters
When you reach the Search page, you'll see the left-side panel with a variety of filters. This is how you specify the exact results you're looking for.
Search results are limited to 50 per page. This limit carries over to the Map View, so the results will not dynamically adjust based on zooming out or panning around.
Across the top, there will be two tabs labeled "Contractors" and "Related Permits". These tabs allow you to switch between returning top-level contractors, which will include a summary of permit counts, job types, and other company data, and individual permits.
### Contractors vs Permits
Across the top of the page, there will be two tabs labeled "Contractors" and "Related Permits". These tabs allow you to switch between returning top-level contractors, which will include a summary of permit counts, job types, and other company data, and individual permits.
Simply defined, **Contractors** are individuals or companies that file permits and do the underlying work for the project. Our search allows you to filter for contractors by a wide range of factors, including business name, project type and scale, geographical area of work, and more.
On the other hand, **Permits** are the actual permit records, which can be submitted (and linked to) a Contractor, or could be done by a non-contractor (such as homeowner).
### Saved Profiles
On the upper right-side of the page, you'll see a button for "Saved Profiles". This is where you can save your previous search queries, filters, and results for future use or reference.
There are a wide variety of single objects that you can save, including Contractors, Permits, States, Jurisdictions, Counties, and Cities.
You can also save entire search queries, so you can easily find and re-run a set of filters that you've used previously.
Finally, even if you haven't saved the Search explicitly, a "Search History" of previous searches will be available to you for convenience.
### Map View
By default, the results will be displayed in a table view, with each entity represented by a row. However, you can also switch to a map-based view by clicking the "Map" toggle () on the far right-hand side of the page.
## Finding your First Contractor
In this section, we'll walk though a sample search for a contractor in the state of Massachusetts.
To ensure we're getting Contractors instead of Permits, we'll select the "Contractors" tab.
Let's look for contractors in Hampden County, MA, that specialize in new construction projects, and have at least 10 permits on file.
### Geography
Shovels data is segmented at all geographical levels, from as low as an individual address to an entire state.
In some cases, the borders between smaller geographical tiers (such as zip codes or jurisdictions) may not perfectly align and overlap with higher level ones. For instance, a jurisdiction may cross into the border of an adjacent state.
For this example, we'll select "County". A text field will appear, where you can enter in the county name of your choice. We'll go with `Hampden, MA`.
### Date Range
This filter allows you to determine the activity range of Contractors you're searching for. In the Free Trial, there are three relative date options: Last 3 Months, Last 6 Months, and Last 12 Months. Subscribed customers have an additional option for "All Time".
We'll go with `Last 12 Months`.
### Financial Filters
These are the technical filters which allow you to get very granular with your criteria.
Financial filters allow you to search by project fees (`Min Permit Fees`) or value (`Min Job Value` and `Min Market Value`).
We'll leave these blank for now.
### Building Filters
These filters allow you to specify more detail about the specific kind of building your desired contractor will have experience with.
For an in-depth breakdown of all these fields, please see our \[link forthcoming] page.
We'll go with `Property Type = Residential`, so we can exclude contractors who only work on commercial or other projects.
### Permit Filters
Permit filters let you specify the exact kind of project the contractor will have experience with, based on the permit details their projects had.
We'll go with `Categories = New Construction`, which specifies that we want contractors who work on new construction projects, as opposed to renovations or installations.
### Contractor Filters
Contractor filters let's you specify contractor-specific details, such as License Number, Company Name, and other job history fields. If you have a specific contractor in mind, these filters will be helpful.
We'll go with `Min Total Permits Count = 10`, which specifies that we want contractors with at least 10 permits on file.
### Getting Results
Now that we've fine-tuned our filters, click "Apply Filters" at the bottom of the panel and wait for the results.
In this case, we've made a very narrow search, in a small geographical area. As of time of writing, we see 3 results for Contractors that meet our criteria (`10` permits, with at least one in `New Construction`, in the `last 12 months` in `Hampden County, MA`).
Each **Contractor** is represented by a row in the results, and if you want more information you can click "View Details" to drill down further.
## Searching for Related Permits
Thus far, we've been searching for Contractors only. By switching to the "Related Permits" tab, we can instead return a list of all qualifying permits that match our existing and relevant criteria. In this case, Contractor filters aren't applicable.
### Difference between Contractors and Permits
There are a few key differences in returned results between Contractors and Related Permits.
* **Contractors** always roll-up their entire job history, so not every permit on their record will necessarily meet all criteria. As long as each criteria is met by at least one or more of their permits, they'll be returned in the search results.
* For more detailed and controlled results, you'll want to explore our API or EDL platforms.
* **Related Permits** do not necessarily have an associated **Contractor**, as homeowners may submit permits without hiring a contractor for the project.
## Next Steps
Now we have our results, and it's time to do something with them.
### Saving Filters
If this is a set query that you'll want to return to frequently, then you can save this search for future use. Click the " Save" button on the top of the Filters panel to save the existing filter.
### Saving Profiles
If one of the Contractors is of specific interest, then you could save their profile directly. Click the bookmark icon () to save the current object.
### Exporting Results
Most commonly, you'll want to export this data somehow. In this example, it may be easier to simply take down the information by hand for the three contractors we found. Using their contact details, we can add them to a sales or marketing pipeline to get in contact directly.
For larger search results, as might be the case in a wider geographical or construction-heavy zone, you can use the "Download List " button to save a CSV of the top 1000 results.
Shovels Online doesn't support more than 1000 results for a given search. If you need more, then we recommend either narrowing your search further (such as smaller geographical filter), or using Shovels API to get your data. If you have any questions, please don't hesitate to reach out to [Support](mailto:support@shovels.ai).
## Happy Digging!
This example only scratches the surface, and we're still learning of new use cases and ways our data can be leveraged. Check out our \[Tutorials] and \[Reference Information] for a more in-depth description of all the features, fields, and parameters available.
And as always, if you have any questions, please don't hesitate to reach out to [Support](mailto:support@shovels.ai).
# Shovels Online Troubleshooting
Source: https://docs.shovels.ai/docs/shovels-online-troubleshooting
Common issues and how to fix them when using Shovels Online.
## Common Issues
These are our most common issues encountered by our users when using Shovels Online, which we'll try our best to answer here.
If we missed anything, or you have further questions that these answers don't address, please email us at [support@shovels.ai](mailto:support@shovels.ai).
### I know for sure that a permit exists: why can't I find it?
We touch on this more in our Shovels Foundations section, but the short answer is that we do not have every permit in our database (yet). It's a complicated and nuanced problem, and one that our engineering team is actively working on.
We'll highlight a few of the issues that could be causing this permit to be missing from our system:
* We don't have coverage in that specific jurisdiction.
* That jurisdiction may have a slow process for digitization, so it might not be virtually available yet.
* (The rarest case) The permit in question may have been garbled in either the submission or the digitization process, and so it's hard for us to parse it and add it to our system.
The biggest issue here is coverage. There are more than 20,000 permitting jurisdictions in the United States, and those boundaries can change. If there's a jurisdiction or geographical area that's of particular importance to you, please let us know and we'll see what we can do to expedite our coverage there.
### Why can't I download more than 1000 records?
We limit exporting records to 1000 **per unique search filters** for many reasons, including optimizing performance of our web application, and because in our user research we've found that most users are fine with keeping their searches narrow and granular.
For broader use cases, especially where there are more data science and engineering resources to leverage, we recommend using the Shovels API, which has no result limit, or our Shovels EDL (Enterprise Data License).
### Why doesn't the Map View show all the results?
The Map View renders the results of a specific page of results (which is currently set to 50). To see subsequent pages, switch back to the Table view and use the pagination controls.
This is due to a technical limitation for the performance of our web application, and in the future we hope to improve on this user experience.
### How much of the Contractor `Company Name` do I need to include in my search?
We've found the best results for searching for individual contractor or construction firms by searching with upper case letters, and at least two words of the company name.
For example, for "Acme Plumbing and Heating Construction, Inc." we'd suggest at least including "Acme" and "Plumbing" in your string.
This is even more important when the company name includes more common words, like "sun" or "roof" or other terms that are frequently to return a ton of results.
If you have any issues with finding the right contractor, or using the Shovels Online search more generally, please reach out to us and we'll be happy to help.
# Retrieving Permit History for an Address
Source: https://docs.shovels.ai/docs/tutorial-api-address-permit-history
This tutorial will show you how to use the Shovels API to retrieve permit history for an address.
When using the Shovels API, it's important to know how to chain together information from different endpoints to get the data you need.
In this tutorial, we'll be looking at the multi-step process of retrieving permit history for a given address.
Let's get into it.
## Step-by-Step Recipe
Authenticate with the Shovels API using your API key. For more information on getting started with the Shovels API, please see our [API Introduction > Authentication](/docs/shovels-api-introduction#authentication).
Use the `GET /v2/addresses/search` endpoint, and enter the address string.
In this example, we'll use cURL to make the request, and look for the address "123 Main St".
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET "https://api.shovels.ai/v2/addresses/search?q=123%20Main%20St" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
This will return a list of addresses that match the search string. Pick the one that matches the address you're looking for originally (you may need to paginate to see the full results).
From the response body in **Step 1**, note or otherwise extract the `geo_id` of the address. It should look like a alphanumeric string.
The `geo_id` is the unique identifier for all location objects in the Shovels platform. These don't only correspond to individual addresses or property plots, but also exist for other geographical segments like `cities`, `counties`, `jurisdictions`, and `states`.
This `geo_id` will be what you need for **Step 3**.
Use the `GET /v2/permits/search` endpoint, and enter the `geo_id` you extracted in **Step 2**.
You will also need to specify the `permit_from` and `permit_to` strings, which are required fields. These represent the time bounds to search for permits for the given address.
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -X GET "https://api.shovels.ai/v2/permits/search?geo_id=$GEO_ID&permit_from=YYYY-MM-DD&permit_to=YYYY-MM-DD" \
-H "X-API-Key: YOUR_API_KEY_HERE"
```
This will return all permits for the given address within the date range you specified.
Now you have your permit history, and you can repeat this for any other addresses you need data for.
## Troubleshooting
Configuring API queries can be tricky, and we're here to help however we can. Double check our [API Troubleshooting](/docs/shovels-api-introduction#troubleshooting) guide for further guidance.
## Additional help
If you still run into issues with your permit history query, or anything else with the Shovels API, please reach out to us at [support@shovels.ai](mailto:support@shovels.ai) and we'll be happy to help.
Happy Digging!
# Building a Contractor Pipeline with the CLI
Source: https://docs.shovels.ai/docs/tutorial-cli-contractor-pipeline
This tutorial walks through using the Shovels CLI to find top contractors in a market, pull their details, and export to CSV — all from your terminal.
The Shovels CLI makes it easy to go from a broad market search to a targeted contractor list in a few commands. In this tutorial, we'll find the top solar contractors in a California ZIP code, inspect their permit history, and export the results to CSV.
This is a common workflow for sales teams, market researchers, and AI agents building lead lists.
## Step-by-Step Recipe
If you haven't already, install the CLI and configure your API key:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -LsSf https://shovels.ai/install.sh | sh
shovels config set api-key YOUR_API_KEY
```
Verify it works:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels version
```
For detailed setup instructions, see the [CLI Quickstart Guide](/docs/shovels-cli-quickstart).
If you know your ZIP code, you can use it directly as a `--geo-id`. If you need to find a city or county geo\_id, search for it:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels cities search -q "Encinitas"
```
```json theme={"theme":{"light":"css-variables","dark":"css-variables"}}
{
"data": [
{
"geo_id": "Q2l0eXxDQXxFbmNpbml0YXM",
"name": "Encinitas, CA"
}
]
}
```
For this tutorial, we'll use ZIP code `92024` (Encinitas, CA).
Search for solar contractors active in 2024:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels contractors search \
--geo-id 92024 \
--permit-from 2024-01-01 \
--permit-to 2024-12-31 \
--tags solar \
--limit 20
```
This returns up to 20 contractors with their names, contact info, permit counts, and ratings.
Add `--include-count` with `--limit 1` to see the total number of matching contractors before fetching the full list.
Pick a contractor ID from the results and view their permit history:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels contractors permits CONTRACTOR_ID --limit 10
```
This returns individual permits with descriptions, dates, job values, and addresses.
To see monthly performance metrics:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels contractors metrics CONTRACTOR_ID \
--metric-from 2024-01-01 \
--metric-to 2024-12-31 \
--property-type residential \
--tag solar
```
Use `jq` to transform the JSON output into CSV:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels contractors search \
--geo-id 92024 \
--permit-from 2024-01-01 \
--permit-to 2024-12-31 \
--tags solar \
--limit all \
| jq -r '["Name","Phone","Email","Permits","Avg Job Value"],
(.data[] | [.name, .primary_phone, .primary_email,
.permit_count, .avg_job_value]) | @csv' \
> solar_contractors_encinitas.csv
```
This creates a CSV with headers, ready for a spreadsheet or CRM import.
Scale up by searching at the state level:
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
shovels contractors search \
--geo-id CA \
--permit-from 2024-01-01 \
--permit-to 2024-12-31 \
--tags solar \
--min-permits 10 \
--limit all \
| jq -r '["Name","Phone","Permits"],
(.data[] | [.name, .primary_phone, .permit_count]) | @csv' \
> solar_contractors_ca.csv
```
Use `--min-permits` to filter for contractors with a track record.
## Adjusting Your Search
If your results are too broad or too narrow, here are some adjustments:
* **Too many results**: Add `--min-permits`, `--min-job-value`, or narrow the `--geo-id` to a smaller area
* **Too few results**: Expand the date range, use a broader geo\_id (county or state), or remove tag filters
* **Wrong specialty**: Use `shovels tags list` to see all available tags and pick the right one
* **Need specific contractors**: Add `--contractor-name` or `--contractor-license` to filter by known details
## Additional Help
If you run into issues, check the [CLI error codes](/docs/knowledge-base/cli/error-codes) guide or reach out to [support@shovels.ai](mailto:support@shovels.ai).
Happy Digging!
# Building an Accurate Contractor Search
Source: https://docs.shovels.ai/docs/tutorial-online-contractor-search
This tutorial will help you build an accurate search query in Shovels Online to find Contractors.
When using **Shovels Online**, it can be difficult to know where to start, and what parameters are the best to use, and which might just add extra noise.
This tutorial is just one example, which will give you a template for building Contractor searches of your own.
In this example, we'll be looking for contractors in New England (for this case, let's look at Massachusetts), specifically those that specialize in `heat pump` installations in `residential` buildings.
For best results, we'll limit our search to contractors that have at least 10 permits on file and have been active in the last 6 months.
Let's get into it.
## Step-by-Step Tutorial
Log in with your username and password at [app.shovels.ai](https://app.shovels.ai/login).
Go to the "Filter By" bar on the left-side of the page, and select the "Search In `Geography` dropdown.
Select "State".
Then, in the "Choose State" field, type in `Massachusetts`.
Go to the "Date Range" dropdown and select `Last 6 Months`.
In this example, we'll leave these filters blank.
However, ou can always add them if you'd like to ensure that your searching for contractors with experience working on projects of a certain size, in either fees or market value.
Click on the "Building Filters" dropdown to expand the list of filters.
Select "Property Type" and choose `Residential`.
As always, there are additional filters here, but we'll keep this example simple.
Click on the "Permits Filters" dropdown to expand the list of filters.
Select "Categories" and choose `Heat Pump`. You can select multiple project categories here, but keep in mind that this will potentially limit the results.
Click on the "Contractors Filters" dropdown to expand the list of filters.
Select "Min Total Permits Count" and type `10`.
Click "Apply Filters" at the bottom of the Filters panel, and ensure that the tab for "Contractors" is selected.
This will return a list of contractors that with experience working on heat pump installations in Massachusetts residential buildings in the last 6 months.
From there, you can either export the entire list of results (up to 1000 results) as a CSV, or you can click into the details of an individual contractor to dig deeper about their work history and experience.
## Adjusting Your Search
If you're not getting the results you want, then the next step will be to adjust your search filter selections. Depending on your use case, the necessary changes could be wide-ranging.
Below, we'll give you some basic examples of things you could adjust, in either direction.
* **Changing Geography**: If you have too many results, then you can narrow your search (or the reverse: expand your geographical area for more results).
* **Changing Date Range**: Similar to above, narrow or expand your results by changing how far back in time you're searching for.
* **Granular Permit Details**: If there are certain details that are usually included in the permit, such as keywords or specific brand name materials, etc, then you can search for these strings directly using the `Description` filter (Permits Filters > Description).
* **Specific Contractor Details**: If there are specific contractors or contractor types that you're searching for, such as `Company Name` or `Classification`, then those fields (under "Contractor Filters")can be used to more accurately target your search results.
## Additional help
If you run into any issues with your Contractor search, or anything else with Shovels Online, please reach out to us at [support@shovels.ai](mailto:support@shovels.ai) and we'll be happy to help.
Happy Digging!
# Release Notes
Source: https://docs.shovels.ai/release-notes/release-notes
Find out what the Engineering team has been up to across our entire platform, including general announcements, updates, and bugfixes.
### Introduction
The new Shovels app has launched, and Properties are already in it: **159M** property records searchable alongside permits and contractors, each opening on a profile with its permit history. This release also brings Miami-Dade County and Cook County into coverage — **3.8M** permits between them — plus **+9,865,649** net permits and congressional district on **20.8M** more permits.
### ✨ New
**The New Shovels App**
[app.shovels.ai](https://app.shovels.ai) has moved off the WeWeb-hosted build onto our own Next.js app on Vercel, running on the same API behind your integration. Two releases have landed since the August 21 preview, the first of them the launch on August 24.
**Properties in the App**
A properties search tab puts **159,000,000** US property records next to permits and contractors, filterable by owner, property or building type, market value, building and lot size, unit count, and permit activity, and readable as a list, a grid, or a map. Every property opens on a profile carrying ownership and address, building and lot attributes, permit totals and statuses, yearly activity, the related permit history, and a map location. Permit addresses now open the property profile, and property results export to CSV.
**Coverage Disclosures**
Absence searches — properties with no permit for a given type of work — disclose measured coverage and the geographic areas we withhold. Rows that could be unreliable are flagged, recent date ranges carry an ingestion-lag hint, and a fully suppressed page no longer shows a misleading zero.
**Sign-in Protection and Clearer Errors**
Password sign-in, registration, and account recovery now carry a CAPTCHA challenge. A missing permit, contractor, or property is told apart from a loading failure, so you see the right action. Support chat lives in a dedicated tab on your Account page.
**Search That Holds Its Ground**
A failed search only clears its own tab, and result totals stay accurate while you page. Opening a contractor from a permit remembers the permit, so Back takes you straight to it. Date-only values no longer shift a day by timezone.
### 🏗️ Permits Dataset
**New Permits Added**
* Net permits: **+9,865,649** (+5.7%)
* Permits newly linked to an address: **+9,897,495**
* Permits newly linked to a contractor: **+6,869,042**
All changes are measured against the August 1 release. "Net" is the change in total dataset size.
**🗺️ Newly Covered Jurisdictions**
**34** new jurisdictions added this release (50+ permits each), **3.9M** records in total. Two of the ten most populous counties in the US are among them:
* FL / Miami-Dade County: **3,246,460**
* IL / Cook County: **568,237**
* NC / Lexington: **87,458**
* OR / Hillsboro: **7,339**
* MA / Sudbury: **5,251**
* CT / Norwich: **3,281**
* VA / Isle of Wight County: **3,008**
* MA / South Hadley: **2,152**
* Plus 26 more
Only about a third of Miami-Dade permits carry a linked address in this release. We expect address coverage there to increase substantially over the following releases.
### 👷 Contractors
* Net contractors: **+174,333** (+4.9%)
* Net addresses: **+440,586**
### 🔧 Data Improvements
* Congressional district now sits on **20.8M** more permits, taking coverage from **51.5%** to **60.0%** after we moved to our own parcel data. Far fewer permits fall into the unknown bucket when you cut activity by district.
* `start_date` is now populated on every permit, up from **97.7%**.
* New York City filing dates are fixed. They now come from the filing itself rather than the work permit, which previously left them empty on every NYC record. Plan-approval dates are also no longer reported as the issue date.
* Address linkage rose from **73.9%** to **75.3%** of permits.
### Introduction
This release brings Miami-Dade County and Cook County into coverage — **3.8M** permits between them — alongside **+9,865,649** net permits and congressional district on **20.8M** more permits. First-seen dates moved earlier on roughly **43%** of permits and a further **1,262,026** permit IDs were re-keyed; review the **Data Changes** section before it trips up your integration.
### ⚠️ Action Required: Data Changes
**First-seen dates are now earlier on 74,506,884 permits (\~43%)**
`start_date` takes `first_seen_date` into account, so it moved earlier on the same permits and is now populated on every permit. If you sync incrementally on either field, permits you have already ingested will re-qualify for date windows they previously fell outside. Expect a larger pull on your next sync.
**1,262,026 permit IDs changed (\~0.7%)**
Two causes. For **565,939** permits we populated or corrected `file_date`, which the `id` is derived from — the usual enrichment re-key, as jurisdictions move onto data collection that captures more fields, plus the New York City filing-date fix. The other **696,087** permits, across **7** jurisdictions, had mislabeled geographic information and are now corrected: the permit data itself is unchanged, but if you filter by `state` or `jurisdiction` you will now find these records under the corrected values. Both groups are in the `old_id → new_id` changelog (Parquet); contact support to receive it.
**734,283 permits are temporarily missing their street address**
Concentrated in VA/Chesapeake, GA/Atlanta, CA/Contra Costa County, and CA/Oakland, which together account for three quarters of them. These records are being reprocessed as part of the collection upgrade and their address links will be restored as it completes — the same cause as the smaller gap flagged last release. Overall address coverage still rose this release.
### 🏗️ Permits Dataset
**New Permits Added**
* Net permits: **+9,865,649** (+5.7%)
* Permits newly linked to an address: **+9,897,495**
* Permits newly linked to a contractor: **+6,869,042**
All changes are measured against the August 1 release. "Net" is the change in total dataset size.
**🗺️ Newly Covered Jurisdictions**
**34** new jurisdictions added this release (50+ permits each), **3.9M** records in total. Two of the ten most populous counties in the US are among them:
* FL / Miami-Dade County: **3,246,460**
* IL / Cook County: **568,237**
* NC / Lexington: **87,458**
* OR / Hillsboro: **7,339**
* MA / Sudbury: **5,251**
* CT / Norwich: **3,281**
* VA / Isle of Wight County: **3,008**
* MA / South Hadley: **2,152**
* Plus 26 more
Only about a third of Miami-Dade permits carry a linked address in this release. We expect address coverage there to increase substantially over the following releases.
### 👷 Contractors
* Net contractors: **+174,333** (+4.9%)
* Net addresses: **+440,586**
### 🔧 Data Improvements
* Congressional district now sits on **20.8M** more permits, taking coverage from **51.5%** to **60.0%** after we moved to our own parcel data.
* `start_date` is now populated on every permit, up from **97.7%**.
* New York City filing dates are fixed. They now come from the filing itself rather than the work permit, which previously left them empty on every NYC record. Plan-approval dates are also no longer reported as the issue date.
* Address linkage rose from **73.9%** to **75.3%** of permits.
✅ **No breaking changes.** No endpoint, parameter, or response field changed, and all existing integrations continue to work unchanged. The action needed is on the data side: a re-sync for the changed permit IDs, and a wider incremental pull for the shifted first-seen dates above.
### 🏗️ Permits Dataset
**New Permits Added**
* Net permits: **+9,865,649** (+5.7%)
* Permits newly linked to an address: **+9,897,495**
* Permits newly linked to a contractor: **+6,869,042**
All changes are measured against the August 1 release. "Net" is the change in total dataset size.
**🗺️ Newly Covered Jurisdictions**
**34** new jurisdictions added this release (50+ permits each), **3.9M** records in total. Two of the ten most populous counties in the US are among them:
* FL / Miami-Dade County: **3,246,460**
* IL / Cook County: **568,237**
* NC / Lexington: **87,458**
* OR / Hillsboro: **7,339**
* MA / Sudbury: **5,251**
* CT / Norwich: **3,281**
* VA / Isle of Wight County: **3,008**
* MA / South Hadley: **2,152**
* Plus 26 more
Only about a third of Miami-Dade permits carry a linked address in this release. We expect address coverage there to increase substantially over the following releases.
### 👷 Contractors
* Net contractors: **+174,333** (+4.9%)
* Net addresses: **+440,586**
### 🔧 Data Improvements
* Congressional district now sits on **20.8M** more permits, taking coverage from **51.5%** to **60.0%** after we moved to our own parcel data.
* `start_date` is now populated on every permit, up from **97.7%**.
* New York City filing dates are fixed. They now come from the filing itself rather than the work permit, which previously left them empty on every NYC record. Plan-approval dates are also no longer reported as the issue date.
* Address linkage rose from **73.9%** to **75.3%** of permits.
### 🔁 Data Quality
First-seen dates moved earlier on **74,506,884** permits (~~**43%**), and `start_date` moved with them, so incremental syncs on either field will re-qualify records you have already ingested. A further **1,262,026** permit IDs changed (~~**0.7%**): **565,939** from populated or corrected `file_date`, and **696,087** across **7** jurisdictions whose mislabeled geographic information is now corrected — the permit data is unchanged, but these records now sit under corrected `state` and `jurisdiction` values. Both groups are in the `old_id → new_id` changelog (Parquet) on request. Separately, **734,283** permits are temporarily missing their street address while a collection upgrade completes, concentrated in VA/Chesapeake, GA/Atlanta, CA/Contra Costa County, and CA/Oakland.
### Introduction
Properties are now a first-class entity alongside permits and contractors: roughly **159M** US properties, each with its permit history already summarized on the record — including which properties have **no** permit on record for the work. This release also adds **+10,505,862** net permits, **377** newly covered jurisdictions, and more than **7x** the Michigan permit count.
### ✨ New
**Properties API (Beta)**
Any US property — about **159,000,000** of them — returned with its permit history summarized on the record. The headline capability is absence search: homes with no solar permit on record are a prospect list, a roof with no roofing permit in decades is an underwriting signal, and a freshly renovated listing with no permit on record is a due-diligence flag. Absence is easy to get confidently wrong, so where coverage is too thin we drop those properties instead of guessing (and tell you how many), and every absence answer carries a confidence score. Also included: filtering for permits pulled but never finalized, nationwide owner search across up to **10** owner names, and attribute filters for market value, lot size, building area, unit count, year built, and residential vs commercial. Attribute data is present on roughly **60–70%** of properties.
**Michigan Coverage**
Michigan went from **679K** permits to over **5M** in a single release — **+4,357,815** permits (+641.9%) — alongside **267** newly covered Michigan jurisdictions, from Detroit suburbs like Brownstown, Northville Township, and Bloomfield to Muskegon in the west. If you filtered Michigan out of your analysis before, it's worth turning it back on.
### 🏗️ Permits Dataset
**New Permits Added**
* **+11,136,248** new permits added this release
* Net permits: **+10,505,862** (+6.4%)
* Permits newly linked to an address: **+5,923,323**
**🗺️ Newly Covered Jurisdictions**
**377** new jurisdictions added this release (50+ permits each), **5.6M** records in total. Highlights:
* MN / Lakeville: **129,300**
* IA / Marion: **103,433**
* MI / Brownstown: **82,674**
* IL / Skokie: **77,159**
* MI / Northville Township: **75,229**
* MI / Bloomfield: **71,120**
* MN / Shoreview: **68,395**
* GA / Woodstock: **66,254**
* MI / Muskegon: **63,503**
* MI / Washington Charter: **62,731**
* Plus 367 more
### 👷 Contractors
* Newly added: **+496,833**
* Net contractors: **+398,475** (+12.6%)
### 🔧 Data Improvements
* Obvious typos in job values and fees now come back empty. When a source record carries an impossible number, we return an empty value instead, so your averages and totals aren't skewed by a single bad entry.
* Regenerated `description_derived` for **55,141** permits.
### Introduction
This release introduces the Properties API in beta — roughly **159M** US properties with permit history summarized on the record — plus **+10,505,862** net permits and **377** newly covered jurisdictions. Some permit IDs shifted this release when we enriched records with more fields, and a set of permits is temporarily missing its address link; review the **Data Changes** section before it trips up your integration.
### ⚠️ Action Required: Data Changes
**628,949 permit IDs changed (\~0.39%)**
These are the same permits — same `jurisdiction`, `state`, and `permit_number` — but a permit's `id` re-keys whenever we enrich it with more fields. An `old_id → new_id` changelog for the month is available in Parquet; contact support to receive it. A further **1,437** permits were removed.
**162,432 permits are temporarily missing their street address link**
These permits lost their `address_id` link while we finish a collection upgrade. We expect to restore them in an upcoming release. If your integration assumes a stable address link on these records, treat the gap as temporary rather than a deletion.
### 🆕 New API: Properties (Beta)
Any US property — about **159,000,000** of them — returned with its permit history already summarized on the record, including the question a permit search can't answer well: which properties have **no** permit on record for the work.
* `GET /v2/properties/search` — search properties
* `GET /v2/properties` — batch lookup, up to **50** ids
**Absence search.** Homes with no solar permit on record are a prospect list, a roof with no roofing permit in decades is an underwriting signal, and a freshly renovated listing with no permit on record is a due-diligence flag. A property can look permit-free just because its permits never linked to an address, so where coverage is too thin we drop those properties instead of guessing (and report how many), and every absence answer carries a confidence score plus a page-level `expected_miss_rate`. See [Absence Queries](https://docs.shovels.ai/docs/knowledge-base/api/properties/absence-queries).
**Pulled, but never finalized.** Filter for properties where a permit was pulled and never finalized. It reads the permit's status, not a guessed date — the classic case being a solar permit left open after the installer went out of business. See [Unfinaled Permits](https://docs.shovels.ai/docs/knowledge-base/api/properties/unfinaled-permits).
**Who owns what, nationwide.** Ownership records live county by county, which makes portfolios painful to assemble. Give up to **10** owner names and get every property recorded to them across the US, no location required.
**Attribute filters.** Cut any of these lists down to your segment: market value, lot size, building area, unit count, year built, residential vs commercial. Attribute data is present on roughly **60–70%** of properties. See [Property Search](https://docs.shovels.ai/docs/knowledge-base/api/properties/property-search).
The Properties API is in beta: query parameters, response fields, and the absence-trust surface may still change based on how it's used in practice.
### 🏗️ Permits Dataset
**New Permits Added**
* **+11,136,248** new permits added this release
* Net permits: **+10,505,862** (+6.4%)
* Permits newly linked to an address: **+5,923,323**
All changes are measured against the July 1 release. The "added" count includes IDs re-keyed by enrichment (\~**629K** permits); "net" is the change in total dataset size.
**🗺️ Newly Covered Jurisdictions**
**377** new jurisdictions added this release (50+ permits each), **5.6M** records in total. Highlights:
* MN / Lakeville: **129,300**
* IA / Marion: **103,433**
* MI / Brownstown: **82,674**
* IL / Skokie: **77,159**
* MI / Northville Township: **75,229**
* MI / Bloomfield: **71,120**
* Plus 371 more
Michigan alone gained **+4,357,815** permits (+641.9%) and **267** new jurisdictions, taking the state from **679K** permits to over **5M**.
### 👷 Contractors
* Newly added: **+496,833**
* Net contractors: **+398,475** (+12.6%)
### 🔧 Data Improvements
* Obvious typos in job values and fees now come back empty. When a source record carries an impossible number, we return an empty value instead of a figure that would skew your averages and totals.
* Regenerated `description_derived` for **55,141** permits.
✅ **No breaking changes.** The Properties endpoints are additive and all existing integrations continue to work unchanged. The only action needed is a re-sync for the changed permit IDs above.
### 🏗️ Permits Dataset
**New Permits Added**
* **+11,136,248** new permits added this release
* Net permits: **+10,505,862** (+6.4%)
* Permits newly linked to an address: **+5,923,323**
All changes are measured against the July 1 release. The "added" count includes IDs re-keyed by enrichment (\~**629K** permits); "net" is the change in total dataset size.
**🗺️ Newly Covered Jurisdictions**
**377** new jurisdictions added this release (50+ permits each), **5.6M** records in total. Highlights:
* MN / Lakeville: **129,300**
* IA / Marion: **103,433**
* MI / Brownstown: **82,674**
* IL / Skokie: **77,159**
* MI / Northville Township: **75,229**
* MI / Bloomfield: **71,120**
* Plus 371 more
Michigan alone gained **+4,357,815** permits (+641.9%) and **267** new jurisdictions, taking the state from **679K** permits to over **5M**.
### 👷 Contractors
* Newly added: **+496,833**
* Net contractors: **+398,475** (+12.6%)
### 🔧 Data Improvements
* Obvious typos in job values and fees now come back empty, so a single impossible source figure no longer skews aggregates.
* Regenerated `description_derived` for **55,141** permits.
### 🔁 Data Quality
**628,949** permit IDs changed (\~0.39%) and **1,437** were removed as records were enriched with more fields — same permits, same `jurisdiction`, `state`, and `permit_number`. An `old_id → new_id` changelog for the month is available in Parquet on request. Separately, **162,432** permits are temporarily missing their street address link while we finish a collection upgrade; we expect to restore them in an upcoming release.
### Introduction
Our biggest permit release since March: **+12,918,150** new permits, county coverage pushed past **87%** (up more than **16** points), and a new `telecom` tag backfilled across the full permit history — **1,513,665** permits now carry it.
### ✨ New
**New `telecom` Project Tag**
A `telecom` tag was backfilled across the full permit history — **1,513,665** permits now carry it. It flags telecom-related work: fiber, phone and cable drops, utility right-of-way installs, cell towers, small cells, 5G, antennas, cable installation, conduit, trenching, and boring. It sits alongside the tags you already use (electrical, solar, HVAC, roofing…) and changes nothing else.
### 🏗️ Permits Dataset
**New Permits Added**
* **+12,918,150** new permits added this release
* Net permits: **+12,406,760** (+8.2%)
* Permits newly linked to an address: **+9,977,549**
**🗺️ Newly Covered Jurisdictions**
**26** new jurisdictions added this release (50+ permits each), **3.0M** records in total. Highlights:
* WA / Spokane: **1,355,156**
* OK / Oklahoma City: **861,975**
* MN / Eagan: **154,661**
* TN / Brentwood: **94,491**
* MN / Rosemount: **85,314**
* GA / Canton: **78,591**
* KY / Oldham County: **62,705**
* UT / Draper: **32,696**
* MO / Bridgeton: **32,256**
* MO / Creve Coeur: **30,838**
* TN / Columbia: **30,490**
* IL / Glen Ellyn: **29,456**
* Plus 14 more
### 📍 Geographic Coverage
A big jump in geo enrichment this release, for better spatial completeness in mapping and location-based analysis.
* County: **70.6%** → **87.1%** (up more than **16** points)
* CBSA: **60.9%** → **82.0%** (up more than **21** points)
* CBSA FIPS: **60.9%** → **82.0%**
### 👷 Contractors
* Newly added: **+233,869**
* Net contractors: **+161,448** (+5.4%)
* Improved California contractor license file — business-held licenses now include the personnel (officers and members) tied to the company
### Introduction
This release adds **+12,918,150** new permits, a big jump in geographic coverage, and a new additive `telecom` tag backfilled across history. Some permit and address IDs shifted this release when we enriched records with more fields — review the **Data Changes** section before it trips up your integration.
### ⚠️ Action Required: Data Changes
**499,834 permit IDs changed (\~0.31%)**
These are the same permits — same `jurisdiction`, `state`, and `permit_number` — but a permit's `id` re-keys whenever we enrich it with more fields. This release we moved several jurisdictions onto data collection that captures more fields and populated or corrected `file_date`. An `old_id → new_id` changelog is available in CSV and Parquet; contact support to receive it. A further **11,556** permits were removed.
**267,506 permits had their `address_id` change**
The same enrichment attached an address to many permits that previously had none, so their address link was created or updated. If you cache `address_id` joins, re-sync the affected permits.
### 🆕 New Project Tag: `telecom`
A `telecom` tag was backfilled across the full permit history — **1,513,665** permits now carry it. It flags fiber, phone and cable drops, utility right-of-way installs, cell towers, small cells, 5G, antennas, cable installation, conduit, trenching, and boring. It works in `permit_tags` right away and changes nothing else.
### 🏗️ Permits Dataset
**New Permits Added**
* **+12,918,150** new permits added this release
* Net permits: **+12,406,760** (+8.2%)
* Permits newly linked to an address: **+9,977,549**
**🗺️ Newly Covered Jurisdictions**
**26** new jurisdictions added this release (50+ permits each), **3.0M** records in total. Highlights:
* WA / Spokane: **1,355,156**
* OK / Oklahoma City: **861,975**
* MN / Eagan: **154,661**
* TN / Brentwood: **94,491**
* MN / Rosemount: **85,314**
* GA / Canton: **78,591**
* Plus 20 more
### 📍 Geographic Coverage
* County: **70.6%** → **87.1%** (up more than **16** points)
* CBSA: **60.9%** → **82.0%** (up more than **21** points)
* CBSA FIPS: **60.9%** → **82.0%**
### 👷 Contractors
* Newly added: **+233,869**
* Net contractors: **+161,448** (+5.4%)
* Improved California contractor license file — business-held licenses now include the personnel (officers and members) tied to the company
✅ **No breaking changes.** The `telecom` tag is additive and all existing integrations continue to work unchanged. The only action needed is a re-sync for the changed permit and address IDs above.
### 🆕 New `telecom` Project Tag
A `telecom` tag was backfilled across the full permit history — **1,513,665** permits now carry it, flagging fiber, cell towers, small cells, 5G, conduit, trenching, and related infrastructure.
### 🏗️ Permits Dataset
**New Permits Added**
* **+12,918,150** new permits added this release
* Net permits: **+12,406,760** (+8.2%)
* Permits newly linked to an address: **+9,977,549**
**🗺️ Newly Covered Jurisdictions**
**26** new jurisdictions added this release (50+ permits each), **3.0M** records in total. Highlights:
* WA / Spokane: **1,355,156**
* OK / Oklahoma City: **861,975**
* MN / Eagan: **154,661**
* TN / Brentwood: **94,491**
* MN / Rosemount: **85,314**
* GA / Canton: **78,591**
* Plus 20 more
### 📍 Geographic Coverage
* County: **70.6%** → **87.1%** (up more than **16** points)
* CBSA: **60.9%** → **82.0%** (up more than **21** points)
* CBSA FIPS: **60.9%** → **82.0%**
### 👷 Contractors
* Newly added: **+233,869**
* Net contractors: **+161,448** (+5.4%)
* Improved California contractor license file — business-held licenses now include the personnel (officers and members) tied to the company
### 🔁 Data Quality
**499,834** permit IDs changed (\~0.31%) and **11,556** were removed as records were enriched with more fields — same permits, same `jurisdiction`, `state`, and `permit_number`. **267,506** permits had their `address_id` change. An `old_id → new_id` changelog is available in CSV and Parquet on request.
### Introduction
Our biggest data product launch since ReZone joins the platform: **Decisions** — the rezonings, variances, and planning votes that set a project in motion long before a permit is filed — is now available in beta. This release also adds **3.5M** new permits, **259** newly covered jurisdictions, and a new `window_door` project tag backfilled across history.
### ✨ New
**Decisions (Beta)**
Zoning and land-use decisions — approvals, rezonings, and variances from city council and planning meetings — are now available in beta across **600+** cities and roughly **200K** records, about half the jurisdictions where we already track permits and growing weekly. Each decision is tied to its property and the people on it, and threads through to permits and jurisdictions. Available on every plan, including the free trial, and via Enterprise Data License.
**New `window_door` Project Tag**
A `window_door` tag was backfilled across the full permit history — **3,494,250** permits now carry it. It sits alongside the tags you already use (electrical, solar, HVAC, roofing…) and changes nothing else.
### 🏗️ Permits Dataset
**New Permits Added**
* **+3,540,129** new permits added this release
* Net permits: **+3,386,216** (+2.3%)
* Total permits: **144,418,410**
* Permits newly linked to an address: **+2,456,748** (\~74.6% coverage)
**New Permits by Type**
* Electrical: **+626,854** (+2.1%)
* Remodel: **+374,906** (+2.4%)
* Plumbing: **+371,673** (+2.1%)
* HVAC: **+354,223** (+2.2%)
* New construction: **+333,810** (+2.3%)
* Roofing: **+299,450** (+3.3%)
* Addition: **+140,302** (+2.3%)
* Gas: **+133,410** (+2.7%)
* Grading: **+104,828** (+1.8%)
* Water heater: **+97,222** (+2.7%)
* Pool & hot tub: **+72,289** (+2.9%)
* Solar: **+67,596** (+1.9%)
* ADU: **+52,943** (+2.3%)
* Demolition: **+49,633** (+2.1%)
**🗺️ Newly Covered Jurisdictions**
**259** new jurisdictions added this release (20+ permits each), **7** of them over 10,000 records. Highlights:
* CT / West Hartford: **59,265**
* FL / Holmes Beach: **38,531**
* PA / Wilkes-Barre: **36,494**
* FL / Haines City: **22,395**
* NC / Morganton: **16,894**
* NC / Hoke County: **16,671**
* CA / St. Helena: **16,489**
* OH / Fairborn: **9,499**
* WI / Greendale: **9,351**
* GA / Greene County: **8,365**
* Plus 249 more
### 👷 Contractors
* Newly added: **+112,915**
* Net contractors: **+74,150** (+2.6%)
* Total contractors: **2,712,522**
* New contractor-license boards in AL, WI, KY, ME, and NC (plumbing & electrical)
### Introduction
**Decisions** is now in the API in beta — a new zoning and land-use data product — alongside a new `GET /v2/meta/coverage` endpoint, the additive `window_door` tag, and a faster, more resilient API. Several behavior and data changes need a look before they trip up your integration; review the Action Required sections first.
This release includes breaking changes to auth status codes, rate limiting, input validation, a permit status reclassification, and \~141K re-keyed permit IDs. Review the **Action Required** sections below.
### ⚠️ Action Required: API Behavior Changes
**Auth errors now return `401` (was `403`)**
A missing or invalid `X-API-Key` now returns `401`. We've kept `403` for a valid key that lacks admin access. If your client branches on status codes, point the auth case at `401`.
**Rate limiting is live — `429`**
Per-key rate limiting starts at **120** requests per **60** seconds, per key — well above normal use. Exceed it and you'll get a `429` with a `Retry-After` header indicating how many seconds to wait. Make sure your client honors it and backs off. If that limit is too tight for your use case, contact support.
**`contractor_name` now requires at least 3 characters**
On `GET /v2/contractors/search`, a name under 3 characters now returns `422` instead of running a slow scan. Those names are still reachable by `id`, license, or geo.
### ⚠️ Action Required: Data Changes
**141,793 permit IDs changed (\~0.1%)**
These are the same permits — same jurisdiction, state, and `permit_number` — but their `file_date` shifted upstream, which moves the `id`. An `old_id → new_id` changelog is available; contact support to receive it. Most affected jurisdictions:
* TX / Temple: **48,203**
* FL / Seminole County: **41,899**
* FL / Escambia County: **16,539**
* CA / Bakersfield: **10,838**
**Some `address_id` links moved**
* Newly linked to an address: **165,427** (0.11%)
* Address link removed: **241,031** (0.17%)
* Re-matched to a different address: **119,534** (0.08%)
When a re-scraped permit no longer carries a published address, we drop the link; our matching also tightened. If you cache `address_id` joins, re-sync the affected permits.
**"Approved" permits now map to `IN_REVIEW`**
A bare "Approved" means plans cleared review but fees aren't paid yet — that's pre-issuance, so it now maps to `IN_REVIEW` rather than `ACTIVE`. Only `APPROVED FOR CONSTRUCTION` stays `ACTIVE`. Adjust any status-based filtering to match.
### 🆕 Decisions API (Beta)
`GET /v2/decisions/search` · `GET /v2/decisions`
The approvals, rezonings, and variances that come out of city council and planning meetings — the decisions that set a project in motion long before a permit exists.
* **Coverage:** **600+** cities and roughly **200K** decision records today — about half the jurisdictions where we already have permits, and growing weekly.
* **Record shape:** each decision is tied to its property (address, coordinates, geo IDs) and to the people on it — applicants, owners, developers, architects, engineers — and threads through to permits and jurisdictions.
* **Access & price:** on every plan, including the free trial, and via Enterprise Data License. During beta it's **1** credit per decision; we'll notify you before that changes.
* **Usage:** works like `/v2/permits/search` — date range + `geo_id` required, cursor pagination, filters (asset class / category / subcategory / property type / project value / full-text), CSV export (≤1,000 rows), and batch-by-id at `/v2/decisions`.
* **Note:** the source has no ZIP, so ZIP-level `geo_id` filters return `422`.
### 🆕 Data Coverage Endpoint
`GET /v2/meta/coverage`
Per-field fill rates for any state, county, city, ZIP, or jurisdiction, bucketed into tiers (under 10%, partial, over 80%). It saves you from filtering on a field in a place where we don't ingest it and wondering why results look thin.
### 🆕 New Project Tag: `window_door`
A `window_door` tag was backfilled across the full permit history — **3,494,250** permits now carry it. It works in `permit_tags` right away and changes nothing else.
### ⚡ API Enhancements
* **Billing:** hitting a credit or trial limit now returns a `402` with a Stripe `upgrade_url` so you can self-serve. Additive — nothing breaks.
* **Contractor search** now matches on business name, not just the contact name.
* **Address search** handles a ZIP inside the query and multi-word cities correctly, and resolves typos faster.
* **Geo search** no longer 500s on odd input — malformed geo IDs return a clean `422`.
* **Speed:** contractor-name filtering is quicker and database pressure under load is reduced.
### 🧰 Shovels CLI
* New `coverage` command (under `cities`, `counties`, `jurisdictions`, `states`, `zipcodes`) wraps the new coverage endpoint, with date-window flags. It's credit-exempt.
* A `decisions` command is coming right behind this release.
### 🏗️ Permits Dataset
**New Permits Added**
* **+3,540,129** new permits added this release
* Net permits: **+3,386,216** (+2.3%)
* Total permits: **144,418,410**
* Permits newly linked to an address: **+2,456,748** (\~74.6% coverage)
**New Permits by Type**
* Electrical: **+626,854** (+2.1%)
* Remodel: **+374,906** (+2.4%)
* Plumbing: **+371,673** (+2.1%)
* HVAC: **+354,223** (+2.2%)
* New construction: **+333,810** (+2.3%)
* Roofing: **+299,450** (+3.3%)
* Addition: **+140,302** (+2.3%)
* Gas: **+133,410** (+2.7%)
* Grading: **+104,828** (+1.8%)
* Water heater: **+97,222** (+2.7%)
* Pool & hot tub: **+72,289** (+2.9%)
* Solar: **+67,596** (+1.9%)
* ADU: **+52,943** (+2.3%)
* Demolition: **+49,633** (+2.1%)
**🗺️ Newly Covered Jurisdictions**
**259** new jurisdictions added this release (20+ permits each), **7** of them over 10,000 records. Highlights:
* CT / West Hartford: **59,265**
* FL / Holmes Beach: **38,531**
* PA / Wilkes-Barre: **36,494**
* FL / Haines City: **22,395**
* NC / Morganton: **16,894**
* NC / Hoke County: **16,671**
* CA / St. Helena: **16,489**
* OH / Fairborn: **9,499**
* WI / Greendale: **9,351**
* GA / Greene County: **8,365**
* Plus 249 more
### 👷 Contractors
* Newly added: **+112,915**
* Net contractors: **+74,150** (+2.6%)
* Total contractors: **2,712,522**
* New contractor-license boards in AL, WI, KY, ME, and NC (plumbing & electrical)
### 🆕 Decisions (Beta)
The new Decisions dataset — zoning and land-use approvals, rezonings, and variances across **600+** cities and roughly **200K** records — is available via Enterprise Data License, tied through to properties, people, permits, and jurisdictions.
### 🏗️ Permits Dataset
**New Permits Added**
* **+3,540,129** new permits added this release
* Net permits: **+3,386,216** (+2.3%)
* Total permits: **144,418,410**
* Permits newly linked to an address: **+2,456,748** (\~74.6% coverage)
**New Permits by Type**
* Electrical: **+626,854** (+2.1%)
* Remodel: **+374,906** (+2.4%)
* Plumbing: **+371,673** (+2.1%)
* HVAC: **+354,223** (+2.2%)
* New construction: **+333,810** (+2.3%)
* Roofing: **+299,450** (+3.3%)
* Addition: **+140,302** (+2.3%)
* Gas: **+133,410** (+2.7%)
* Grading: **+104,828** (+1.8%)
* Water heater: **+97,222** (+2.7%)
* Pool & hot tub: **+72,289** (+2.9%)
* Solar: **+67,596** (+1.9%)
* ADU: **+52,943** (+2.3%)
* Demolition: **+49,633** (+2.1%)
**🗺️ Newly Covered Jurisdictions**
**259** new jurisdictions added this release (20+ permits each), **7** of them over 10,000 records. Highlights:
* CT / West Hartford: **59,265**
* FL / Holmes Beach: **38,531**
* PA / Wilkes-Barre: **36,494**
* FL / Haines City: **22,395**
* NC / Morganton: **16,894**
* NC / Hoke County: **16,671**
* CA / St. Helena: **16,489**
* OH / Fairborn: **9,499**
* WI / Greendale: **9,351**
* GA / Greene County: **8,365**
* Plus 249 more
### 👷 Contractors
* Newly added: **+112,915**
* Net contractors: **+74,150** (+2.6%)
* Total contractors: **2,712,522**
* New contractor-license boards in AL, WI, KY, ME, and NC (plumbing & electrical)
### 🪟 Data Quality
A `window_door` project tag was backfilled across the full permit history — **3,494,250** permits now carry it.
### Introduction
Plenty of fresh coverage this release. We've added **7.5M** new permits and **32** newly covered jurisdictions — Portland (OR), Orlando (FL), and a strong cluster of Twin Cities suburbs lead the way. **+131K** contractors were added too. Total permits grow by +**7.19M** (+5.4%).
### ✨ New
**Contractor Exports Return Full Result Set**
When exporting contractors from the Shovels Online web app, the resulting CSV was previously capped at the on-screen page size (**50** rows). Exports now include all matching contractors up to **1,000** rows. No action required.
### 🏗️ Permits Dataset
**New Permits Added**
* **+7,479,676** new permits added this release
* Total permits: **141,032,194** (+5.4%)
* Permits filed in April 2026: **243,517**
* Permits with linked address: **105.2M** (74.6%, +0.8pp)
**New Permits by Type**
* Electrical: **1,417,453**
* Plumbing: **1,185,763**
* HVAC: **942,391**
* Remodel: **889,732**
* New construction: **648,351**
* Roofing: **489,444**
* Addition: **325,031**
* Demolition: **101,553**
* Solar: **91,852**
**🗺️ Newly Covered Jurisdictions**
**32** new jurisdictions added, each with >10,000 historical records. Highlights:
* OR / Portland: **1,578,132**
* FL / Orlando: **1,028,207**
* MN / Maple Grove: **331,140**
* MN / St. Louis Park: **301,465**
* MN / Minnetonka: **264,317**
* MN / Eden Prairie: **193,188**
* MN / Edina: **168,687**
* MN / Apple Valley: **133,736**
* MN / Golden Valley: **104,639**
* NC / Holly Springs: **99,545**
* Plus 22 more across OH, OK, PA, CO, IL, GA, MO, WA, NC
### 👷 Contractors
* Total contractors: **2,638,372** (up from **2,550,254**)
* Newly added: **+130,899**
### Introduction
This release adds **7.5M** new permits, **32** newly covered jurisdictions, and **131K** new contractors. Total permits reach **141M** (+5.4%).
### 🏗️ Permits Dataset
**New Permits Added**
* **+7,479,676** new permits added this release
* Total permits: **141,032,194** (+5.4%)
* Permits filed in April 2026: **243,517**
* Permits with linked address: **105.2M** (74.6%, +0.8pp)
**New Permits by Type**
* Electrical: **1,417,453**
* Plumbing: **1,185,763**
* HVAC: **942,391**
* Remodel: **889,732**
* New construction: **648,351**
* Roofing: **489,444**
* Addition: **325,031**
* Demolition: **101,553**
* Solar: **91,852**
**🗺️ Newly Covered Jurisdictions**
**32** new jurisdictions added, each with >10,000 historical records. Highlights:
* OR / Portland: **1,578,132**
* FL / Orlando: **1,028,207**
* MN / Maple Grove: **331,140**
* MN / St. Louis Park: **301,465**
* MN / Minnetonka: **264,317**
* MN / Eden Prairie: **193,188**
* MN / Edina: **168,687**
* MN / Apple Valley: **133,736**
* MN / Golden Valley: **104,639**
* NC / Holly Springs: **99,545**
* Plus 22 more across OH, OK, PA, CO, IL, GA, MO, WA, NC
### 👷 Contractors
* Total contractors: **2,638,372** (up from **2,550,254**)
* Newly added: **+130,899**
### ℹ️ Note on Permit IDs
As part of routine pipeline updates, **280,010** permits (0.28%) had their `id` regenerated this release. The underlying permits are unchanged — same jurisdiction, state, and `permit_number` — but small shifts in `file_date` regenerate the `id`. Largest impacted: TN / Nashville (**207K**), FL / Seminole County (**43K**). Reply to your release notes email if you need the old\_id → new\_id changelog.
✅ **No breaking changes.** All existing integrations continue to work unchanged.
### 🏗️ Permits Dataset
**New Permits Added**
* **+7,479,676** new permits added this release
* Total permits: **141,032,194** (+5.4%)
* Permits filed in April 2026: **243,517**
* Permits with linked address: **105.2M** (74.6%, +0.8pp)
**New Permits by Type**
* Electrical: **1,417,453**
* Plumbing: **1,185,763**
* HVAC: **942,391**
* Remodel: **889,732**
* New construction: **648,351**
* Roofing: **489,444**
* Addition: **325,031**
* Demolition: **101,553**
* Solar: **91,852**
**🗺️ Newly Covered Jurisdictions**
**32** new jurisdictions added, each with >10,000 historical records. Highlights:
* OR / Portland: **1,578,132**
* FL / Orlando: **1,028,207**
* MN / Maple Grove: **331,140**
* MN / St. Louis Park: **301,465**
* MN / Minnetonka: **264,317**
* MN / Eden Prairie: **193,188**
* MN / Edina: **168,687**
* MN / Apple Valley: **133,736**
* MN / Golden Valley: **104,639**
* NC / Holly Springs: **99,545**
* Plus 22 more across OH, OK, PA, CO, IL, GA, MO, WA, NC
### 👷 Contractors
* Total contractors: **2,638,372** (up from **2,550,254**)
* Newly added: **+130,899**
### ℹ️ Note on Permit IDs
As part of routine pipeline updates, **280,010** permits (0.28%) had their `id` regenerated this release. The underlying permits are unchanged — same jurisdiction, state, and `permit_number` — but small shifts in `file_date` regenerate the `id`. Largest impacted: TN / Nashville (**207K**), FL / Seminole County (**43K**). Contact your account team if you need the old\_id → new\_id changelog.
### Introduction
This release marks a major milestone: Shovels is now powered entirely by our own data pipeline. We've transitioned away from our third-party permit data provider, giving us full end-to-end ownership over permit and contractor data quality, enrichment, and refresh cycles. While the total permit count is lower, the data is significantly richer — with improved field coverage across the board, a powerful new `description_derived` field on 81M permits, and better contractor intelligence. This is a one-time transition and a stronger foundation for everything going forward.
### ✨ New
**🔄 Fully Shovels-Owned Pipeline**
100% Shovels-Owned Data — faster iteration, better enrichment, consistent data provenance.
All third-party provider data has been removed. This gives us full control over data quality and allows us to iterate faster on enrichments without vendor dependencies. Each future release will continue as a full snapshot, growing as we expand our coverage.
### 🏗️ Permits Dataset
**Net-New Permits Added**
* **+11.6M** Permits — expanded coverage across new jurisdictions: NY (+4.0M), TX (+3.5M), FL (+1.9M)
* Total permits in this release: **129.9M**
* Permits retained with original IDs: **118.3M**
* Permits filed in March 2026: **352K**
**New Field: `description_derived`**
* **81M** Permits — **62.4%** coverage
* A clean, plain-English summary of each permit's raw description (e.g., "Tear off and replace the roof on an existing single-family home")
**📊 Permit Data Quality**
Field coverage improved significantly in this release:
* Subtype coverage: **75.7%** (+18%)
* Final date coverage: **70.5%** (+16%)
* Fees coverage: **65.0%** (+11%)
* Job value coverage: **62.6%** (+10%)
* Issue date coverage: **80.5%** (+9%)
* File date coverage: **90.5%** (+4%)
### 👷 Contractor Intelligence
**Contractor License Coverage**
* **63.5%** (+20%) — up from 43%, significantly richer licensing data on **2.5M** contractors
* Phone number coverage: **57.7%** (+7%)
* Email address coverage: **30.2%** (+6%)
**3 New State Licensing Boards Added**
Massachusetts plumbing (\~**120K** plumbers), North Carolina plumbing & electrical, and Iowa plumbing. CSL dataset grew to **3.14M** records (+4%).
### ⚠️ Important Changes
This release includes breaking changes. Please review carefully.
**Contractor IDs have been regenerated** — if you've stored contractor IDs, they will need to be updated. We've prepared a contractor ID changelog mapping old IDs to new ones — **81.8%** of new contractors are mapped. Download below:
* API customers: [CSV](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3kSW5FHHjf6v6tpbW674fRP1R2lLwW1Rn6tN73dcMDW4GsQDQ98SGw3N6vy4lVm4CsvN11-T7bshGFSW6CbHSq3wj5yLW49845J4_sVl9MmY8YFQXg22W4RD6pS4cllXZW4xwn771mwZRWW3tzSlD91mj6HW45VSRj2QqJTMW3h7rtP4sXfpgW4crt1r6vxFBCN907w1gr-_69N707N7PXDYptF2LGBT36Tw3W3w6kF292m2v5W4cdT7k5NRPcvW6Ql9YM86tWgQW4gPSWW6WVzG_W7JS4vG5Kwn7jW11-7yc1SY66qf2ffJC404) · [Parquet](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3pjN8v7d4B7P-fHVsFdnZ1WF_ZrVTqSB76Jnk4dW2N8t4gh4lZW4G1s3Q2N3K35W3X-HKF4Dm42QW7YQ5cn8tjyFSW97DzMJ4KHs3vW53jsbw6wfWLyW1_zx9h1mrW0-N7h_CsCMqPDyW1YLhR48HFFgfMc4wrVJx0N3W35l5Pn717L0RW8fxL3m5vHVLnW3vKBhH2T5W55W1Lzy8-8PW-YnW8W0FX-7lJ_6sW8FNLc73wwRRnW2K1l9V285Bm3W64Hrtb4mbTjMW2hLgLH8gkxbgW3grbJt8t-9WGVGjYY22fn7nSf3BSfSK04)
* EDL customers: [CSV](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3nBW8F6Xd224xD1sW3LkhRt3m9crmW3sWkzn2hTQh7N25zvBSZrqkgW1mZy1523Qt3PW39vzvW3cfbGBW19ggrC6TVx_TW6P_try6HvLbJVCq5Kd8qD83bW7jDWPD68H_ZtW6CSqZY4NzgjgW5D8SRM1X_85lW7D3D0826xqsNW24v1751FDK98W6xV5987WGwQKW4y0QjJ2yF0HJW8PLLCq4w7bWNW2BJHJt5j1j1PW7vsg4w51QmjsW8Ynw7j2nC04yW5t1Q4-8XjvXBW6DNRWb6Q1bL2W6zkKY58PJ0dzVNzwPD7JsqV9f662NQv04) · [Parquet](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3lKW7drhVs6TTxHyW80CqX97hWX2tW5PtSbj86S_8VW5XJdRG2VT9MsW8WHTs88r9lvfN6Sk5FG95t6lW1-G_9p1z_jxdW1H599x3CJGLbW6H5WJn5tpGg0W5P2lBq8zRQK5W3Wk8Gz75KCltW19md2K1q6F_jW7W84R6587TJ2W8P6-Dp1fS3SvW2BJ1v48RD2jgW6YVqRt2nLlkFW6Nwx3677NKpMW16RzXG75_gx6N3WC5g6CsC5YW1hLxv59dZJ3_W4hMkc_7wFPBbN1pdJDcbxYL0W2RMp4F9g6NcFN2p4Yd1W6XXgf4q396j04)
For background on how contractor IDs work, see [Why Contractor IDs Can Change](https://docs.shovels.ai/docs/knowledge-base/data/contractors/id-changes#why-contractor-ids-can-change).
**Permit count decreased** — from 216M to **130M**. **98M** older permits sourced from a third-party provider were removed. The **118.3M** permits that remain kept their original IDs — no action needed for those records.
**Contractor count decreased** — from 3.9M to **2.5M**. This reflects the removal of duplicates and low-quality records. The remaining **2.5M** contractors are cleaner and more richly attributed.
**Some jurisdictions have reduced coverage** — DC was dropped entirely. VT, RI, MA, CT, DE, WI, and NM lost >80% of permits due to the removal of third-party data. Coverage will grow as we expand our own data collection.
**`first_seen_date` has been reset** — all values now fall between 2025-05-19 and 2026-03-28. Going forward, this field will accumulate normally with each biweekly release.
**Employee and resident records reduced** — Employees: 38.1M → **12.8M**. Residents: 45.9M → **27.6M**.
### Introduction
This release transitions Shovels to a fully owned data pipeline, with richer field coverage, a new `description_derived` field on 81M permits, and better contractor intelligence.
⚠️ **This release includes breaking changes.** Contractor IDs have been regenerated and permit/contractor counts have changed significantly. See the Important Changes section below.
### ✨ New
**🔄 Fully Shovels-Owned Pipeline**
100% Shovels-Owned Data — faster iteration, better enrichment, consistent data provenance. All third-party provider data has been removed, giving us full control over data quality and enrichment without vendor dependencies.
### 🏗️ Permits Dataset
**Net-New Permits Added**
* **+11.6M** Permits — expanded coverage: NY (+4.0M), TX (+3.5M), FL (+1.9M)
* Total permits in this release: **129.9M**
* Permits retained with original IDs: **118.3M**
* Permits filed in March 2026: **352K**
**New Field: `description_derived`**
* Available on **81M** permits — **62.4%** coverage
* Plain-English summary of each permit's raw description
**📊 Permit Data Quality**
* Subtype coverage: **75.7%** (+18%)
* Final date coverage: **70.5%** (+16%)
* Fees coverage: **65.0%** (+11%)
* Job value coverage: **62.6%** (+10%)
* Issue date coverage: **80.5%** (+9%)
* File date coverage: **90.5%** (+4%)
### 👷 Contractor Intelligence
* Contractor license coverage: **63.5%** (+20%)
* Phone number coverage: **57.7%** (+7%)
* Email address coverage: **30.2%** (+6%)
* **3** new state licensing boards: Massachusetts plumbing, North Carolina plumbing & electrical, Iowa plumbing
* CSL dataset: **3.14M** records (+4%)
### ⚠️ Important Changes
**Contractor IDs have been regenerated** — if you've stored contractor IDs, they will need to be updated. We've prepared a contractor ID changelog mapping old IDs to new ones — **81.8%** of new contractors are mapped. Download below:
* API customers: [CSV](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3kSW5FHHjf6v6tpbW674fRP1R2lLwW1Rn6tN73dcMDW4GsQDQ98SGw3N6vy4lVm4CsvN11-T7bshGFSW6CbHSq3wj5yLW49845J4_sVl9MmY8YFQXg22W4RD6pS4cllXZW4xwn771mwZRWW3tzSlD91mj6HW45VSRj2QqJTMW3h7rtP4sXfpgW4crt1r6vxFBCN907w1gr-_69N707N7PXDYptF2LGBT36Tw3W3w6kF292m2v5W4cdT7k5NRPcvW6Ql9YM86tWgQW4gPSWW6WVzG_W7JS4vG5Kwn7jW11-7yc1SY66qf2ffJC404) · [Parquet](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3pjN8v7d4B7P-fHVsFdnZ1WF_ZrVTqSB76Jnk4dW2N8t4gh4lZW4G1s3Q2N3K35W3X-HKF4Dm42QW7YQ5cn8tjyFSW97DzMJ4KHs3vW53jsbw6wfWLyW1_zx9h1mrW0-N7h_CsCMqPDyW1YLhR48HFFgfMc4wrVJx0N3W35l5Pn717L0RW8fxL3m5vHVLnW3vKBhH2T5W55W1Lzy8-8PW-YnW8W0FX-7lJ_6sW8FNLc73wwRRnW2K1l9V285Bm3W64Hrtb4mbTjMW2hLgLH8gkxbgW3grbJt8t-9WGVGjYY22fn7nSf3BSfSK04)
* EDL customers: [CSV](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3nBW8F6Xd224xD1sW3LkhRt3m9crmW3sWkzn2hTQh7N25zvBSZrqkgW1mZy1523Qt3PW39vzvW3cfbGBW19ggrC6TVx_TW6P_try6HvLbJVCq5Kd8qD83bW7jDWPD68H_ZtW6CSqZY4NzgjgW5D8SRM1X_85lW7D3D0826xqsNW24v1751FDK98W6xV5987WGwQKW4y0QjJ2yF0HJW8PLLCq4w7bWNW2BJHJt5j1j1PW7vsg4w51QmjsW8Ynw7j2nC04yW5t1Q4-8XjvXBW6DNRWb6Q1bL2W6zkKY58PJ0dzVNzwPD7JsqV9f662NQv04) · [Parquet](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3lKW7drhVs6TTxHyW80CqX97hWX2tW5PtSbj86S_8VW5XJdRG2VT9MsW8WHTs88r9lvfN6Sk5FG95t6lW1-G_9p1z_jxdW1H599x3CJGLbW6H5WJn5tpGg0W5P2lBq8zRQK5W3Wk8Gz75KCltW19md2K1q6F_jW7W84R6587TJ2W8P6-Dp1fS3SvW2BJ1v48RD2jgW6YVqRt2nLlkFW6Nwx3677NKpMW16RzXG75_gx6N3WC5g6CsC5YW1hLxv59dZJ3_W4hMkc_7wFPBbN1pdJDcbxYL0W2RMp4F9g6NcFN2p4Yd1W6XXgf4q396j04)
For background on how contractor IDs work, see [Why Contractor IDs Can Change](https://docs.shovels.ai/docs/knowledge-base/data/contractors/id-changes#why-contractor-ids-can-change).
**Permit count decreased** — from 216M to **130M**. **98M** older third-party permits removed. The **118.3M** permits that remain kept their original IDs.
**Contractor count decreased** — from 3.9M to **2.5M**, reflecting removal of duplicates and low-quality third-party records.
**Some jurisdictions have reduced coverage** — DC dropped entirely. VT, RI, MA, CT, DE, WI, and NM lost >80% of permits.
**Field coverage changes** — geocoding: 72% → **67%**, inspection data: 15% → **3%**, address ID: 78% → **74%** (improving in upcoming releases).
**`first_seen_date` has been reset** — all values now fall between 2025-05-19 and 2026-03-28.
### Introduction
This release transitions Shovels to a fully owned data pipeline, with richer field coverage, a new `description_derived` field on 81M permits, and improved contractor intelligence. Please review the Important Changes section for breaking changes.
### 🏗️ Permits Dataset
**Net-New Permits Added**
* **+11.6M** Permits — NY (+4.0M), TX (+3.5M), FL (+1.9M)
* Total permits in this release: **129.9M**
* Permits retained with original IDs: **118.3M**
* Permits filed in March 2026: **352K**
**New Field: `description_derived`**
* **81M** Permits — **62.4%** coverage
* Plain-English summaries of permit raw descriptions
**📊 Permit Data Quality**
* Subtype coverage: **75.7%** (+18%)
* Final date coverage: **70.5%** (+16%)
* Fees coverage: **65.0%** (+11%)
* Job value coverage: **62.6%** (+10%)
* Issue date coverage: **80.5%** (+9%)
* File date coverage: **90.5%** (+4%)
### 👷 Contractor Intelligence
* Contractor license coverage: **63.5%** (+20%)
* Phone number coverage: **57.7%** (+7%)
* Email address coverage: **30.2%** (+6%)
* **3** new state licensing boards added
* CSL dataset: **3.14M** records (+4%)
### ⚠️ Important Changes
**Contractor IDs have been regenerated** — if you've stored contractor IDs, they will need to be updated. We've prepared a contractor ID changelog mapping old IDs to new ones — **81.8%** of new contractors are mapped. Download below:
* API customers: [CSV](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3kSW5FHHjf6v6tpbW674fRP1R2lLwW1Rn6tN73dcMDW4GsQDQ98SGw3N6vy4lVm4CsvN11-T7bshGFSW6CbHSq3wj5yLW49845J4_sVl9MmY8YFQXg22W4RD6pS4cllXZW4xwn771mwZRWW3tzSlD91mj6HW45VSRj2QqJTMW3h7rtP4sXfpgW4crt1r6vxFBCN907w1gr-_69N707N7PXDYptF2LGBT36Tw3W3w6kF292m2v5W4cdT7k5NRPcvW6Ql9YM86tWgQW4gPSWW6WVzG_W7JS4vG5Kwn7jW11-7yc1SY66qf2ffJC404) · [Parquet](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3pjN8v7d4B7P-fHVsFdnZ1WF_ZrVTqSB76Jnk4dW2N8t4gh4lZW4G1s3Q2N3K35W3X-HKF4Dm42QW7YQ5cn8tjyFSW97DzMJ4KHs3vW53jsbw6wfWLyW1_zx9h1mrW0-N7h_CsCMqPDyW1YLhR48HFFgfMc4wrVJx0N3W35l5Pn717L0RW8fxL3m5vHVLnW3vKBhH2T5W55W1Lzy8-8PW-YnW8W0FX-7lJ_6sW8FNLc73wwRRnW2K1l9V285Bm3W64Hrtb4mbTjMW2hLgLH8gkxbgW3grbJt8t-9WGVGjYY22fn7nSf3BSfSK04)
* EDL customers: [CSV](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3nBW8F6Xd224xD1sW3LkhRt3m9crmW3sWkzn2hTQh7N25zvBSZrqkgW1mZy1523Qt3PW39vzvW3cfbGBW19ggrC6TVx_TW6P_try6HvLbJVCq5Kd8qD83bW7jDWPD68H_ZtW6CSqZY4NzgjgW5D8SRM1X_85lW7D3D0826xqsNW24v1751FDK98W6xV5987WGwQKW4y0QjJ2yF0HJW8PLLCq4w7bWNW2BJHJt5j1j1PW7vsg4w51QmjsW8Ynw7j2nC04yW5t1Q4-8XjvXBW6DNRWb6Q1bL2W6zkKY58PJ0dzVNzwPD7JsqV9f662NQv04) · [Parquet](https://email.shovels.ai/e3t/Ctc/2Q+113/d5GN6904/VWDNN14TlTsMW62Df9z7NFx6dW7FnbcL5Mn68SN6hnppx3l5QzW7lCdLW6lZ3lKW7drhVs6TTxHyW80CqX97hWX2tW5PtSbj86S_8VW5XJdRG2VT9MsW8WHTs88r9lvfN6Sk5FG95t6lW1-G_9p1z_jxdW1H599x3CJGLbW6H5WJn5tpGg0W5P2lBq8zRQK5W3Wk8Gz75KCltW19md2K1q6F_jW7W84R6587TJ2W8P6-Dp1fS3SvW2BJ1v48RD2jgW6YVqRt2nLlkFW6Nwx3677NKpMW16RzXG75_gx6N3WC5g6CsC5YW1hLxv59dZJ3_W4hMkc_7wFPBbN1pdJDcbxYL0W2RMp4F9g6NcFN2p4Yd1W6XXgf4q396j04)
For background on how contractor IDs work, see [Why Contractor IDs Can Change](https://docs.shovels.ai/docs/knowledge-base/data/contractors/id-changes#why-contractor-ids-can-change).
**Permit count decreased** — from 216M to **130M**. **98M** older third-party permits removed. The **118.3M** permits that remain kept their original IDs.
**Contractor count decreased** — from 3.9M to **2.5M**, reflecting removal of duplicates and low-quality records.
**Some jurisdictions have reduced coverage** — DC dropped entirely. VT, RI, MA, CT, DE, WI, and NM lost >80% of permits.
**Field coverage changes** — geocoding: 72% → **67%**, inspection data: 15% → **3%**, address ID: 78% → **74%** (improving in upcoming releases).
**`first_seen_date` has been reset** — all values now fall between 2025-05-19 and 2026-03-28.
**Employee and resident records reduced** — Employees: 38.1M → **12.8M**. Residents: 45.9M → **27.6M**.
### Introduction
Big month. We're launching Shovels CLI — a new way to query permits, contractors, and addresses from the command line (or let your AI agent do it). On the data side, we added 1.8M new permits and geocoded another 1.4M records. Let's get into it.
### ✨ New
**>\_ Introducing Shovels CLI**
Your AI agent's gateway to U.S. construction data
One binary. Zero config. Pure JSON.
Search permits, query contractors, resolve addresses — all from your terminal
**Install in seconds**
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -LsSf https://shovels.ai/install.sh | sh
```
**Built for automation**
* JSON-only output to stdout, errors to stderr
* Auto-pagination with `--limit all`
* Auto-retry with backoff on rate limits
* Help text optimized for LLM comprehension
Works on macOS, Linux, and Windows. Learn more at [shovels.ai/cli](https://shovels.ai/cli)
### 🏗️ Permits Dataset
**New Permits Discovered**
* **1.8M** Records
* Expanding historical and current coverage across all jurisdictions
* Permits filed in February 2026: **156K**
* Permits filed year-to-date (2026): **462K**
**🧱 Permit Activity by Category**
* Electrical Permits: **+388K** - Largest category growth this release
* New construction permits: **+203K**
* HVAC permits: **+136K**
* Remodel permits: **+136K**
* Roofing permits: **+91K**
* Plumbing permits: **+71K**
* Solar permits: **+23K**
* Demolition permits: **+19K**
* ADU permits: **+12K**
* EV charger permits: **+2.2K**
### 📍 Geocoding Improvements
**Additional Permits Geocoded**
* **+1.4M** Records
* Now with latitude and longitude coordinates for spatial analysis
### Introduction
Big month. We're launching Shovels CLI — a new way to query permits, contractors, and addresses from the command line (or let your AI agent do it). The API gained result counts on every paginated endpoint, an improved usage dashboard, and more accurate contractor metrics. On the data side, we added 1.8M new permits and geocoded another 1.4M records.
### ✨ New
**>\_ Introducing Shovels CLI**
Your AI agent's gateway to U.S. construction data
One binary. Zero config. Pure JSON.
Search permits, query contractors, resolve addresses — all from your terminal
**Install in seconds**
```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl -LsSf https://shovels.ai/install.sh | sh
```
**Built for automation**
* JSON-only output to stdout, errors to stderr
* Auto-pagination with `--limit all`
* Auto-retry with backoff on rate limits
* Help text optimized for LLM comprehension
Works on macOS, Linux, and Windows. Learn more at [shovels.ai/cli](https://shovels.ai/cli)
### ⚡ API Enhancements
**Result Counts on All Paginated Endpoints**
* **7** Endpoints · `include_count=true`
* Get total result counts before paginating
* Returns `{value, relation}` shape — exact count or "10,000+" for large sets
Supported on:
* `/v2/permits/search`
* `/v2/contractors/search`
* `/v2/contractors/{id}/permits`
* All **4** `geo_permits` endpoints
**Enhanced Usage Dashboard**
`GET /v2/usage` now includes:
* Daily breakdown
* `is_over_limit` flag
* `available_at` projection so you always know your credit status
* Over-limit users can still access usage data
**Improved Contractor Metrics Accuracy**
Fixed `permit_count` and `total_job_value` inflation for `tag=all` queries. Average metrics now use permit-count-weighted calculations for mathematical precision.
### 🏗️ Permits Dataset
**New Permits Discovered**
* **1.8M** Records
* Expanding historical and current coverage across all jurisdictions
* Permits filed in February 2026: **156K**
* Permits filed year-to-date (2026): **462K**
**🧱 Permit Activity by Category**
* Electrical Permits: **+388K** - Largest category growth this release
* New construction permits: **+203K**
* HVAC permits: **+136K**
* Remodel permits: **+136K**
* Roofing permits: **+91K**
* Plumbing permits: **+71K**
* Solar permits: **+23K**
* Demolition permits: **+19K**
* ADU permits: **+12K**
* EV charger permits: **+2.2K**
### 📍 Geocoding Improvements
**Additional Permits Geocoded**
* **+1.4M** Records
* Now with latitude and longitude coordinates for spatial analysis
✅ **No breaking changes.** All existing integrations continue to work unchanged.
### Introduction
We added 1.8M new permits and geocoded another 1.4M records this month. Here's the full breakdown.
### 🏗️ Permits Dataset
**New Permits Discovered**
* **1.8M** Records
* Expanding historical and current coverage across all jurisdictions
* Permits filed in February 2026: **156K**
* Permits filed year-to-date (2026): **462K**
**🧱 Permit Activity by Category**
* Electrical Permits: **+388K** - Largest category growth this release
* New construction permits: **+203K**
* HVAC permits: **+136K**
* Remodel permits: **+136K**
* Roofing permits: **+91K**
* Plumbing permits: **+71K**
* Solar permits: **+23K**
* Demolition permits: **+19K**
* ADU permits: **+12K**
* EV charger permits: **+2.2K**
### 📍 Geocoding Improvements
**Additional Permits Geocoded**
* **+1.4M** Records
* Now with latitude and longitude coordinates for spatial analysis
### Introduction
We're kicking off February with 6.1M new permits, 142K new contractors, and 5.2M additional geocoded records. Here's the full breakdown.
### 🚀 What's New
### 🏗️ Permits Dataset
New Permits Scraped
* **6.1M** records
* Expanding historical and current coverage across all jurisdictions
* Permits filed in January 2026: **197K**
🧱 Permit Activity by Category
* Electrical Permits: **+911K** - Largest category growth this release
* New construction permits: **+803K**
* HVAC permits: **+620K**
* Remodel permits: **+549K**
* Plumbing permits: **+447K**
* Roofing permits: **+268K**
* Solar permits: **+234K**
* Demolition permits: **+120K**
* ADU permits: **+100K**
* EV charger permits: **+9K**
### 📍 Geocoding Improvements
Additional Permits Geocoded
* **+5.2M** records
* Now with latitude and longitude coordinates
### 👷 Contractor Intelligence
New Contractors Added
* **+142K** records
* Expanding the contractor database
### ⚠️ ID Updates
**5M** IDs Updated
IDs were updated to correct issues arising from data collected across multiple platforms, as well as changes in administrative jurisdictions.
* Most affected areas: Oregon, Douglas County, Omaha
For any missing IDs, you can still match using permit number or address fields. Or reach out for assistance.
### 🤖 Charlie - Your AI Research Agent
What is Charlie?
Charlie is your AI research agent for building permits and contractors. Ask questions in plain English—no SQL, no filters—and get answers instantly.
January 2026 Updates:
* Automatic error recovery – Charlie now automatically reconnects when the backend restarts. No more persistent errors requiring page refreshes.
* Clickable profile links – When Charlie returns contractor or permit results, you now get direct links to their profile pages.
* Improved county queries – Fixed an issue where county-based queries were failing due to formatting mismatches.
Try it out – Visit [charlie.shovels.ai](https://charlie.shovels.ai) to start asking questions.
### Introduction
We're kicking off February with 6.1M new permits, 142K new contractors, and 5.2M additional geocoded records. Here's the full breakdown.
### 🚀 What's New
### 🏗️ Permits Dataset
New Permits Scraped
* **6.1M** records
* Expanding historical and current coverage across all jurisdictions
* Permits filed in January 2026: **197K**
🧱 Permit Activity by Category
* Electrical Permits: **+911K** - Largest category growth this release
* New construction permits: **+803K**
* HVAC permits: **+620K**
* Remodel permits: **+549K**
* Plumbing permits: **+447K**
* Roofing permits: **+268K**
* Solar permits: **+234K**
* Demolition permits: **+120K**
* ADU permits: **+100K**
* EV charger permits: **+9K**
### 📍 Geocoding Improvements
Additional Permits Geocoded
* **+5.2M** records
* Now with latitude and longitude coordinates
### 👷 Contractor Intelligence
New Contractors Added
* **+142K** records
* Expanding the contractor database
### ⚠️ ID Updates
**5M** IDs Updated
IDs were updated to correct issues arising from data collected across multiple platforms, as well as changes in administrative jurisdictions.
* Most affected areas: Oregon, Douglas County, Omaha
For any missing IDs, you can still match using permit number or address fields. Or reach out for assistance.
✅ **No breaking changes.** All existing integrations continue to work unchanged.
### Introduction
We're kicking off February with 6.1M new permits, 142K new contractors, and 5.2M additional geocoded records. Here's the full breakdown.
### 🚀 What's New
### 🏗️ Permits Dataset
New Permits Scraped
* **6.1M** records
* Expanding historical and current coverage across all jurisdictions
* Permits filed in January 2026: **197K**
🧱 Permit Activity by Category
* Electrical Permits: **+911K** - Largest category growth this release
* New construction permits: **+803K**
* HVAC permits: **+620K**
* Remodel permits: **+549K**
* Plumbing permits: **+447K**
* Roofing permits: **+268K**
* Solar permits: **+234K**
* Demolition permits: **+120K**
* ADU permits: **+100K**
* EV charger permits: **+9K**
### 📍 Geocoding Improvements
Additional Permits Geocoded
* **+5.2M** records
* Now with latitude and longitude coordinates
### 👷 Contractor Intelligence
New Contractors Added
* **+142K** records
* Expanding the contractor database
### ⚠️ ID Updates
**5M** IDs Updated
IDs were updated to correct issues arising from data collected across multiple platforms, as well as changes in administrative jurisdictions.
* Most affected areas: Oregon, Douglas County, Omaha
For any missing IDs, you can still match using permit number or address fields. Or reach out for assistance.
### Introduction
Happy New Year! We're starting 2026 with 5.5M new permits, 4.7M additional geocoded records, and 171K new contractors. On the API side, we've added negative query filters and dynamic tallies, plus performance improvements for contractor and text search. Here's everything new this month.
### 🚀 What's New
### 🏗️ Permits Dataset
**New Permits Added**
* **5.5M Records**
* Expanding historical and current coverage across all jurisdictions
* Permits filed in December 2025: **295K**
**🧱 Permit Activity by Category**
* **Electrical Permits:** **+1.3M** - Largest category growth this release
* Plumbing permits: **+803K**
* HVAC permits: **+569K**
* New construction permits: **+460K**
* Remodel permits: **+351K**
* Roofing permits: **+267K**
* Solar permits: **+131K**
* ADU permits: **+82K**
* Demolition permits: **+65K**
* EV charger permits: **+18K**
### 📍 Geocoding Improvements
Additional Permits Geocoded
* **+4.7M Records**
* Now with latitude and longitude coordinates
* Total geocoded permits: **143.9M**
This release significantly improves spatial completeness for downstream mapping and location-based analysis.
### 👷 Contractor Intelligence
**New Contractors Added**
* **+171K Records**
* Total now: **3.6M contractors** in our database
* Additional contractor licenses captured: **+256K**
* Additional primary phone numbers available: **+69K**
* Additional primary email addresses available: **+40K**
### Introduction
Happy New Year! We're starting 2026 with 5.5M new permits, 4.7M additional geocoded records, and 171K new contractors. On the API side, we've added negative query filters and dynamic tallies, plus performance improvements for contractor and text search. Here's everything new this month.
### 🚀 What's New
### ⚡ API: New Features
**Negative Query Filters**
Exclude specific tags and classifications using a dash prefix:
```
# Exclude HVAC permits
permit_tags=-hvac
# Mixed: solar permits excluding roofing
permit_tags=solar&permit_tags=-roofing
```
**Note:** Pure negative queries at state-level are limited to 365 days. Add a positive filter or use a more specific geo\_id to expand the range.
**Dynamic Tallies in Contractor Search**
Use `include_tallies=true` to get `tag_tally` and `status_tally` per contractor. Tallies are scoped to your search filters (date range, geo, tags).
**Clarified Contractor Search Semantics**
Filters work independently: `permit_tags` for capability/expertise, `permit_from/to` for activity period, and `tag_tally` for work breakdown within that period.
### 🔧 API: Performance & Bug Fixes
**Performance improvements**
* Contractor search and full-text search (`permit_q`) are now significantly faster, especially for broad geo queries
* Text search also supports English stemming, so "installing" matches "install"
**Invalid date range handling**
* Now returns 422 with a clear message instead of 500 Internal Server Error
**Contractor search pagination**
* Fixed an issue where pagination could prematurely signal end of results
✅ **No breaking changes.** All existing integrations continue to work unchanged.
### 🏗️ Permits Dataset
**New Permits Added**
* **5.5M Records**
* Expanding historical and current coverage across all jurisdictions
* **Permits filed in December 2025:** **295K**
**🧱 Permit Activity by Category**
* **Electrical Permits:** **+1.3M** - Largest category growth this release
* **Plumbing permits:** **+803K**
* **HVAC permits:** **+569K**
* **New construction permits:** **+460K**
* **Remodel permits:** **+351K**
* **Roofing permits:** **+267K**
* **Solar permits:** **+131K**
* **ADU permits:** **+82K**
* **Demolition permits:** **+65K**
* **EV charger permits:** **+18K**
### 📍 Geocoding Improvements
**Additional Permits Geocoded**
* **+4.7M Records**
* Now with latitude and longitude coordinates
* **Total geocoded permits:** **143.9M**
This release significantly improves spatial completeness for downstream mapping and location-based analysis.
### 👷 Contractor Intelligence
**New Contractors Added**
* **+171K Records**
* **Total now:** **3.6M contractors** in our database
* **Additional contractor licenses captured:** **+256K**
* **Additional primary phone numbers available:** **+69K**
* **Additional primary email addresses available:** **+40K**
### Introduction
Happy New Year! We're starting 2026 with 5.5M new permits, 4.7M additional geocoded records, and 171K new contractors. On the API side, we've added negative query filters and dynamic tallies, plus performance improvements for contractor and text search. Here's everything new this month.
### 🚀 What's New
### 🏗️ Permits Dataset
**New Permits Added**
* **5.5M Records**
* Expanding historical and current coverage across all jurisdictions
* **Permits filed in December 2025:** **295K**
**🧱 Permit Activity by Category**
* **Electrical Permits:** **+1.3M** - Largest category growth this release
* **Plumbing permits:** **+803K**
* **HVAC permits:** **+569K**
* **New construction permits:** **+460K**
* **Remodel permits:** **+351K**
* **Roofing permits:** **+267K**
* **Solar permits:** **+131K**
* **ADU permits:** **+82K**
* **Demolition permits:** **+65K**
* **EV charger permits:** **+18K**
### 📍 Geocoding Improvements
**Additional Permits Geocoded**
* **+4.7M Records**
* Now with latitude and longitude coordinates
* **Total geocoded permits:** **143.9M**
This release significantly improves spatial completeness for downstream mapping and location-based analysis.
### 👷 Contractor Intelligence
**New Contractors Added**
* **+171K Records**
* **Total now:** **3.6M contractors** in our database
* **Additional contractor licenses captured:** **+256K**
* **Additional primary phone numbers available:** **+69K**
* **Additional primary email addresses available:** **+40K**
### Introduction
This month brings substantial improvements across data quality, coverage, and enrichment. We've significantly expanded our permits dataset with better field coverage, added 160 new jurisdictions, and continued strengthening our contractor intelligence with enhanced parsing and enrichment.
### 🚀 What's New
### 🏗️ Permits Dataset
**Historical Permits Added**
* **6.6M** records
* Expanding historical coverage across all jurisdictions
* Permits filed in November 2025: **222K**
**✨ Data Quality Enhancements**
**Fee Information Expanded**
* **14M** more records
* Better financial insights for market analysis
* Additional permits with job values: **10M**
* Additional records with issue dates: **13M**
* Improved description, type, and subtype coverage
* Enhanced permit classification and categorization across the dataset
### 📍 Coverage Expansion
**New Jurisdictions Added**
* **160** jurisdictions
* Expanding our nationwide coverage footprint
* APN (Assessor's Parcel Number) field added to **40M** permits
* Enabling better property linkage and parcel-level analysis
### 👷 Contractor Intelligence
**Dataset Expansion**
* **63K** new contractors - expanding your universe of potential partners
* Additional addresses parsed: **50K**
* Additional phone numbers available: **40K**
* Additional license issue dates captured: **7K**
* Enhanced geographic coverage
* Expanded city, street, and ZIP code coverage across all states
### Introduction
This month brings substantial improvements across data quality, coverage, and enrichment. We've significantly expanded our permits dataset with better field coverage, added 160 new jurisdictions, and continued strengthening our contractor intelligence with enhanced parsing and enrichment.
### 🚀 What's New
### 🏗️ Permits Dataset
**Historical Permits Added**
* **6.6M Records**
* Expanding historical coverage across all jurisdictions
* **Permits filed in November 2025:** **222K**
**✨ Data Quality Enhancements**
**Fee Information Expanded**
* **14M More Records**
* Better financial insights for market analysis
* **Additional permits with job values:** **10M**
* **Additional records with issue dates:** **13M**
* Improved description, type, and subtype coverage
* Enhanced permit classification and categorization across the dataset
### 📍 Coverage Expansion
**New Jurisdictions Added**
* **160 Jurisdictions**
* Expanding our nationwide coverage footprint
* **APN (Assessor's Parcel Number) field added to 40M permits**
* Enabling better property linkage and parcel-level analysis
### 👷 Contractor Intelligence
**Dataset Expansion**
* **63K New Contractors** - Expanding your universe of potential partners
* **Additional addresses parsed:** **50K**
* **Additional phone numbers available:** **40K**
* **Additional license issue dates captured:** **7K**
* **Enhanced geographic coverage**
* Expanded city, street, and ZIP code coverage across all states
### Introduction
This month brings substantial improvements across data quality, coverage, and enrichment. We've significantly expanded our permits dataset with better field coverage, added 160 new jurisdictions, and continued strengthening our contractor intelligence with enhanced parsing and enrichment.
### 🚀 What's New
### 🏗️ Permits Dataset
**Historical Permits Added**
* **6.6M Records**
* Expanding historical coverage across all jurisdictions
* **Permits filed in November 2025:** **222K**
**✨ Data Quality Enhancements**
**Fee Information Expanded**
* **14M More Records**
* Better financial insights for market analysis
* **Additional permits with job values:** **10M**
* **Additional records with issue dates:** **13M**
* Improved description, type, and subtype coverage
* Enhanced permit classification and categorization across the dataset
### 📍 Coverage Expansion
**New Jurisdictions Added**
* **160 Jurisdictions**
* Expanding our nationwide coverage footprint
* **APN (Assessor's Parcel Number) field added to 40M permits**
* Enabling better property linkage and parcel-level analysis
### 👷 Contractor Intelligence
**Dataset Expansion**
* **63K New Contractors** - Expanding your universe of potential partners
* **Additional addresses parsed:** **50K**
* **Additional phone numbers available:** **40K**
* **Additional license issue dates captured:** **7K**
* **Enhanced geographic coverage**
* Expanded city, street, and ZIP code coverage across all states
### Introduction
This month's release brings significant intelligence improvements across our platform. We've enhanced our data classification capabilities, expanded geographic coverage, and continued growing both our permits and contractor datasets with better enrichment quality.
### 🚀 What's New
**🧠 Data Intelligence Breakthrough**
**Property Type Classification Enhanced**
* **20M+ Permits Reclassified**
* **4M additional addresses** now correctly labeled as commercial or residential
Our enhanced data pipeline now analyzes permit descriptions—the most reliable source of regulatory context—to automatically infer property types. This means more accurate market segmentation and better targeting capabilities for your use cases.
### 🏗️ Permits Dataset
* **Permits filed in October 2025:** **180K**
* **Historical permits added to the database:** **3.4M**
### 🗺️ Geographic Intelligence
* Enhanced FIPS code coverage across all permits
* Simplifying geographic analysis and enabling more accurate county-level aggregation for your research and targeting needs
### 👷 Contractor Intelligence
**Dataset Expansion**
* **66K New Contractors** - Expanding your universe of potential partners with cleaner, better-deduplicated data
* **Phone number coverage increased:** +3.6%
* **Email address coverage increased:** +4.5%
* **License field coverage increased:** +10.6%
### Introduction
This month's release brings significant intelligence improvements across our platform. We've enhanced our data classification capabilities, expanded geographic coverage, and continued growing both our permits and contractor datasets with better enrichment quality.
### 🚀 What's New
**🔌 API Improvements**
**New Contractor Classification Parameter**
* `contractor_classification_derived` - Human-readable categories with nationwide coverage
We've introduced a new query parameter that replaces state-specific license codes with standardized, human-readable categories like "electrical", "hvac", and "plumbing". This works consistently across all states, making it easier to filter and discover contractors without domain expertise.
### ⚠️ Deprecation Notice
**Legacy `contractor_classifications` parameter status:** **Deprecated**
The old parameter continues to work for backward compatibility, but we recommend migrating to the new standardized taxonomy for better user experience and broader coverage.
### 🏗️ Permits Dataset
* **Permits filed in October 2025:** **180K**
* **Historical permits added to the database:** **3.4M**
### 👷 Contractor Intelligence
**Dataset Expansion**
* **66K New Contractors** - Expanding your universe of potential partners with cleaner, better-deduplicated data
* **Phone number coverage increased:** +3.6%
* **Email address coverage increased:** +4.5%
* **License field coverage increased:** +10.6%
### Introduction
This month's release brings significant intelligence improvements across our platform. We've enhanced our data classification capabilities, expanded geographic coverage, and continued growing both our permits and contractor datasets with better enrichment quality.
### 🚀 What's New
**🧠 Data Intelligence Breakthrough**
**Property Type Classification Enhanced**
* **20M+ Permits Reclassified**
* **4M additional addresses** now correctly labeled as commercial or residential
Our enhanced data pipeline now analyzes permit descriptions—the most reliable source of regulatory context—to automatically infer property types. This means more accurate market segmentation and better targeting capabilities for your use cases.
### 🏗️ Permits Dataset
* **Permits filed in October 2025:** **180K**
* **Historical permits added to the database:** **3.4M**
### 🗺️ Geographic Intelligence
* Enhanced FIPS code coverage across all permits
* Simplifying geographic analysis and enabling more accurate county-level aggregation for your research and targeting needs
### 👷 Contractor Intelligence
**Dataset Expansion**
* **66K New Contractors** - Expanding your universe of potential partners with cleaner, better-deduplicated data
* **Phone number coverage increased:** +3.6%
* **Email address coverage increased:** +4.5%
* **License field coverage increased:** +10.6%
### Introduction
This month marks a pivotal moment in our mission: we've begun bridging the gap between online and offline permit data. For the first time, we're bringing dark data into the light, accessing jurisdictions that have never made their records digitally available. Combined with significant growth across our platform, October's release represents our most comprehensive coverage to date.
### 🚀 What's New
**Bridging the Digital Divide: Offline Jurisdictions Brought Online**
* **500K+ Records**
* First-ever offline permit acquisition from **Cook County, IL** and **Oak Ridge North, TX**
Many jurisdictions still operate without public-facing digital portals. We're now bringing this offline data online, giving you comprehensive coverage without the manual work. This is just the beginning of connecting America's fragmented permit landscape into one unified, accessible platform.
### 📊 Data Growth
**Total Records**
* **185M** (+10M new permit records added - historical and newly filed)
* **Permits filed in September:** 80K
* **Permits filed in 2025 year-to-date:** 4.5M
### 👷 Contractor Intelligence
**New contractors added to our database**
* **190K contractors** - Expanding your universe of potential partners and competitors
* **New license records:** 70K
* **New contractor phone numbers:** 200K+
* **New contractor email addresses:** 100K+
### Introduction
This month marks a pivotal moment in our mission: we've begun bridging the gap between online and offline permit data. For the first time, we're bringing dark data into the light, accessing jurisdictions that have never made their records digitally available. Combined with significant growth across our platform, October's release represents our most comprehensive coverage to date.
### 🚀 What's New
**Bridging the Digital Divide: Offline Jurisdictions Brought Online**
* **500K+ Records**
* First-ever offline permit acquisition from **Cook County, IL** and **Oak Ridge North, TX**
Many jurisdictions still operate without public-facing digital portals. We're now bringing this offline data online, giving you comprehensive coverage without the manual work. This is just the beginning of connecting America's fragmented permit landscape into one unified, accessible platform.
### 📊 Data Growth
**Total Records**
* **185M** (+10M new permit records added - historical and newly filed)
* **Permits filed in September:** 80K
* **Permits filed in 2025 year-to-date:** 4.5M
### 👷 Contractor Intelligence
**New contractors added to our database**
* **190K contractors** - Expanding your universe of potential partners and competitors
* **New license records:** 70K
* **New contractor phone numbers:** 200K+
* **New contractor email addresses:** 100K+
### Introduction
This month marks a pivotal moment in our mission: we've begun bridging the gap between online and offline permit data. For the first time, we're bringing dark data into the light, accessing jurisdictions that have never made their records digitally available. Combined with significant growth across our platform, October's release represents our most comprehensive coverage to date.
### 🚀 What's New
**Bridging the Digital Divide: Offline Jurisdictions Brought Online**
* **500K+ Records**
* First-ever offline permit acquisition from **Cook County, IL** and **Oak Ridge North, TX**
Many jurisdictions still operate without public-facing digital portals. We're now bringing this offline data online, giving you comprehensive coverage without the manual work. This is just the beginning of connecting America's fragmented permit landscape into one unified, accessible platform.
### 📊 Data Growth
**Total Records**
* **185M** (+10M new permit records added - historical and newly filed)
* **Permits filed in September:** 80K
* **Permits filed in 2025 year-to-date:** 4.5M
### 👷 Contractor Intelligence
**New contractors added to our database**
* **190K contractors** - Expanding your universe of potential partners and competitors
* **New license records:** 70K
* **New contractor phone numbers:** 200K+
* **New contractor email addresses:** 100K+
### ✨ New
* **Total Records:** **174 million** (+700K from last month)
* **New Permits from August:** **162,281** - Our biggest monthly addition to date!
* **Permits Transitioned to Finalized Status:** **560K**
* **New Address Points:** **450K** with precise geo-coordinate mapping
* **New Jurisdictions in Coverage:** **119**
### 🚀 Upgrades
* **Enhanced Data Collection Methods:** Improved fill rates across all data points
* **Job Value Entries:** **2.3M+ new entries** added across permits for better cost estimation
* **New Construction Permits:** **55,000**
* **New HVAC Permits:** **124,000**
### 👷 Contractor Data Enhancements
* **New Contractors Added:** **13,000**
* **New License Records:** **70,000**
* **New Phone Numbers:** **8,000**
* **New Email Addresses:** **30,000**
### ✨ New
* **Total Records:** **174 million** (+700K from last month)
* **New Permits from August:** **162,281** - Our biggest monthly addition to date!
* **Permits Transitioned to Finalized Status:** **560K**
* **New Address Points:** **450K** with precise geo-coordinate mapping
* **New Jurisdictions in Coverage:** **119**
### 🚀 Upgrades
* **Enhanced Data Collection Methods:** Improved fill rates across all data points
* **Job Value Entries:** **2.3M+ new entries** added across permits for better cost estimation
* **New Construction Permits:** **55,000**
* **New HVAC Permits:** **124,000**
### 👷 Contractor Data Enhancements
* **New Contractors Added:** **13,000**
* **New License Records:** **70,000**
* **New Phone Numbers:** **8,000**
* **New Email Addresses:** **30,000**
### ✨ New Permit Records
* **Total Records:** **174 million** (+700K from last month)
* **New Permits from August:** **162,281** - Our biggest monthly addition to date!
* **Permits Transitioned to Finalized Status:** **560K**
* **New Address Points:** **450K** with precise geo-coordinate mapping
### 🚀 Expanded Jurisdiction Coverage
* **New Jurisdictions Added:** **119** new jurisdictions in our coverage
### 🛠 Data Quality Improvements
* **Enhanced Data Collection Methods:** Improved fill rates across all data points
* **Job Value Entries:** **2.3M+ new entries** added across permits for better cost estimation
* **New Construction Permits:** **55,000**
* **New HVAC Permits:** **124,000**
### 👷 Contractor Data Enhancements
* **New Contractors Added:** **13,000**
* **New License Records:** **70,000**
* **New Phone Numbers:** **8,000** added to `contractors.phone_primary`
* **New Email Addresses:** **30,000**
### ✨ New
* **Total new permits added:** Over **3 million** across all U.S. states.
* **California:** 2 million of these permits were added, making it our largest single-state update.
* **2025 permit count-to-date:** 2.9 million (through July 15, 2025).
* **Permits filed since June 1:** Over 250,000, with **22,000 filed in July** so far.
* **Construction:** 130,000
* **Solar:** 125,000
* **EV chargers:** 10,000
* **New contractor phone numbers:** Over **35,000** new primary phone numbers have been added.
### 🚀 Upgrades
* Continued improving contractor classification standardization. As a result, more contractors now have a Shovels-standard classification assigned to the `license_class` field.
### ✨ New
* **Total new permits added:** Over **3 million** across all U.S. states.
* **California:** 2 million of these permits were added, making it our largest single-state update.
* **2025 permit count-to-date:** 2.9 million (through July 15, 2025).
* **Permits filed since June 1:** Over 250,000, with **22,000 filed in July** so far.
* **Construction:** 130,000
* **Solar:** 125,000
* **EV chargers:** 10,000
* **New contractor phone numbers:** Over **35,000** new primary phone numbers have been added.
### 🚀 Upgrades
* Continued improving contractor classification standardization. As a result, more contractors now have a Shovels-standard classification assigned to the `license_class` field.
### ‼️ Action Required
**Token-based pagination is fully live.** Page-based pagination **will be removed on August 1, 2025**. Update any calls that still rely on the deprecated `page` parameter.
### ✨ New
* **Total new permits added:** Over **3 million** across all U.S. states.
* **California:** 2 million of these permits were added, making it our largest single-state update.
* **2025 permit count-to-date:** 2.9 million (through July 15, 2025).
* **Permits filed since June 1:** Over 250,000, with **22,000 filed in July** so far.
* **Construction:** 130,000
* **Solar:** 125,000
* **EV chargers:** 10,000
* **New contractor phone numbers:** Over **35,000** new primary phone numbers have been added to `contractors.phone_primary`.
### 🚀 Upgrades
* Continued improving contractor classification standardization. As a result, more contractors now have a Shovels-standard classification assigned to the `license_class` field.
### 🚀 Upgrades:
* Minor improvements to the UX and ongoing speed enhancements.
### 🐞 Bug Fixes:
* Bug fixes
### ⚠️ Final Reminder:
Token-based pagination has been operational for the past two months. **Since page-based pagination will be discontinued at the start of August, this is your final month to update your integrations.**
### ✨ New Permit Records:
* **Total permit records:** 171,752,486
*That's up from 170,626,720, a jump of over **1.1 million**!*
* **June additions:** Over **25,000 new records** from June alone.
* **New construction permits:**
* **150,000+** new construction records
* **50,000** of which are **solar projects**
### 🚀 Expanded Jurisdiction Coverage:
We've added **dozens of new jurisdictions,** some of which are:
* **Livingston Parish, LA** – 6,951 new permits
* **Rapides Parish, LA** – 4,454 new permits
* **Dallas County, TX** – 706 new permits
* **Tequesta, FL** – 320 new permits
### 🧑🏼🔧 Contractor Data Enhancements:
* **14,000** new contractor records added
* Nearly **20,000** new primary phone numbers
* Over **20,000** new primary email addresses
### 🚀 Upgrades:
* Faster pagination, especially for deeper pages.
* More jurisdiction coverage.
### ✨ Reminder:
* Added cursor-based pagination for **all** endpoints, which substantially improves deep search pagination.
This replaces the former `page` parameter for pagination. This old method will be supported for an additional three months, at which point it will be deprecated.
### ✨ New:
* Added **1.2 million permits**, including **248,000 from May** alone, significantly boosted by our new infrastructure.
* **300 new jurisdictions** are now online, increasing our nationwide coverage.
* **70,000 new contractors** added, bringing the total to **2.96 million**.
* Enhanced deduplication logic eliminated duplicate contractor records, improving data quality.
* **40,000 additional contact details** (phone numbers, emails, addresses) were linked, thanks to infrastructure enhancements.
⚠️ **Breaking Changes & Compatibility Notes**
### ‼️ Permit ID Updates:
* Approximately **2 million duplicate permits** from legacy system migrations were removed. The most affected states are **Florida and Texas.**
* **\~6% of permits now have new IDs.** Attributes such as `permit_number` and address remain unchanged.
If you query by cached `id` and encounter missing results (404), use `permit_number`, address, or other metadata to re-fetch the permit. If you still encounter issues, reach out—we’ll help map old IDs to new ones.
### ‼️ Contractor ID Updates
* **\~170,000 contractors have new IDs** that have been regenerated based on improved data quality.
* All contractors remain searchable by:
* Areas of work
* Permit projects
* Business names
If you cache contractor IDs on your end and you encounter not-found responses, use other record attributes to update IDs on your end. If ID changes affect your workflow in a more substantial way, contact us directly and we’ll assist.
### 🚀 Upgrades:
* Improved the **Permit Search** logic to always order resulting permits by newest first.
### ✨ New:
* Added cursor-based pagination for **all** endpoints, which substantially improves deep search pagination.
This replaces the former `page` parameter for pagination. This old method will be supported for an additional three months, at which point it will be deprecated.
### 🚀 Upgrades:
* Improved the response ordering logic to always order results in chronological order, descending.
### ✨ New:
* Added a new `apn` column to the `addresses` table
* Added two new columns `status` and `status_detailed` to the `csl` and `contractors` tables
* Status codes for all states are mapped against the following schema:
* `active`: License is active and in good standing with no restrictions.
* `active_conditional`: License is active but subject to specific conditions, limitations, or temporary provisions.
* `expired`: License has lapsed due to non-renewal; typically eligible for renewal.
* `retired`: License has been voluntarily placed in an inactive state, often due to the holder retiring or the business closing.
* `suspended`: License is temporarily invalid, usually due to disciplinary actions or unresolved administrative issues.
* `revoked`: License has been permanently terminated, typically due to serious violations or disciplinary reasons.
* `pending`: License application, renewal, or reinstatement is currently under review or being processed.
* `inactive`: License is currently not valid, often due to administrative issues or other undefined reasons not captured by the categories above.
### 🚀 Upgrades:
* Expanded `csl` table coverage by adding 11 new states: `CT`, `GA`, `IL`, `MD`, `MO`, `OK`, `NY`, `OR`, `SC`, `TN`, `UT`.
### 🐞 Bugfix:
* Fixed an address parsing issue that was affecting `csl` table contractors in `NV`.
### ✨ New:
* General speed improvements
* New and improved onboarding flow
### 🪲 Bug Fixes:
* Fixed bug that didn't change the geo on the download list button
* Fixed bug preventing some filters from being "sticky" in between searches
* Fixed bug preventing geography profile filters from updating charts
No API changes this month!
### 🏗️ Permits:
* Total permits: The dataset now contains 171M permits — an increase of 2M compared to the previous batch. With 8 new jurisdictions.
* `first_seen_date` coverage: \~150M permits have a first\_seen\_date
### 🧑🏼🔧 Contractors:
* We now have over 3 million contractors in the dataset, with \~50K new records added this month
* License linkage: An additional 80K contractors have been linked to state license files.
### 📇 Classification coverage:
* All contractors with a license now have the `classification_derived` field as well. This field maps state-specific classifications into standardized categories.
* If a contractor has multiple license classifications, they are concatenated using the pipe ("|") character.
### 🗂️ License Files:
* A new table, CSL, is added to our production database. It contains data from 26 states files, and has a total of 1.8 M contractors.
* Around 200K contractors are linked to our Contractors table while the others are new. As part of this new dataset, we have around 700K new contractors phone numbers and 200K new emails.
There were in total almost 3000 different classifications categories across different state files. We standardized this to the following categories:
`concrete_and_paving`, `demolition_and_excavation`, `electrical`, `fencing_and_glazing`, `framing_and_carpentry`, `general_building_contractor`, `general_engineering_contractor`, `hvac`, `landscaping_and_outdoor_work`, `plumbing`, `roofing`, `specialty_trades`
### ✨ New:
* Added **Website Search** filter options to **Contractor Filters**
* Added tooltips to the UI to better explain functionality
### 🚀 Upgrades:
* Added Search filter auto-caching to improve UX between page reloads and sessions
* Added new **New User** onboarding experience
* Adjusted datepicker widget for date range selection to *monthly*, which will improve usability
### ✨ New:
* Added new fields to the `/contractors/` endpoint group:
* `first_seen_date`: Date when the contractor was first recorded in the Shovels platform and given an id.
* `license_act_date`: Date when the contractor's license became active.
* `license_inact_date`: Date when the contractor's license became inactive.
* `review_count`: Number of reviews the contractor has received.
* `rating`: Rating of the contractor based on reviews.
* `dba`: "Doing Business As" name for the contractor.
* `sic`: Standard Industrial Classification (SIC) code of the contractor.
* `naics`: North American Industry Classification System (NAICS) code of the contractor.
* `linkedin_url`: LinkedIn URL of the contractor.
* `revenue`: Annual revenue of the contractor's business.
* `employee_count`: Number of employees working for the contractor.
* `primary_industry`: Primary industry in which the contractor operates.
* Implemented new API rate limits: **1M requests/month**, or **\~33k requests/day**.
### ✨ New:
* Added `first_seen_date` to the `permits` table
* this represents the date when the the permit itself was first retrieved for the Shovels platform
This is different than the `first_seen_date` column in the `contractors` table, which by contrast represents the date the contractor was first seen in our platform and assigned an `id`.
FINAL WARNING: As previously announced, the V1 of the Shovels API is **now deprecated**.
### ✨ New:
* Added **Address Profiles**
* Added **Free Forever** plan in place of a Free Trial.
### 🐞 Bugfix:
* Fixed issue where **Geography Profiles** (`City`, `Jurisdiction`, `County`, etc) filters for **Property Type** and **Permit Category** weren't updating counts correctly.
### ✨ New:
* Added new `/v2/{geography}/{geo_id}/metrics/monthly` endpoint.
* Added new `/v2/{geography}/{geo_id}/metrics/current` endpoint.
### 💥 Breaking:
* Removed `/v2/{geography}/{geo_id}/metrics` endpoint.
* Changed **monetary value** data types from `decimal` `integer` (eg, `dollars` `cents`). This will affect the following parameters:
* `property_assess_market_value`
* `job_value`
* `fees`
* `avg_job_value`
* `total_job_value`
* `avg_job_value`
* `total_job_value`
* `total_job_value`
* Changed **percentage** data types from `decimal` `integer` (eg, `0.75` `75`). This will affect the following parameters:
* `inspection_pass_rate`
* `avg_inspection_pass_rate`
* `avg_inspection_pass_rate`
* `avg_inspection_pass_rate`
### 🐞 Bugfix:
* Fixed issue where `address` object's latitude and longitude coordinates were occasionally reversed, and built in anomaly detection and correction for future processing.
* This fix continues downstream to **API** and **Online** records.
***
In order to ensure smooth transitions between updates, we've settled on a few short term policies.
1. We will do our best to give as much advance warning on breaking changes as possible.
* Some recent breaking changes are delayed updates to the schema that should have been part of the V2 launch. **We do not plan on making breaking changes this often.**
2. Where sensible and possible, we will provide backward compatibility for new endpoints, parameters, and schemas.
As described under **EDL** changes above, we found that there were rare cases where the latitude and longitude coordinates were reversed. Affected records have been corrected.
We've also beefed up our address-geospatial processing to find and fix these errors going forward.
* **Online**: Monday, February 03, 2025.
* **API**: Monday, February 10, 2025.
* **EDL**: Wednesday, February 6, 2025.
### ✨ New:
* Added `employees` to **Contractor Profiles**.
* Added links to **Jurisdiction Profiles** for all permits.
* Added links to **City Profiles** and **County Profiles** for all geo-related profiles.
### 🐞 Bugfixes:
* Fixed password reset bug.
### ✨ New:
* Added new `/v2/contractors/{id}/employees` endpoint.
* Added new `/v2/addresses/{id}/metrics` endpoint.
### ⚠️ End of Life:
* We will **DEPRECATE** the **API V1** by the end of January 2025.
Please update all endpoints using the old schema **before January 31, 2025** to ensure continued usage.
### 🐞 Bugfixes:
* Fixed metrics calculation algorithm on all endpoints.
### 💥 Breaking:
* `contractors` table:
* `first_seen_at` **renamed** `first_seen_date`.
* Data type **changed** `DATE`.
* `reviews` **renamed** `review_count`.
* `residents` table:
* Foreign key **changed** from `permit_id` `address_id`.
* **Please note**: The `address_id` field is also the foreign key for the `permits` table, allowing easier joins between `residents` and `permits` tables.
Please ensure that any existing queries or automation using `first_seen_at`, `reviews`, or `permit_id` are updated accordingly.
***
These new permits will be available across the entire platform.
### ✨ New:
* +6M permits nationwide.
* +9 permit jurisdictions.
### ✨ New:
* Added +2M permits in `Texas`.
* Added metric visualizations to **Contractor Profiles**.
### 🐞 Bugfixes:
* Corrected issue where `Contractor Profiles` wouldn't load properly.
✨ **New**:
* `residents` endpoint.
### 🚀 Upgrades:
* Improved address validation for all geography-related fields, in line with US Census data.
### 💥 Breaking:
* Casing standardizations (please update accordingly if your logic is **case-sensitive**):
* `property_type` values are now **lower case**, eg `residential`.
### ✨ New:
* `first_seen_at` column in the `contractor` table.
* `employees` table, linked to `contractors` table:
* Includes a wide range of firmographic data for individual employees.
* See "Schema Updates" below for the full list of columns.
### 🚀 Upgrades:
* additional columns in the `residents` table. See "Schema Updates" below for the full list.
### 💥 Breaking:
* Casing standardizations (in addition to the changes outlined in the API Section previously).
* `property_owner_type` values are now **snake\_case**, eg `individual` or `company_owned`.
* `owner_name`, `owner_street`, `owner_city` values are now **upper case**, eg `JANE DOE`, `MAIN ST`, or `AGAWAM`.
* `applicant_name`, `applicant_street`, `applicant_city` values are now **upper case**, just like `owner_$` above.
* (PLANNED) Replacement of `permits_ids` with `address_id` in the `residents` table (please update your pipelines that use this foreign key value.)
* This change will go into effect **January 2025**.
***
We made a number of additions to our datasets, including new columns. For breaking changes to casing, see "API" above.
* `personal_emails_validation_status` (string): The validation status of the associated personal email. 'Valid' indicates a validated email; null means unknown.
* `personal_emails_last_seen` (date): The date of the last validation or verification attempt for the personal email. Null if unknown.
* `business_email` (string): The primary business email observed for this person.
* `business_email_validation_status` (string): Validation status of the business email. 'Valid' means the email was validated; null means unknown. May contain values like 'Valid-ESP' (validated by email service provider) or 'Valid-Digital' (validated by cookie/digital tag).
* `business_email_last_seen` (date): The date of the last known validation or verification attempt of the business email. Null if unknown.
* `linkedin_url` (string): URL of the person’s LinkedIn profile.
* `homeowner` (string): Reports if the person in this record is a homeowner. Y and N are observed values, P represents that they are likely a homeowner, based on probabilistic modeling, and null values represent 'unknown'.
* `gender` (string): The person’s gender.
* `age_range` (string): The person’s age mapped to standard demographic ranges.
* `is_married` (boolean): Indicates if the person is married.
* `has_children` (boolean): Indicates if the person has children.
* `income_range` (string): The person’s income range.
* `net_worth` (string): The person’s net worth, mapped to standard demographic ranges. Null if unknown.
* `job_title` (string): The person’s job title.
* `seniority_level` (string): The seniority level of the person’s role.
* `department` (string): The department in which the person works.
* `job_title_last_updated` (date): The last date the person’s job title was updated.
* `last_updated` (date): The last date any value in this record was updated.
* `work_history` (string, JSON): A JSON-formatted history of the person’s work experiences (e.g., company name, \* position, duration, start/end time, job description, location, social\_url).
* `education_history` (string): The person’s education background.
* `social_connections` (string): The number of social media connections the person has, provided in ranges (e.g., 1-9, 10-49, etc.).
* `company_name` (string): The name of the company where the person works.
* `company_domain` (string): The company’s domain.
* `company_phone` (string): The company’s contact phone number.
* `company_sic` (string): The Standard Industrial Classification (SIC) code(s) of the company, separated by semicolons if multiple.
* `company_street_no` (string): The street number of the company’s address.
* `company_street` (string): The street name of the company’s address.
* `company_city` (string): The city in which the company is located.
* `company_zip` (string): The company’s 5-digit ZIP code.
* `company_state` (string): The uppercase state abbreviation for the company’s address.
* `company_linkedin_url` (string): The URL of the company’s LinkedIn profile.
* `company_revenue` (string): The company’s revenue, expressed in standard ranges.
* `company_employee_count` (string): The number of observed US enterprise employees at the company, provided in \* standard firmographic ranges.
* `company_primary_industry` (string): The primary industry in which the company operates.
* `company_description` (string): A description of the company’s business activities.
* `company_naics` (string): The company’s North American Industry Classification System (NAICS) code(s).
* `contractor_id` (string)
* `person_id` (string)
* `name` (string)
* `street_no` (string)
* `street` (string)
* `city` (string)
* `zipcode` (string)
* `zipcode_ext` (string)
* `state` (string)
* `phone` (string)
* `email` (string)
* `email_validation_status` (string)
* `email_last_seen` (date)
* `business_email` (string)
* `business_email_validation_status` (string)
* `business_email_last_seen` (date)
* `linkedin_url` (string)
* `homeowner` (string)
* `gender` (string)
* `age_range` (string)
* `is_married` (boolean)
* `has_children` (boolean)
* `income_range` (string)
* `net_worth` (string)
* `job_title` (string)
* `seniority_level` (string)
* `department` (string)
* `job_title_last_updated` (date)
* `work_history` (string, JSON formatted)
* `education_history` (string)