FINRA BrokerCheck Scraper avatar

FINRA BrokerCheck Scraper

Pricing

from $2.80 / 1,000 result row emitteds

Go to Apify Store
FINRA BrokerCheck Scraper

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

DevilScrapes

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

7 hours ago

Last modified

Categories

Share

FINRA BrokerCheck Scraper

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.
FieldTypeDescription
sourceTypestringindividual or firm
crdNumberstringFINRA's stable CRD identifier
queryMatchedstringThe input query that produced this row
namestringBroker or firm name
hasDisclosurebooleanTrue when a disciplinary disclosure is on file
disclosuresarray | nullDisciplinary disclosure records
currentEmployments / previousEmploymentsarray | nullEmployer history
registeredStates / registeredSROsarray | nullRegistration records
fetchedAtstringISO 8601 UTC timestamp

🔥 Features

  • Bulk name-or-CRD input — mix plain-text names and raw CRD numbers in the same queries list; 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 fingerprintscurl-cffi impersonation rotates across Chrome, Firefox, and Safari profiles per request.
  • We retry with exponential backoff on 408 / 429 / 503 and honour Retry-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 hasDisclosure and disclosures straight 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

  1. Open the Actor input form.
  2. Pick Search typeindividual or firm. It applies to every entry in your query list.
  3. Fill Queries with names to search and/or raw CRD numbers to fetch directly — mix both in the same list.
  4. (Optional) Set Max results per query — how many search hits to keep per name (CRD lookups always return at most one row).
  5. Leave Fetch full detail on to merge disclosures, employment, and registration history into every row.
  6. 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

FieldTypeRequiredDefaultDescription
searchTypestring (individual | firm)yesIndex to search for non-numeric queries
queriesarray of stringyesNames to search or raw CRD numbers to fetch directly
maxResultsPerQueryintegerno20Cap on search hits fetched per name query (1-100)
fetchFullDetailbooleannotrueMerge CRD detail — disclosures, employment, registration history
proxyConfigurationobjectno{"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:

EventRate (USD)Trigger
actor-start$0.20Once per Actor run at boot
result-emitted$0.0028Per 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 disclosureDetail object 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 nestedbranchOfficeLocations ships inside currentEmployments, 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.