Angi Scraper & API | Contractor Listings and Reviews · Apify
Angi Scraper | Contractors, Reviews & Public Contacts avatar

Angi Scraper | Contractors, Reviews & Public Contacts

Pricing

from $2.20 / 1,000 results

Go to Apify Store
Angi Scraper | Contractors, Reviews & Public Contacts

Angi Scraper | Contractors, Reviews & Public Contacts

Extract public Angi contractor listings, ratings, available addresses and reviews by US ZIP, plus supported HomeStars and European profiles. Pay per delivered row plus a start fee; US contractor phones are usually unavailable.

Pricing

from $2.20 / 1,000 results

Rating

0.0

(0)

Developer

kane liu

kane liu

Maintained by Community

Actor stats

2

Bookmarked

2

Total users

0

Monthly active users

6 hours ago

Last modified

Categories

Features

Limited permissions

Share

Extract public Angi contractor listings, profile details and reviews into exportable rows, with related marketplaces in Canada, the UK and Europe.

  • Search US home-service trades by ZIP — plumbing, HVAC, roofing.
  • Read published names, addresses, ratings and profile links.
  • Collect public catalogs, sitemap entries and Angi Inc company information.
  • Follow supported international profiles, including HomeStars Canada.
  • Pay $3.00 per 1,000 delivered rows on the Free plan, plus a start fee.

What does Angi Scraper do?

Angi Scraper reads public information from Angi.com, formerly Angie's List. Available fields depend on the page; selecting a mode does not guarantee that the source will make that page accessible.

  • US listings: nearme lists a service; search uses a category ID and ZIP.
  • Profiles and company pages: detail, contact and about return available public information. US profile reviews become separate rows when present.
  • International pages: intl-list and intl-detail cover HomeStars (ca), MyBuilder (uk), MyHammer (de, at), Travaux (fr), Werkspot (nl) and Instapro (it). homestars is a Canadian profile mode.
  • Reference rows: catalog returns public metadata; sitemap lists URLs without fetching the profiles.
  • Optional detail enrichment: enrichDetails is off by default. Direct profile collection can include review rows without this switch.

What data can you extract from Angi?

Fields vary by kind.

Contractor rows: search, nearme, detail, intl-list, intl-detail

FieldDescriptionExample
kindCollection typesearch
nameBusiness display nameExample Plumbing
urlCanonical profile linkhttps://www.angi.com/companylist/us/ny/new-york/example-reviews-12345.htm
addressPublished street, locality, region, postal code and country{"locality":"New York"}
phonesPhone entries; empty for US contractors unless wantsPhone discloses a shared call-tracking number[]
phoneSourcePhone provenance; shared_tracking means an Angi call-tracking number, not the contractor's direct lineshared_tracking
phoneIsDirectWhether the number dials the business directly; false for tracking numbersfalse
descriptionBusiness self-descriptionLicensed plumbing contractor
gradeAngi letter gradeA
gpaAngi numeric grade3.79
businessHighlightsPublic badges (e.g. Family Owned, Emergency Services)[{"name":"EmergencyServices"}]
logoBusiness logo object when published{"url":"https://..."}
ratingRating on the source's scale4.8
reviewCountPublished review total42
reviewsIdNumber from a US profile URL12345
serviceProviderIdUS search identifier, distinct from reviewsId67890
ratingRawOriginal international profile rating8
ratingScaleExplicit international rating scale{"bestRating":10,"worstRating":1}
leadAbsencesReasons contact fields are missing{}

Join US rows by canonical profile URL or reviewsId, not serviceProviderId. An international rating without a confirmed scale can have rating: null while retaining ratingRaw.

Set wantsPhone to request the US gateway contact block. Full-page requests attach a phones entry with phoneSource: "shared_tracking" and phoneIsDirect: false; smaller requests leave phones empty.

Use intl-catalog with an international market (ca, uk, fr, nl, de) to list service categories (entityType: "service"). Use intl-service-pro with an international market for ServicePro merchants (entityType: "servicePro"), setting serviceProSlug for one merchant or omitting it to list the directory.

Review rows: kind=review

FieldDescriptionExample
rowTypeIdentifies a separately billed review rowreview
parentIdContractor identifier12345
parentUrlContractor profile linkSame as the parent row
reviewerNamePublished author nameJane Doe
ratingIndividual review rating5
dateLabelDate as displayedSeptember 2026
textReview textExample review text.
verifiedSource verification flagtrue
projectCostPublished project cost$500

Company rows: contact, about

