FindLaw Scraper — Lawyer & Law Firm Leads avatar

FindLaw Scraper — Lawyer & Law Firm Leads

Pricing

from $10.00 / 1,000 results

Go to Apify Store
FindLaw Scraper — Lawyer & Law Firm Leads

FindLaw Scraper — Lawyer & Law Firm Leads

Search millions of US attorneys and law firms by practice area, state, and city. Get names, phones, emails, websites, addresses, ratings, and social profiles — streamed to your dataset in real time. Paid plans get full output; free plans get a sample.

Pricing

from $10.00 / 1,000 results

Rating

0.0

(0)

Developer

Emmanuel

Emmanuel

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 days ago

Last modified

Categories

Share

FindLaw Real-Time Data — Attorney & Law-Firm Leads

Turn the largest US lawyer directory into a clean, structured lead list. Search by practice area + state + city, collect attorney and law-firm profiles with phones, emails, websites, and social links, and stream every row to your dataset in real time.

⚡ Real-time streaming • 🎯 Phones, emails & socials • 🏛️ Lawyers + firms • 🔔 Webhooks • 💸 Pay per result


This Actor is paid only. Free (Apify free-plan) accounts get a small sample of 2 results per run — after that the run finishes gracefully with a clear message to upgrade. There is no error, no crash, and nothing is lost: your sample is in the dataset.

PlanWhat you get
Apify free planLimited to 2 results per run. The run exits cleanly with an upgrade message.
Any paid Apify plan (Bronze → Diamond)✅ Full, unlimited output up to your maxItems — no caps.

Free accounts: upgrade to a paid Apify plan to get full, unlimited data. The restriction is also shown in the input UI and logged at the start of every limited run. A paywall object in the run OUTPUT always shows which mode applied.


What it does

