FINRA BrokerCheck Scraper
Pricing
from $2.80 / 1,000 result row emitteds
FINRA BrokerCheck Scraper
Turn FINRA BrokerCheck name and CRD lookups into a bulk, disclosure-merged dataset — registration status, employment history, and disciplinary records for brokers and firms, straight from FINRA's public API.
Pricing
from $2.80 / 1,000 result row emitteds
Rating
0.0
(0)
Developer
DevilScrapes
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 hours ago
Last modified
Categories
Share
FINRA BrokerCheck Scraper — Bulk Broker & Firm Lookups
We do the dirty work so your dataset stays clean. 😈
$3.00 / 1,000 results — pay only for results, no credit card to try. Look up FINRA-registered brokers and brokerage firms by name or CRD number, in bulk, and get back one merged row per hit — registration status, employment history, and disciplinary disclosures included.
FINRA's public BrokerCheck site is built for one lookup at a time. This Actor takes a list of names or CRD numbers, resolves each search hit against the CRD detail/report endpoint, and merges the disclosure, employment, and registration-history data into a single structured row — turning a manual per-broker chore into a bulk dataset your compliance or due-diligence pipeline can consume directly.
🎯 What this scrapes
FINRA BrokerCheck's public, keyless JSON API (api.brokercheck.finra.org) — the same data that backs brokercheck.finra.org, structured for bulk use:
- Individual brokers — name search or direct CRD lookup, with registration scope, disclosure flag, employment history, registered states, and registered SROs.
- Brokerage firms — name search or direct CRD lookup, with disclosure flag and firm-level detail.
- Disclosure merge (the differentiator) — every hit's disciplinary disclosures, prior/current employments, and registration counts are pulled from the CRD detail endpoint and merged into the same row as the search hit, instead of leaving you to chase a second lookup per name.
| Field | Type | Description |
|---|---|---|
sourceType | string | individual or firm |
crdNumber | string | FINRA's stable CRD identifier |
queryMatched | string | The input query that produced this row |
name | string | Broker or firm name |
hasDisclosure | boolean | True when a disciplinary disclosure is on file |
disclosures | array | null | Disciplinary disclosure records |
currentEmployments / previousEmployments | array | null | Employer history |
registeredStates / registeredSROs | array | null | Registration records |
fetchedAt | string | ISO 8601 UTC timestamp |
🔥 Features
- Bulk name-or-CRD input — mix plain-text names and raw CRD numbers in the same
querieslist; a purely numeric entry is fetched directly, everything else is searched. - Disclosure merge, not just a search hit — every row can carry the full disciplinary, employment, and registration picture from the CRD detail endpoint, not just the thin search-result summary most lookups stop at.
- Pydantic v2 validation — input and output are model-validated; a malformed input fails fast before any request goes out.
- We rotate browser fingerprints —
curl-cffiimpersonation rotates across Chrome, Firefox, and Safari profiles per request. - We retry with exponential backoff on
408 / 429 / 503and honourRetry-After, up to 5 attempts per request. - A search that finds nothing still succeeds — an unmatched name or invalid CRD finishes the run cleanly with zero rows for that query and a status message explaining what was searched, never a false failure.
- You pay only for results that land — no data, no charge beyond the flat per-run start fee.
💡 Use cases
- Registered-rep due diligence — before onboarding a broker or referral partner, pull their full registration and disclosure history in one call instead of a manual BrokerCheck lookup.
- Compliance backfill — bulk-check a roster of reps or a list of firm CRDs against current registration status and disclosure flags.
- Background-check pipelines — feed
hasDisclosureanddisclosuresstraight into an existing KYC or vendor-vetting workflow. - Firm monitoring — track a watchlist of brokerage firm CRDs for new disclosures between scheduled runs.
- Recruiting and M&A screening — check a candidate roster or an acquisition target's registered reps before signing.
⚙️ How to use it
- Open the Actor input form.
- Pick Search type —
individualorfirm. It applies to every entry in your query list. - Fill Queries with names to search and/or raw CRD numbers to fetch directly — mix both in the same list.
- (Optional) Set Max results per query — how many search hits to keep per name (CRD lookups always return at most one row).
- Leave Fetch full detail on to merge disclosures, employment, and registration history into every row.
- Click Start. Results stream into the default dataset.
Quick examples
Mixed CRD + name lookup (the QA fixture):
{"searchType": "individual","queries": ["5998211", "john smith"],"maxResultsPerQuery": 5,"fetchFullDetail": true}
Firm search only, search-hit summary (no detail merge):
{"searchType": "firm","queries": ["Vanguard", "Fidelity"],"maxResultsPerQuery": 10,"fetchFullDetail": false}
📥 Input
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
searchType | string (individual | firm) | yes | — | Index to search for non-numeric queries |
queries | array of string | yes | — | Names to search or raw CRD numbers to fetch directly |
maxResultsPerQuery | integer | no | 20 | Cap on search hits fetched per name query (1-100) |
fetchFullDetail | boolean | no | true | Merge CRD detail — disclosures, employment, registration history |
proxyConfiguration | object | no | {"useApifyProxy": true} | Apify Proxy configuration |
📤 Output
One dataset row per hit. Real record from a live run — CRD 5998211, direct CRD lookup with fetchFullDetail: true (disclosure and employment arrays trimmed here for length; the live dataset row carries all of them):
{"sourceType": "individual","crdNumber": "5998211","queryMatched": "5998211","name": "John Smith","otherNames": ["JAKE SMITH", "JOHN SMITH", "John J Smith", "John Jacob Lakin Smith"],"scope": "InActive","iaScope": "InActive","hasDisclosure": true,"approvedRegistrationCount": null,"employmentsCount": null,"industryEntryDate": null,"currentEmployments": [],"previousEmployments": [{ "firmId": 628, "firmName": "FIFTH THIRD SECURITIES, INC.", "city": "EVERGREEN PARK", "state": "IL" }],"registeredStates": [],"registeredSROs": [],"examsCount": { "stateExamCount": 3, "principalExamCount": 0, "productExamCount": 4 },"registrationCount": { "approvedSRORegistrationCount": 0, "approvedFinraRegistrationCount": 0, "approvedStateRegistrationCount": 0 },"disclosures": [{"eventDate": "2/15/2023","disclosureType": "Financial","disclosureResolution": "Final","disclosureDetail": { "Type": "Compromise", "Disposition": "Satisfied/Released" }}],"fetchedAt": "2026-09-19T08:57:30.725036+00:00"}
A name-search row (e.g. queries: ["john smith"]) additionally populates approvedRegistrationCount, employmentsCount, and industryEntryDate from the search hit itself — those three fields only come from the search index, not the CRD-direct detail path shown above.
Download the dataset as JSON, CSV, Excel, or XML from the Export button on the run page.
💰 Pricing
Pay-Per-Event (PPE) — you pay only for results that land:
| Event | Rate (USD) | Trigger |
|---|---|---|
actor-start | $0.20 | Once per Actor run at boot |
result-emitted | $0.0028 | Per validated broker/firm row pushed |
Per-1,000-row extrapolation: ~$3.00 — bulk name/CRD lookups merged with disclosures, employment, and registration history in one call, on pay-per-result pricing.
No credit card required to try — Apify gives new accounts free test credits.
🚧 Limitations
- Public BrokerCheck data only — no SEC IAPD (Investment Adviser Public Disclosure) merge in this version; that is a distinct dataset and detail shape.
- No PDF disclosure reports — structured JSON fields only, not FINRA's rendered PDF report.
- Disclosure detail varies by type — the
disclosureDetailobject is passed through as-is since its shape differs across disclosure types (financial, regulatory, customer dispute, criminal); we do not force it into a fixed schema. - Branch offices stay nested —
branchOfficeLocationsships insidecurrentEmployments, not as separate rows. - No historical tracking — every run is a fresh lookup. Schedule runs and diff your own exports to track changes over time.
❓ FAQ
Is this legal? Yes — this Actor only calls FINRA BrokerCheck's own public, keyless API, the same one that backs the public BrokerCheck website. No login, no scraping of gated content.
What's the difference between this and a plain BrokerCheck search? A plain search gives you a thin hit — name, CRD, and a disclosure flag. This Actor resolves that hit against the CRD detail endpoint and merges the full disclosure, employment, and registration-history picture into the same row, so you get one bulk, analysis-ready dataset instead of a manual follow-up lookup per name.
What happens if a name doesn't match anything, or a CRD is invalid? The run finishes successfully with zero rows for that query and a status message describing what was searched — an unmatched search is not a failure.
Can I look up a firm and its individual reps in the same run?
Not in one call — searchType applies to the whole queries list. Run the Actor twice (once per type) if you need both.
Does turning off "Fetch full detail" save money? Yes — with it off, each row carries only the search-hit summary (no employment/registration/disclosure merge), and no detail-endpoint call is made per hit.
💬 Your feedback
Found a field FINRA changed, or need SEC IAPD data merged in too? Open an issue on the Actor's Apify Store page or contact us at apify.com/DevilScrapes.