FieldDescriptionExample
nameCompany, not a contractorAngi Inc
addressPublished company address{"locality":"Indianapolis"}
phonesCompany contact numbers when shown[{"value":"+1 555 0100"}]
webCompany website and social links{"website":"https://www.angi.com/"}

Catalog rows: categories, businessHighlights, featureflags, platformConfig

FieldDescriptionExample
kindMetadata categorycategories
nameEntry name, when suppliedPlumbing
rawCategory or business-highlight entrySource object
dataFeature or platform settingsSource object

Sitemap rows: sitemap-category, sitemap-pro

FieldDescriptionExample
urlListed category or profile URLhttps://www.angi.com/nearme/plumbing/
enumeratedURL entry, not fetched profile contenttrue
permalinkIdProfile number extracted from the URL12345
seedSuggested input for a later run{"searchType":"detail","detailUrls":["https://www.angi.com/companylist/us/ny/new-york/example-reviews-12345.htm"]}

How to scrape Angi

  1. Open Angi Scraper in Apify Console.
  2. Choose a searchType and fill the corresponding target below.
  3. Set maxResults to 3 for a small first run.
  4. Click Start and wait for completion.
  5. Open the dataset and download your rows.

Empty input, or only platform defaults, starts an example with nearme, hvac, ZIP 10001 and at most 3 rows. It is a paid run under the same event prices, not a free preview. INPUT_REPORT marks it with exampleRun: true.

A ZIP alone uses nearme. detailUrls takes priority and each supported URL determines its page type. Set searchType explicitly for other modes: entering categoryId alone does not select search.

Near-me listings

Use nearme, a service slug such as roofing, and postalCode. Results may include surrounding service areas, not only businesses inside that ZIP.

Use search, categoryId and postalCode; 107 means plumbing. Use the matching category when requesting street merging.

US profiles

Use detail with detailUrls. The contractor row comes first.

Company contact and About pages

Use contact or about. No target URL is needed.

International city lists

Use intl-list, a non-US market and that site's city slug, such as toronto or paris-01.

International profiles and HomeStars

Use intl-detail with supported profile URLs.

Catalogs

Use catalog with catalogKind: categories, businessHighlights, featureflags or platformConfig.

Sitemaps

Use sitemap with category-sitemap, or pro-pool plus sitemapShard.

What to fill in

FieldWhat to enterDefault
searchTypeMode described abovenearme
marketus, ca, uk, de, at, fr, nl, itus
categoryNear-me service slughvac
categoryIdNumeric US search category; set it for your trade107
postalCodeUS ZIP or ZIP+410001 only without a location
cityUS city or international city slugEmpty
regionUS state with cityEmpty
detailUrlsArray of supported page URLs[]
sliceBuilt-in service/city choice, such as us/plumbingEmpty
queryCategory catalog search text; not a substitute for categoryIdEmpty
maxResultsRows per search or detail URL, from 1 to 7070; form prefill 3
maxPagesNo longer used; delivered rows follow maxResults onlyEmpty
ssaFilterLimit US search to Super Service Award businessesfalse
enrichDetailsAdd available profile fields to listing rowsfalse
mergeNearmeStreetsAdd streets from matching near-me listings to US searchtrue
catalogKindMetadata or sitemap typeCategories when omitted
sitemapShardProvider sitemap part, from 1 to 71First part
harvestSeedsSave discovered service/city choices for later reusefalse

For US city searches, clear the form's prefilled ZIP and supply city plus optional region. An explicit ZIP wins. region alone is invalid. A slice replaces the US service or international city choice, not searchType.

Near-me example

{"searchType":"nearme","category":"hvac","postalCode":"75201","maxResults":3}

US search example

{"searchType":"search","category":"plumbing","categoryId":107,"postalCode":"60601","maxResults":3}

US profile example

Replace the illustrative URL with a profile URL from your results.

{"searchType":"detail","detailUrls":["https://www.angi.com/companylist/us/ny/new-york/example-reviews-12345.htm"],"maxResults":3}

Contact example

{"searchType":"contact","maxResults":1}

About example

{"searchType":"about","maxResults":1}

International list example

{"searchType":"intl-list","market":"ca","city":"toronto","maxResults":3}

International ServicePro example

{"searchType":"intl-service-pro","market":"ca","serviceProSlug":"217845-new-canadian-drain-plumbing-ltd","maxResults":1}
{"searchType":"intl-service-pro","market":"uk","maxResults":3}

International profile example

{"searchType":"intl-detail","market":"uk","detailUrls":["https://www.mybuilder.com/profile/example-business"],"maxResults":1}