FindLaw is the largest US attorney directory — millions of consumers and businesses use it monthly to find lawyers. This Actor turns that directory into structured, ready-to-use lead data:

  • Lawyer search — pick practice areas and locations; every matched attorney becomes a dataset row.
  • Law firm search — the same for firms, tagged separately.
  • Profile details — fetch full structured details for specific attorney or firm URLs.
  • Lead details (enabled by default) — for every result, the profile (and the practice's own website when needed) is used to collect phone, email, website, and social profiles. Adds a little extra time per business, but every business goes to the dataset — results are never filtered, so runtime per 1,000 businesses stays predictable. Lead-detail work is pipelined in parallel pools: profiles are read 8 at a time, then contact hunts run 8 at a time in their own pool, while each worker holds one exit and reuses it for every page it reads.
  • Webhooks — push each row to your CRM, Slack, Zapier, Make, or Google Sheets the moment it is collected.
  • Real-time streaming — rows are written to the dataset as they are found, so you can watch results arrive and export early if you want. RAM stays light during long scrapes.

Outcomes you get

  • A clean prospect list of attorneys/firms in your target practice + geography with direct contact details.
  • Predictable costs — you pay per result; the maxItems cap and the user spending limit are always honored.
  • Flat, stable JSON — same field names on every row, ready for Sheets, Airtable, a database, or an LLM pipeline.

Who it is for

PersonaWhat they do with it
Legal marketing agenciesBuild prospect lists of attorneys who need SEO, ads, or intake services.
Legal-tech & SaaS sales teamsFind firms by practice area and reach out with product offers.
Recruiters (attorney placement)Track which firms are growing, which practice areas they cover, and how to reach hiring partners.
Consultants & market researchersMap the competitive landscape of law practices by city and state.
Law firms (competitive intel)Watch which other firms show up in your practice + city, and what they advertise.
Bar associations & CLE providersAnalyze practice-area distribution across states and cities.
Lead brokers & data teamsEnrich CRM pipelines with verified directory presence and contact URLs.
AI/automation buildersFeed flat JSON into agents, RAG pipelines, and enrichment workflows.

Use cases

🎯 Attorney lead generation — Export every personal-injury firm in Texas with phones and websites for an outreach campaign.

🏢 Firm prospecting for vendors — Sell practice-management software, answering services, or marketing to firms by practice area and firm size signals.

📍 Market mapping — Count attorneys per city and practice area to pick expansion markets or plan ad budgets.

🕵️ Competitive monitoring — Schedule weekly runs for your practice area + city and see which competitors appear.

🤝 Recruiting pipelines — Track firms' attorney rosters, then use profile URLs to reach candidates.

📈 SEO/SEM prospect qualification — Identify firms with a directory presence but no website (an easy pitch for web services).

🧠 AI agents & RAG — Stream flat JSON into an LLM to summarize, score, or triage law-firm leads automatically.

🔁 CRM enrichment — POST results through webhooks into HubSpot/Salesforce/Zapier as new records arrive.


Quick start

  1. Open the Actor in Apify Console.
  2. In Search tasks, add one row per search — a practice area in a city or state.
  3. Leave Enable lead details on to collect phones, emails, websites, and socials.
  4. Press Start and watch rows stream into the dataset.
  5. Export as JSON / CSV / Excel, or pull via the Apify API, webhooks, or MCP.

Example — personal injury attorneys in two cities

{
"enableSearchLawyers": true,
"searchTasks": [
{ "practiceArea": "personal injury", "state": "New York", "city": "New York" },
{ "practiceArea": "personal injury", "state": "New York", "city": "Brooklyn" }
],
"enableLeadDetails": true,
"maxItems": 10000
}

Example — firms state-wide in two states

Leave City empty to cover a whole state — the task then works through the state's cities until it reaches its limit, so the rows are spread across the state instead of one market:

{
"enableSearchFirms": true,
"firmSearchTasks": [
{ "practiceArea": "divorce", "state": "California" },
{ "practiceArea": "divorce", "state": "Florida" }
],
"maxItems": 5000
}

Example — details for specific profile URLs

{
"enableScrapeByUrl": true,
"scrapeUrls": [
"https://lawyers.findlaw.com/profile/lawyer/…"
]
}

Example — several practice areas in one state

Practice areas accept natural names — common aliases are mapped automatically (car accident → vehicle-accident directory, criminal defense → criminal law, and similar).

{
"enableSearchLawyers": true,
"searchTasks": [
{ "practiceArea": "car accident", "state": "Texas" },
{ "practiceArea": "criminal defense", "state": "Texas" },
{ "practiceArea": "divorce", "state": "Texas" }
]
}

Example — a different size for each market

Five cities, but you only want a handful of leads from most of them and a deep pull from two:

{
"enableSearchLawyers": true,
"searchTasks": [
{ "practiceArea": "personal injury", "state": "Florida", "city": "Miami", "maxResults": 500 },
{ "practiceArea": "personal injury", "state": "Florida", "city": "Tampa", "maxResults": 250 },
{ "practiceArea": "personal injury", "state": "Florida", "city": "Orlando" },
{ "practiceArea": "personal injury", "state": "Florida", "city": "Jacksonville" },
{ "practiceArea": "personal injury", "state": "Florida", "city": "Hialeah" }
],
"searchMaxResultsPerTask": 25,
"maxItems": 5000
}

Miami exports up to 500 and Tampa up to 250 because their rows set maxResults. The other three have no maxResults, so they fall back to searchMaxResultsPerTask (25). maxItems still caps the whole run at 5,000.


Input reference (full schema)

FieldTypeDefaultDescription
enableSearchLawyersbooleantrueTurn on attorney search by practice area and location.
searchTasksobject[][]One row per search task — the main input. Columns: practiceArea (optional, e.g. personal injury; aliases like car accident are mapped automatically), state (required, e.g. Florida or FL), city (optional — leave empty to cover the whole state, up to 60 cities per task), maxResults (optional — caps just this task, falls back to the default below). Duplicate rows are collapsed.
searchMaxResultsPerTaskinteger10How many attorneys a task may export when its own maxResults is empty. Defaults to a small sample of 10 — raise it for a deeper pull per market.
searchDepthinteger20How far into each task's results to go (1–100). Usually the task's result cap should do the limiting; depth is the safety ceiling.
enableSearchFirmsbooleanfalseTurn on law-firm search.
firmSearchTasksobject[][]One row per firm search task — same columns as searchTasks.
firmSearchMaxResultsPerTaskinteger10How many firms a task may export when its own maxResults is empty. Defaults to a small sample of 10.
firmSearchDepthinteger10How far into each firm task's results to go (1–100).
enableLawyerProfilesbooleanfalseFetch details for specific attorney profile URLs.
lawyerUrlsstring[][]Attorney profile URLs.
enableFirmProfilesbooleanfalseFetch details for specific firm profile URLs.
firmUrlsstring[][]Firm profile URLs.
enableScrapeByUrlbooleanfalseScrape any directory URL with automatic lawyer/firm detection.
scrapeUrlsstring[][]Directory URLs to scrape.
enableLeadDetailsbooleantrueEnable lead details. Visit each profile (and, when needed, the practice's own website) to collect phone, email, website, and social profiles. Adds a little extra time per business. Nothing is filtered — every business goes to the dataset even if no lead details are found.
maxItemsinteger10000Global cap on total dataset rows across all features — the main cost lever. Applies on top of the per-task caps.
webhookUrlstring""Optional. Each record is POSTed here as JSON as it is collected (in addition to the dataset).
webhookFormatstringjsonjson = full record object; slack = Slack-friendly message payload.
proxyConfigurationobjectApify residential, USConnection settings. Apify residential proxy (US) is on by default; switch groups/country or provide your own proxy URLs if needed.

Validation rules: at least one feature must actually run (a search with practice areas/states, or profile/scrape URLs). A non-empty webhookUrl must be a valid http(s) URL. Free-plan runs are capped automatically as described above.

Note on lead details vs filters: there are no lead filters on purpose. Filtering results by lead completeness would make runtime per 1,000 businesses unpredictable and pricing impossible. With enableLeadDetails the Actor collects what it can for every row and returns everything.


Output reference (field-by-field)

Every row is a flat JSON object. Rows are tagged with featureType: lawyer_search, lawyer_profile, firm_search, firm_profile, or scrape_by_url.

Common fields (every row):

FieldTypeDescription
featureTypestringWhich feature produced the row.
scrapedAtstringISO 8601 UTC timestamp of collection.
sourcestringThe URL the row was collected from.
idstring | nullDirectory record identifier when present.
urlstring | nullDirectory profile URL.
namestring | nullAttorney or firm name.
profileImagestring | nullProfile photo URL when listed.
city / state / address / zipCodestring | nullLocation fields (attorneys may also have addressLine2 on firm rows).
phonestring | nullPhone number, normalized when possible.
emailstring | nullEmail address when listed (lead details).
websitestring | nullPractice's own website when listed (lead details).
websitesstring[] | nullAll website links found.
socialsstring[] | nullSocial profile URLs (Facebook, LinkedIn, X, Instagram, YouTube, …).
practiceAreasstring[] | nullPractice areas listed on the profile.
mainPracticeAreastring | nullFirst / primary practice area.
yearsExperienceinteger | nullYears of experience when stated.
rating / reviewCountnumber | nullDirectory rating and review count when shown.
freeConsultationboolean | nullWhether a free consultation is offered.
superLawyers / superLawyersCountboolean / integer | nullSuper Lawyers recognition and selection count when shown.
enrichedboolean | nulltrue when lead details were collected for this row.

Lawyer-only fields: firmName, firmUrl, bio, overview, avPreeminent, leadCounsel, awards, honors, languages.

Firm-only fields: addressLine2, description, firmSize, attorneys (list of {name, url, title, profileImage, superLawyers}), attorneyCount, appointments, creditCardsAccepted, virtualAppointments.

Example — a lawyer row:

{
"featureType": "lawyer_search",
"scrapedAt": "2026-09-24T12:00:00.000Z",
"source": "https://lawyers.findlaw.com/…",
"name": "Jane Doe",
"firmName": "Doe & Associates",
"firmUrl": "https://lawyers.findlaw.com/…",
"profileImage": "https://…",
"city": "New York",
"state": "New York",
"address": "350 Fifth Avenue, Suite 1000",
"zipCode": "10118",
"phone": "(212) 555-0123",
"email": "info@doelaw.com",
"website": "https://doelaw.com",
"websites": ["https://doelaw.com"],
"socials": ["https://www.linkedin.com/in/janedoe"],
"practiceAreas": ["Personal Injury", "Car Accidents"],
"mainPracticeArea": "Personal Injury",
"yearsExperience": 18,
"rating": 4.8,
"reviewCount": 32,
"freeConsultation": true,
"superLawyers": true,
"superLawyersCount": 5,
"enriched": true,
"url": "https://lawyers.findlaw.com/lawyer/attorney/…"
}

Example — a firm row:

{
"featureType": "firm_profile",
"scrapedAt": "2026-09-24T12:00:00.000Z",
"name": "Doe & Associates",
"city": "New York",
"state": "New York",
"phone": "(212) 555-0123",
"email": "info@doelaw.com",
"practiceAreas": ["Personal Injury"],
"attorneyCount": 6,
"attorneys": [
{ "name": "Jane Doe", "url": "https://lawyers.findlaw.com/…", "title": "Founding Partner", "profileImage": null, "superLawyers": true }
],
"freeConsultation": true,
"enriched": true,
"url": "https://lawyers.findlaw.com/…"
}

Run summary (OUTPUT tab → key-value store): every run writes an OUTPUT record with featuresEnabled, totalPushed, durationMs, errors, spendingLimitReached, and the paywall object:

{
"paywall": {
"detected": true,
"isPaying": false,
"pricingTier": "FREE",
"limited": true,
"blocked": false
}
}
  • detected — platform pay-status variables were present.
  • isPaying — the account is on a paid plan (full output).
  • pricingTier — the Apify plan tier when known.
  • limited — free-tier cap applied (limit mode).
  • blocked — free-tier block mode applied (owner-configurable; the default is limit).

Webhooks

Webhooks deliver every record in real time as it is collected — in addition to the dataset (the dataset always gets everything; the webhook is an extra push).

Setup

  1. Open the Actor's input in Apify Console.
  2. Paste your receiver URL into Webhook URL (must be http:// or https://).
  3. Choose Webhook format: JSON (full record) or Slack message.
  4. Start the run. Each record is POSTed right after it is saved to the dataset.

Any receiver that accepts JSON POST requests works: Zapier, Make (Integromat), n8n, Pipedream, Slack incoming webhooks, Microsoft Teams, Google Apps Script, your own API.

Apify platform webhooks (Console → Actors → Webhooks) work too — they fire on run lifecycle events and can include the default dataset URL. The input-UI webhook here is per-record and pushes row-by-row.

JSON payload

webhookFormat: json sends the full record — exactly the same object that lands in the dataset (see Output reference):

{
"featureType": "lawyer_search",
"scrapedAt": "2026-09-24T12:00:00.000Z",
"name": "Jane Doe",
"firmName": "Doe & Associates",
"city": "New York",
"state": "New York",
"phone": "(212) 555-0123",
"email": "info@doelaw.com",
"website": "https://doelaw.com",
"practiceAreas": ["Personal Injury"],
"url": "https://lawyers.findlaw.com/…"
}

Slack payload

webhookFormat: slack sends a Slack text message:

⚖️ *Jane Doe*
*Firm:* Doe & Associates • *Practice:* Personal Injury
*Phone:* (212) 555-0123
350 Fifth Avenue, Suite 1000
<https://lawyers.findlaw.com/…|View listing>

Delivery is best-effort: a failing receiver never stops the run or blocks dataset writes (a warning is logged instead).


MCP usage

Use this Actor from any MCP-compatible client (Claude Desktop, Cursor, VS Code, …) via the Apify MCP Server:

  1. Get an Apify API token.
  2. Point your MCP client at https://mcp.apify.com (SSE) with your token, or add the Apify MCP server entry to claude_desktop_config.json.
  3. Ask your assistant: "Run the FindLaw Real-Time Data actor for personal injury attorneys in Dallas, Texas, max 500 results, and give me the dataset."

The actor exposes the standard Apify tool set (call-actor, get-actor-details, get-dataset, …), so the agent can start runs, stream results, and read the dataset. You can also call it programmatically:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('YOUR_USERNAME/findlaw-real-time-data').call({
enableSearchLawyers: true,
searchTasks: [
{ practiceArea: 'personal injury', state: 'Texas', city: 'Dallas', maxResults: 500 },
],
enableLeadDetails: true,
maxItems: 500,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0]);

Controlling scope & cost

LeverEffect
Search tasksOne row per task — this is your scope. Add a row per practice area × city you want.
Max results on a task rowCap for that one task. Leave it empty to use the default.
searchMaxResultsPerTask / firmSearchMaxResultsPerTaskThe default cap for any task that does not set its own. Default 10.
maxItemsGlobal ceiling on dataset rows across every feature — the final cost lever.
searchDepth / firmSearchDepthSafety ceiling on how far each task goes (1–100).
enableLeadDetailsAdds a little extra time per business for contact collection. Turn off for fast, directory-only lists.
User spending limitApify applies your per-run max cost automatically — the Actor stops cleanly when the limit is reached, so you are never charged beyond what you approved.

Three levels of control

  1. Scope — which markets you ask for. The task list is your scope: one row per practice area and place. Delete a row and that market is skipped.
  2. Per task — how much from each. Set Max results on a row to cap that task alone; leave it empty and the task uses searchMaxResultsPerTask. A task stops as soon as it reaches its own limit and the rest of the run continues, so no single market can consume the run.
  3. Whole run — how much in total. maxItems caps the dataset across every feature and always wins over the two levels above.

The run log tells you the cap each task resolved to (… (personal injury / Miami / Florida) — reading results (depth 1, cap 500)…) and reports progress against it (9 found, 5 pushed (5/500 for this task)), so you can see exactly how each task was regulated without opening the dataset.

A capped run is also fast, not just small: work that cannot be exported is skipped rather than collected and discarded.

Dataset views

The Console dataset ships with tabbed views — Overview, Lawyers, Firms — so you can read results without building filters. Filter on featureType to split lawyers from firms.

Error handling & reliability

  • Temporary data-source issues are retried automatically with fresh connection exits.
  • One unreadable listing never stops the run — the Actor moves on and the summary reports what happened.
  • Failure messages are always stable, user-facing sentences; raw technical errors are never written to logs or output.
  • The user spending limit is honored: when reached, the run stops gracefully with everything collected so far intact.

Free tier (Apify free plan)

Free accounts are limited to 2 results per run; the run then exits gracefully with an upgrade message. Paid plans have no caps. See Paid only / free tier at the top. The owner can tune the sample size or switch to hard-blocking via the Actor's environment variables (FREE_TIER_MODE, FREE_TIER_MAX_ITEMS) without a code change.

FAQ

Is this Actor available on the free plan? Only as a tiny sample — 2 results per run. To get full, unlimited data you need a paid Apify plan. Free runs stop cleanly with an upgrade message; nothing crashes.

Why am I limited on the free plan? Collecting complete data has real infrastructure costs (compute + connections). The sample lets you verify data quality before upgrading.

How do I know if a run was capped? Check the run log (a warning is printed at the start) and the paywall object in the run OUTPUT — limited: true with your pricing tier.

Do I need API keys or login credentials? No. The Actor works with public directory data only — no accounts, no logins, no third-party API keys.

Are results filtered to only rows that have emails? No — by design. Enable lead details collects contact information when available, and every business goes to the dataset regardless. This keeps runtime predictable and pricing fair.

How fast is it? The Actor runs everything it can in parallel — independent search tasks run concurrently, and lead details flow through two pipelined pools (profiles, then contact hunts), the same speed model as the reference Google Maps actor. Speed is not only about width, though, so these rules keep long runs moving — each one is pinned by an offline test and was chosen from live measurements:

  1. Every worker holds one exit and reuses it. Minting a fresh residential exit per request looks parallel but costs a handshake on every page — measured at roughly 5 seconds per request. An exit is now swapped only when it is actually refused.
  2. A visit to a practice's own website keeps one single exit for the entire visit, instead of re-negotiating while looking for contact details.
  3. The proxy is never given more concurrent work than it can serve. Pushing far more parallel requests at a gateway than it has exits makes every read stall until it times out — measurably slower than simply queueing — so in-flight requests are capped at twice the exit pool.
  4. No single page can hold a batch. Every read has a time budget: a page that cannot be read within it is reported and the row keeps the data already collected, so one dead exit costs seconds instead of minutes.
  5. The slow half no longer blocks the fast half. Reading a row's details and hunting contacts on the practice's own site run in separate pools, so the slower contact hunt never queues behind the reads.

A run that is capped (a free-tier sample, or your own maxItems) also stops preparing rows it will never be allowed to export, and a state-wide task reads one city page rather than walking the whole state as soon as its own limit is reached.

Can a run look stuck? It cannot stay silent: each task logs the stage it is on — reading results, reading profiles, checking websites — as it goes, and logs when a task's limit is reached.

The result: in live testing, lead-detail runs complete at roughly 5–7 seconds per business — a 1,000-business run finishes comfortably inside the 10,000-second timeout. Runtime does depend on exit availability at the moment you run: if a data source is temporarily refusing requests, the Actor retries on a fresh exit, keeps the row, and moves on rather than stalling.

Will one bad URL break my run? No. Unreadable listings are skipped with a warning; the run continues.

Can I schedule runs? Yes — use Apify Schedules for any cron pattern; the same input is reused.

Can I get both lawyers and firms in one run? Yes — enable both searches; rows are tagged by featureType.

What formats can I export? JSON, CSV, Excel, HTML, RSS — plus the Apify API, webhooks, and integrations.

Does it respect my spending limit? Yes. If your per-run max cost is reached, the Actor stops collecting immediately and finishes gracefully — you are never charged beyond the limit you set.

Is any raw source content included in results or webhooks? No. Output and webhooks contain only the structured fields documented above.


Support

Open an issue on the Actor page or contact the publisher with your run ID and your (redacted) input JSON.


Contact me

Need something built beyond this Actor? I take on custom projects — from Apify scrapers and data pipelines to full-stack web apps.

Emaildubem115@gmail.com
GitHubgithub.com/DrunkCodes

Reach out with a short description of your project and timeline — happy to discuss scope and pricing.