# Justia Lawyer Profiles, Reviews & Office Locations Scraper (`abotapi/justia-lawyer-scraper`) Actor

Scrape attorney profiles from the Justia Lawyer Directory. Extract names, contacts, office locations, practice areas, education, experience, awards, bar admissions, peer endorsements and Justia Lawyer Ratings. Search by state, city, practice area or scrape profile URLs.

- **URL**: https://apify.com/abotapi/justia-lawyer-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Lead generation, Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.25 / 1,000 attorney results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Justia Lawyer Directory Scraper

Scrape public US attorney profiles from the [Justia Lawyer Directory](https://lawyers.justia.com) - one of the largest free legal directories in the United States. The actor returns **55+ structured fields per attorney**, including identity, contact details, multiple offices with weekly hours, practice areas with sub-specialties, full education and bar admissions, awards, professional associations, peer endorsements, the Justia Lawyer Rating breakdown, badges, and social profiles.

### Why use this actor

- **Richest field set on the market** - every section Justia exposes: badges, biography, practice areas (with sub-areas), additional practice areas, video conferencing, fees, jurisdictions (with bar ID + admission year), languages, professional experience, education (school + degree + year), awards, professional associations, speaking engagements, certifications, publications, websites, videos, social profiles, multiple offices (each with name, address, phone, hours), peer endorsements, rating breakdown, and the Justia Lawyer Rating.
- **Two modes** - search by state(s) + practice area(s) + city, OR paste your own Justia directory / category / profile URLs.
- **Precise filters** - narrow by Justia Lawyer Rating, free consultation, or top-rated status, so you only pay for the records you actually want.
- **Output that's ready for downstream tools** - stable `type:id` key for idempotent upserts, clean JSON, typed arrays + objects.

### Input parameters

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (build URL from filters) or `url` (paste Justia links). |
| `states` | array | `["California"]` | One or more US states (full name or 2-letter code). Search mode only. |
| `practiceAreas` | array | `["Personal Injury"]` | One or more practice areas (e.g. `"Personal Injury"`, `"Criminal Law"`, `"Family Law"`). `"All"` / empty = no area filter. Search mode only. |
| `cities` | array | `[]` | Optional city filter. Combines with each state. |
| `startUrls` | array | - | One or more Justia URLs (directory listing, category, or attorney profile). URL mode only. |
| `fetchDetails` | boolean | `true` | Open each profile to add 40+ fields (badges, bio, practice areas, offices, education, experience, awards, peer reviews, rating breakdown). |
| `fetchVcard` | boolean | `false` | Also download the `.vcf` file to add a structured vCard block (fax, name parts, organization). |
| `minRating` | number | - | Only keep attorneys with at least this Justia Lawyer Rating (0-10 scale). |
| `freeConsultationOnly` | boolean | `false` | Only keep attorneys who offer a free consultation. |
| `topRatedOnly` | boolean | `false` | Only keep attorneys with the maximum Justia rating (10.0). |
| `maxItems` | integer | `20` | **THE cap for the run.** Stop after this many records. `0` = unlimited. |
| `maxPages` | integer | `0` | Max pages per directory URL. Does NOT cap the run (defers to `maxItems`). `0` = unlimited. |
| `proxy` | object | Residential (US) | Network connection. Residential is the default and is recommended; it requires a paid Apify plan. |

### Example inputs

**Search by state + practice area**
```json
{
  "mode": "search",
  "states": ["California", "New York"],
  "practiceAreas": ["Personal Injury", "Family Law"],
  "fetchDetails": true,
  "maxItems": 200
}
````

**Drill into a city**

```json
{
  "mode": "search",
  "states": ["California"],
  "practiceAreas": ["Personal Injury"],
  "cities": ["San Francisco"],
  "fetchDetails": true,
  "maxItems": 50
}
```

**Direct profile URLs**

```json
{
  "mode": "url",
  "startUrls": [
    "https://lawyers.justia.com/lawyer/christopher-adamson-10198",
    "https://lawyers.justia.com/lawyer/alan-ahdoot-83686"
  ],
  "fetchDetails": true,
  "fetchVcard": true
}
```

**Scrape a whole directory page (with pagination)**

```json
{
  "mode": "url",
  "startUrls": ["https://lawyers.justia.com/lawyers/personal-injury/california"],
  "fetchDetails": true,
  "maxItems": 500
}
```

### Output

Each record is one attorney profile (`type = "profile"`). Every record includes stable identifiers - `type`, `id` (numeric), `url`, and `profileUrl` - so the recommended idempotency key for deduplication / upserts is `type + ":" + id`.

#### Top-level convenience fields

| Field | Type | Notes |
| --- | --- | --- |
| `type`, `id`, `url`, `profileUrl`, `slug` | string / number / string | Stable identifiers. `slug` is the URL slug without the numeric id. |
| `name`, `jobTitle`, `jobTitles[]` | string / string\[] | Identity + tagline. |
| `primaryPhone`, `telephones[]` | string / string\[] | Aggregated across header + offices. |
| `profileImage` | string | Avatar URL. |
| `justiaRating`, `topRated` | number / boolean | Justia Lawyer Rating (0-10 scale). `topRated` = rating ≥ 10. |
| `clientReviewCount`, `clientReviewRating` | number | Counts shown on the profile. |
| `ratingBreakdown` | object | Per-criterion scores: Legal Knowledge, Legal Analysis, Communication Skills, Ethics and Professionalism (0-5 scale). |
| `yearsOfExperience` | number | From the profile header facts. |
| `city`, `state`, `breadcrumb[]` | string / array | Geographic context from the breadcrumb. |
| `badges[]`, `claimedProfile`, `offersVideoConferencing`, `connectPro`, `liiPlatinum`, `hasSocialMedia` | array / booleans | From the Badges section. |
| `biography` | string | Multi-paragraph biography. |
| `practiceAreas[]` | array of `{area, subAreas[]}` | Each area with its sub-specialty list. |
| `additionalPracticeAreas[]` | array of strings | Additional areas not in the main list. |
| `videoConferencing[]` | array of strings | FaceTime, Zoom, Microsoft Teams, RingCentral, etc. |
| `fees[]`, `freeConsultation`, `contingentFees` | array / booleans | Fees + consultation model. |
| `jurisdictions[]` | array of `{name, organization, idNumber, since}` | Bar admissions with ID + admission year. |
| `languages[]` | array of `{language, modalities[]}` | Spoken / written modalities per language. |
| `experience[]` | array of `{role, organization, startDate, endDate}` | Professional experience entries. |
| `education[]` | array of `{school, degree, field, startDate, endDate, honors}` | Law school + undergraduate. |
| `awards[]` | array of `{title, organization, year}` | Honors and recognitions. |
| `associations[]` | array of `{organization, role}` | Professional associations. |
| `speakingEngagements[]` | array of `{title, date, description}` | CLEs + speaking engagements. |
| `certifications[]` | array of `{title, organization, year}` | Certifications. |
| `publications[]` | array of `{title, details[]}` | Publications. |
| `websites[]`, `websiteDetails[]` | array / array of `{type, title, url, description}` | Firm websites + blogs. |
| `videos[]` | array of strings | Video URLs (YouTube, Vimeo). |
| `socialProfiles[]` | array of strings | Facebook, X, LinkedIn, Instagram, Avvo, Martindale, Lawyers.com, etc. |
| `offices[]` | array of `{name, label, telephone, address, officeHours[]}` | Every office with full address + weekly hours. |
| `addresses[]`, `officeHours[]` | array | Top-level convenience: every office address, plus the first office's hours. |
| `peerReviews[]` | array of `{reviewerName, reviewerProfileUrl, date, rating, text}` | Lawyer-to-lawyer endorsements. |
| `emailContactUrl`, `vcardUrl` | string | Contact-form URL and vCard download URL. |
| `vcard` | object | (Optional, `fetchVcard:true`) Parsed vCard with fax, name parts, organization, structured address, URLs. |
| `sourceUrl`, `scrapedAt`, `metadata` | string / object | Run bookkeeping. |

#### Example record (excerpt)

```json
{
  "type": "profile",
  "id": 10198,
  "url": "https://lawyers.justia.com/lawyer/christopher-adamson-10198",
  "profileUrl": "https://lawyers.justia.com/lawyer/christopher-adamson-10198",
  "slug": "christopher-adamson",
  "name": "Christopher Adamson",
  "jobTitle": "Award-Winning Personal Injury Lawyer",
  "primaryPhone": "(866) 540-4527",
  "justiaRating": 10.0,
  "topRated": true,
  "ratingBreakdown": {
    "Legal Knowledge": 5.0,
    "Legal Analysis": 5.0,
    "Communication Skills": 5.0,
    "Ethics and Professionalism": 5.0
  },
  "yearsOfExperience": 20,
  "badges": ["Claimed Lawyer Profile", "Offers Video Conferencing", "LII Platinum", "Social Media"],
  "claimedProfile": true,
  "offersVideoConferencing": true,
  "liiPlatinum": true,
  "freeConsultation": true,
  "contingentFees": true,
  "practiceAreas": [
    { "area": "Personal Injury",
      "subAreas": ["Animal & Dog Bites", "Brain Injury", "Car Accidents", "Construction Accidents", "Motorcycle Accidents", "Premises Liability", "Truck Accidents", "Wrongful Death"] }
  ],
  "jurisdictions": [
    { "name": "California", "idNumber": "238500", "since": "2005" }
  ],
  "languages": [{ "language": "English", "modalities": ["Spoken", "Written"] }],
  "education": [
    { "school": "Loyola Law School, Loyola Marymount University", "degree": "J.D.", "endDate": "2005" },
    { "school": "University of California - Los Angeles", "degree": "B.A.", "endDate": "2002" }
  ],
  "offices": [
    {
      "name": "Adamson Ahdoot LLP",
      "label": "San Jose Office",
      "telephone": "(866) 552-1841",
      "address": { "street": "3165 Olin Ave #2", "city": "San Jose", "region": "CA", "postalCode": "95117", "country": "US" },
      "officeHours": [ { "day": "Monday", "hours": "Open 24 hours" } ]
    }
  ],
  "peerReviews": [
    { "reviewerName": "Alan Ahdoot", "date": "March 12, 2024", "rating": 10.0,
      "text": "caring and experienced advocate. Breadth of knowledge and experience coupled with compassion and empathy." }
  ],
  "socialProfiles": [
    "https://www.facebook.com/AdamsonAhdootLLP/",
    "https://www.linkedin.com/in/christopher-adamson-4731526/"
  ],
  "vcardUrl": "https://lawyers.justia.com/lawyer/christopher-adamson-10198/vcard",
  "emailContactUrl": "https://lawyers.justia.com/lawyer/christopher-adamson-10198/contact"
}
```

### Cost control

See the Store page for current rates. Two settings drive what a run costs:

- **Fetch full profile.** When on, each attorney's profile page is retrieved to add the 40+ detail fields, and that retrieval is billed per attorney. When off, you get the listing fields only and nothing extra is billed.
- **Max items.** The hard cap on records for the run. Set it deliberately.

For cheap discovery, run with Fetch full profile off, pick the attorneys you care about, then re-run those profile URLs in URL mode with it on. Runs that return no records are not billed for results.

### Scheduling & automation

Each record has a stable `type + ":" + id` key so scheduled runs can power idempotent upserts into your CRM, warehouse, or lead database.

Common recurring workflows:

- **Weekly state-level refresh** - re-run the same `states + practiceAreas` combination to detect new attorneys or profile updates.
- **Monthly enrichment sync** - re-scrape previously stored profile URLs (URL mode) to refresh phones, addresses, ratings, and offices.
- **Change monitoring** - track competitive movements in a specific vertical (DUI in Texas, Immigration in New York, etc.).
- **Lead pipeline replenishment** - automatically append newly discovered attorneys into outbound or enrichment workflows.

### Notes on data coverage

- The actor returns **everything Justia exposes publicly on the profile page**. Optional fields may be `null` for attorneys who haven't claimed / filled those sections.
- Justia does **not** publish attorney email addresses on the profile page itself - only an `emailContactUrl` (a contact form) and a `vcardUrl` (a `.vcf` file with the address + phone). To enrich emails yourself, follow the firm `websites[]` and look for a public `mailto:` on the firm's own contact page.
- The Justia Lawyer Rating is a peer-endorsement score (0-10 scale). `ratingBreakdown` is the per-criterion 0-5 breakdown.
- The actor runs on a Residential (US) connection by default, which requires a paid Apify plan. Leave the Proxy field at its default unless you have a reason to change it.

# Actor input Schema

## `mode` (type: `string`):

How to start the scrape. 'search' builds a directory URL from the state(s), practice area(s), and city below. 'url' takes the exact Justia directory or profile URLs you paste.

## `states` (type: `array`):

One or more US states (full name like 'California' or 2-letter code like 'CA'). Leave empty for all states. Applied as a search filter in search mode only.

## `practiceAreas` (type: `array`):

One or more practice areas (e.g. 'Personal Injury', 'Criminal Law', 'Family Law'). 'All' or empty means no area filter. Applied as a search filter in search mode only.

## `cities` (type: `array`):

Optional city filter (e.g. 'San Francisco'). Combines with each state in search mode. Leave empty to scrape the entire state.

## `startUrls` (type: `array`):

Paste one or more Justia URLs (directory listing like https://lawyers.justia.com/lawyers/personal-injury/california, category, or attorney profile like https://lawyers.justia.com/lawyer/christopher-adamson-10198). Multi-URL supported. Filter fields above are ignored in URL mode. If a URL already includes a page number, results continue from that page.

## `fetchDetails` (type: `boolean`):

Open each attorney profile page to add badges, biography, practice areas with sub-specialties, multiple offices with weekly hours, education, experience, awards, professional associations, jurisdictions, languages, certifications, speaking engagements, social profiles, peer endorsements, and the Justia Lawyer Rating breakdown. Adds one HTTP request per attorney.

## `fetchVcard` (type: `boolean`):

Download the .vcf file each profile exposes to add a structured vCard block: name parts, fax, organization, structured address, URLs. Adds one small HTTP request per attorney. Off by default (the HTML page already covers most fields).

## `minRating` (type: `number`):

Only keep attorneys with at least this Justia Lawyer Rating (0 to 10 scale).

## `freeConsultationOnly` (type: `boolean`):

Only keep attorneys who advertise a free consultation.

## `topRatedOnly` (type: `boolean`):

Only keep attorneys with the maximum Justia Lawyer Rating (10.0).

## `maxPages` (type: `integer`):

How many result pages to read per directory URL. Does NOT cap the run. Leave empty (or 0) for unlimited pages. The run is bounded by Max results, which is the cap.

## `maxItems` (type: `integer`):

THE cap for the run: stop after this many attorney records (default 20). Set to 0 for unlimited.

## `proxy` (type: `object`):

Network connection. Leave this at the default (Residential, US) unless you have a specific reason to change it. Residential requires a paid Apify plan.

## Actor input object example

```json
{
  "mode": "search",
  "states": [
    "California"
  ],
  "practiceAreas": [
    "Personal Injury"
  ],
  "cities": [],
  "startUrls": [
    "https://lawyers.justia.com/lawyers/personal-injury/california"
  ],
  "fetchDetails": true,
  "fetchVcard": false,
  "freeConsultationOnly": false,
  "topRatedOnly": false,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `credentials` (type: `string`):

No description

## `reviews` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "states": [
        "California"
    ],
    "practiceAreas": [
        "Personal Injury"
    ],
    "cities": [],
    "startUrls": [
        "https://lawyers.justia.com/lawyers/personal-injury/california"
    ],
    "fetchDetails": true,
    "fetchVcard": false,
    "freeConsultationOnly": false,
    "topRatedOnly": false,
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/justia-lawyer-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "search",
    "states": ["California"],
    "practiceAreas": ["Personal Injury"],
    "cities": [],
    "startUrls": ["https://lawyers.justia.com/lawyers/personal-injury/california"],
    "fetchDetails": True,
    "fetchVcard": False,
    "freeConsultationOnly": False,
    "topRatedOnly": False,
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/justia-lawyer-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "states": [
    "California"
  ],
  "practiceAreas": [
    "Personal Injury"
  ],
  "cities": [],
  "startUrls": [
    "https://lawyers.justia.com/lawyers/personal-injury/california"
  ],
  "fetchDetails": true,
  "fetchVcard": false,
  "freeConsultationOnly": false,
  "topRatedOnly": false,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call abotapi/justia-lawyer-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/justia-lawyer-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Justia Lawyer Profiles, Reviews & Office Locations Scraper",
        "description": "Scrape attorney profiles from the Justia Lawyer Directory. Extract names, contacts, office locations, practice areas, education, experience, awards, bar admissions, peer endorsements and Justia Lawyer Ratings. Search by state, city, practice area or scrape profile URLs.",
        "version": "1.1",
        "x-build-id": "csONvb42g6IM0J39p"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/abotapi~justia-lawyer-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-abotapi-justia-lawyer-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/abotapi~justia-lawyer-scraper/runs": {
            "post": {
                "operationId": "runs-sync-abotapi-justia-lawyer-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/abotapi~justia-lawyer-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-abotapi-justia-lawyer-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "mode"
                ],
                "properties": {
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "search",
                            "url"
                        ],
                        "type": "string",
                        "description": "How to start the scrape. 'search' builds a directory URL from the state(s), practice area(s), and city below. 'url' takes the exact Justia directory or profile URLs you paste.",
                        "default": "search"
                    },
                    "states": {
                        "title": "State(s)",
                        "type": "array",
                        "description": "One or more US states (full name like 'California' or 2-letter code like 'CA'). Leave empty for all states. Applied as a search filter in search mode only.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "practiceAreas": {
                        "title": "Practice area(s)",
                        "type": "array",
                        "description": "One or more practice areas (e.g. 'Personal Injury', 'Criminal Law', 'Family Law'). 'All' or empty means no area filter. Applied as a search filter in search mode only.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "cities": {
                        "title": "City(s)",
                        "type": "array",
                        "description": "Optional city filter (e.g. 'San Francisco'). Combines with each state in search mode. Leave empty to scrape the entire state.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "startUrls": {
                        "title": "Justia directory or profile URLs",
                        "type": "array",
                        "description": "Paste one or more Justia URLs (directory listing like https://lawyers.justia.com/lawyers/personal-injury/california, category, or attorney profile like https://lawyers.justia.com/lawyer/christopher-adamson-10198). Multi-URL supported. Filter fields above are ignored in URL mode. If a URL already includes a page number, results continue from that page.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "fetchDetails": {
                        "title": "Fetch full profile details (richer records)",
                        "type": "boolean",
                        "description": "Open each attorney profile page to add badges, biography, practice areas with sub-specialties, multiple offices with weekly hours, education, experience, awards, professional associations, jurisdictions, languages, certifications, speaking engagements, social profiles, peer endorsements, and the Justia Lawyer Rating breakdown. Adds one HTTP request per attorney.",
                        "default": true
                    },
                    "fetchVcard": {
                        "title": "Fetch vCard (structured contact data)",
                        "type": "boolean",
                        "description": "Download the .vcf file each profile exposes to add a structured vCard block: name parts, fax, organization, structured address, URLs. Adds one small HTTP request per attorney. Off by default (the HTML page already covers most fields).",
                        "default": false
                    },
                    "minRating": {
                        "title": "Min Justia rating",
                        "minimum": 0,
                        "maximum": 10,
                        "type": "number",
                        "description": "Only keep attorneys with at least this Justia Lawyer Rating (0 to 10 scale)."
                    },
                    "freeConsultationOnly": {
                        "title": "Free consultation only",
                        "type": "boolean",
                        "description": "Only keep attorneys who advertise a free consultation.",
                        "default": false
                    },
                    "topRatedOnly": {
                        "title": "Top-rated only",
                        "type": "boolean",
                        "description": "Only keep attorneys with the maximum Justia Lawyer Rating (10.0).",
                        "default": false
                    },
                    "maxPages": {
                        "title": "Max pages per list",
                        "minimum": 0,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "How many result pages to read per directory URL. Does NOT cap the run. Leave empty (or 0) for unlimited pages. The run is bounded by Max results, which is the cap."
                    },
                    "maxItems": {
                        "title": "Max results",
                        "minimum": 0,
                        "type": "integer",
                        "description": "THE cap for the run: stop after this many attorney records (default 20). Set to 0 for unlimited.",
                        "default": 20
                    },
                    "proxy": {
                        "title": "Proxy",
                        "type": "object",
                        "description": "Network connection. Leave this at the default (Residential, US) unless you have a specific reason to change it. Residential requires a paid Apify plan.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ],
                            "apifyProxyCountry": "US"
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