HomeStars example

{"searchType":"homestars","market":"ca","detailUrls":["https://www.homestars.com/profile/example-business"],"maxResults":1}

Catalog example

{"searchType":"catalog","catalogKind":"categories","query":"plumbing","maxResults":3}

Sitemap example

{"searchType":"sitemap","catalogKind":"pro-pool","sitemapShard":1,"maxResults":3}

maxItems and limit are aliases for maxResults; zip and zipCode mean postalCode, and urls and startUrls mean detailUrls. Close field-name misspellings are corrected; unknown fields are ignored with a note.

Each run writes its interpretation to INPUT_REPORT in the default key-value store. Invalid inputs stop before row charges with an INVALID_INPUT: message naming the field and fix (the start fee can still apply). Numeric limits are clamped to their allowed range.

Output example

Illustrative US search row; the business and profile ID are placeholders, not a live result.

{
"kind": "search",
"id": "67890",
"name": "Example Plumbing",
"url": "https://www.angi.com/companylist/us/ny/new-york/example-reviews-12345.htm",
"category": "plumbing",
"categoryId": 107,
"serviceProviderId": "67890",
"reviewsId": "12345",
"rating": 4.8,
"reviewCount": 42,
"phones": [],
"emails": [],
"address": {"street": null, "locality": "New York", "region": "NY", "country": "US"}
}

Export the dataset as JSON, CSV, Excel, XML, RSS or an HTML table. INPUT_ECHO records accepted settings, RUN_SUMMARY describes completion, and ERROR_SUMMARY describes failures.

Why use this Angi scraper?

  • Pay for delivered rows. Zero-row pages do not incur row charges; the start fee still applies.
  • Keep delivered data. Written rows and saved partial results remain available after a stop.
  • Understand missing fields. Contact restrictions are recorded in leadAbsences instead of invented phone numbers.
  • Process several URLs. detailUrls accepts multiple supported pages in one run.
  • No proxy setup. No external proxies or Angi login are needed.

How much does it cost to scrape Angi?

Pricing is pay-per-event: $0.005 per run start at the default 512 MB (1 event per GB, minimum 1) and $0.003 per row on the Free plan. Platform usage is included.

Apify planStart at 512 MBPrice per rowPrice per 1,000 rows
Free$0.005$0.003$3.00
Starter$0.005$0.003$3.00
Scale$0.005$0.0026$2.60
Business$0.005$0.0022$2.20
Platinum$0.005$0.002$2.00
Diamond$0.005$0.0016$1.60

maxResults applies to each category/ZIP search or detail URL. Its default and maximum are 70. Contractor and review rows share that allowance. Free Apify plan users have developer-set limits of 200 rows per run and 10 runs of this Actor. The whole run is also limited by Maximum cost per run (maxTotalChargeUsd in the API).

Example: one input delivering 5 rows at 512 MB on the Free plan costs 5 × $0.003 + $0.005 = $0.020. Limits are ceilings, not promises of matching results. Zero-row runs have no row charge but still have the start fee.

  • The Apify Free plan includes $5.00 in monthly usage credit (about 1,666 rows at $0.003 per row across Free-plan runs capped at 200 rows per run).

What happens when the run timeout is too short

When collection needs more time than the selected run timeout, automatic resumption can pause the run rather than fail it. It temporarily shows ABORTED with the message "Queued on the collection backend; this run will resume automatically when results are ready (about N s)."

When results are ready, the same run resumes and writes to the same dataset. A continuation after earlier delivery can show "Resumed: delivering N rows." Already-delivered rows are skipped rather than delivered and charged again. Successful delivery finishes as SUCCEEDED; a source failure can still fail the run.

Keep the default timeout of 3600 seconds to reduce interruptions. Synchronous endpoints (run-sync, run-sync-get-dataset-items) wait at most 300 seconds and can return an empty dataset while the run is paused. Start longer runs asynchronously and read the dataset after SUCCEEDED, or use a succeeded-event webhook.

Use with AI agents and the API

Apify MCP server

Add this tool to Claude, ChatGPT, Cursor, VS Code or another client with remote MCP support:

https://mcp.apify.com?tools=lentic_clockss/angi-scraper

Sign in with Apify through OAuth, or send Authorization: Bearer <YOUR_APIFY_TOKEN>. The tool returns run and dataset IDs; read rows with get-dataset-items. Clients without remote support can run npx @apify/actors-mcp-server --tools lentic_clockss/angi-scraper with APIFY_TOKEN configured.

Example prompt: "Find three plumbing contractors for ZIP 60601 using category 107. Return names, ratings and profile URLs."

  • Agent execution note: Set maxResults between 5 and 20 on exploratory tool calls to keep responses compact, and read RUN_SUMMARY and INPUT_REPORT from the default key-value store to inspect normalized inputs and delivered row counts. When a long run briefly reports ABORTED with the automatic-resume status message, wait for the same run ID to finish as SUCCEEDED instead of starting a duplicate run.

Apify API

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("lentic_clockss/angi-scraper").call(run_input={
"searchType": "search", "category": "plumbing", "categoryId": 107,
"postalCode": "60601", "maxResults": 3
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
print(row)
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('lentic_clockss/angi-scraper').call({
searchType: 'search', category: 'plumbing', categoryId: 107,
postalCode: '60601', maxResults: 3
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);

For asynchronous HTTP calls, send the input as JSON to POST https://api.apify.com/v2/acts/lentic_clockss~angi-scraper/runs with an Authorization: Bearer <YOUR_APIFY_TOKEN> header. Read rows at GET https://api.apify.com/v2/datasets/<datasetId>/items?format=json after completion.

Integrations

Use Apify integrations with Google Sheets, Make, Zapier, n8n, Slack, Airtable or your own webhook. Use Apify Schedules for recurring runs.

FAQ

This Actor reads public data only. You are responsible for complying with the site's terms and applicable data-protection law. Processing personal data requires a lawful basis. This is an unofficial tool, not an Angi product.

Why did my run end as ABORTED?

An ABORTED run with the automatic-resumption message is paused, not finished. Wait for its continuation. A manual abort is different; inspect the status message before starting another run.

How many results can I get?

Request 1–70 rows per input. Source availability, the Free plan limits and your maximum cost can reduce delivery.

Why are some fields empty?

The source may omit them or restrict them to signed-in users. leadAbsences explains missing contact fields. The requested ZIP is not substituted for a contractor's own address.

Why do leadAbsences reasons look different from earlier runs?

From November 2026 the reasons are English codes. Earlier runs returned Chinese-language labels; the table shows which new code each one became.

FieldEarlier label (JSON-escaped)New value
phones on contractor and intl-service-pro rows, except intl-detail rows with a public profile"\u672c\u7ad9\u65e0\uff1a<note>"gated (<note>), e.g. gated (serviceProPublicProfile=null on the anonymous API)
emails, address.street, address.postalCode and web.website on search, nearme and intl-list rows whose profile page was not readSamenot_on_list_page (<note>), e.g. not_on_list_page (list card has no email)
personSamenot_extracted (no person name parsed on the page)
emails on intl-service-pro rows, url on intl-catalog rows, address.street on about rowsSamenot_collected (<note>), e.g. not_collected (Services query selects only id, slug and name; no page url)
Gateway card fields gpa, grade, logo, description, awards, businessHighlights, advertisingDetails, eligibleForRaq, hasCoupons and nearmeCardDetails sent as null, "" or []SameNot on source: gateway card JSON <key> is empty, e.g. Not on source: gateway card JSON awards is empty
Any other empty fieldSamenot_verified (<note>), e.g. not_verified (gateway card JSON has no gpa key)
A field that now has a value, and address.postalCode on enriched rowsAny, e.g. "\u5217\u8868\u5361\u65e0 ZIP\uff0c\u9700\u8be6\u60c5"No entry

quality.missingFields marks not_present only for Not on source.

Can I get every contractor's phone number?

No. US guest contractor pages usually hide phones. Angi Inc company contacts and HomeStars profiles may publish numbers.

Can I search a whole city?

A US city resolves to one representative ZIP, not every ZIP in that city.

What does a zero-row run mean?

A confirmed no-match or unsupported input can finish successfully with zero rows. An inaccessible page or unexplained empty response is not automatically treated as no match. Check RUN_SUMMARY or ERROR_SUMMARY.

Can I collect map results or submit quote requests?

No. map, city-hub, leads-funnel and fake-tld are unavailable. Quote submissions and private account data are outside this Actor's scope.

Limitations

  • No guaranteed complete city, country, category or review coverage.
  • No guaranteed phone, email or street address for each contractor.
  • Other Angi country domains are not supported; use the named international marketplaces.

More local services and lead generation scrapers from the same publisher:

Browse all of our scrapers at apify.com/lentic_clockss.

Support

Report a problem or request a field through the Issues tab of this Actor's page. Include the run ID and the input you used.