# NFA BASIC Registrant Search (`automation-lab/nfa-basic-registrant-search`) Actor

Search public NFA BASIC firms by name and export registration, membership, location, profile history and regulatory-action indexes for derivatives compliance screening.

- **URL**: https://apify.com/automation-lab/nfa-basic-registrant-search.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.62 / 1,000 firm snapshots

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## NFA BASIC Registrant Search

Export **NFA BASIC registrant records** for recurring derivatives-firm registration and regulatory-action screening. Supply firm names and receive deduplicated public NFA identifiers, membership status, current registration types, available public location, profile history and regulatory-action index cases.

This Actor is for compliance operations, counterparty onboarding and derivatives-market researchers who need repeatable firm snapshots rather than manual copying. It is independently operated by automation-lab and is not affiliated with or endorsed by NFA or CFTC.

### What does it collect?

The Actor searches NFA BASIC's public firm-name surface and follows public profile and regulatory-index records. One default-dataset row represents one unique NFA ID. Names can be ambiguous: several firms or historical entities can have similar names. Review identifiers before joining results to your own counterparties.

It does not search individuals, enumerate the full registry, apply advanced registration/membership/action filters, retrieve pool or principal details, or download case documents. Source-classified individual records returned within firm searches are excluded. Historic firm entities can lack a location or current registration.

### Who is it for?

Compliance analysts can refresh known derivatives counterparties; onboarding teams can retain public registration snapshots; researchers can export firm regulatory-action indexes. This is a public-record collection tool, not automated risk scoring.

### Why use it?

- Refresh a supplied firm watchlist with stable NFA identifiers.
- Keep registration and membership text together with the underlying public profile link.
- Export profile-history events and regulator/exchange case indexes in the same firm row.
- Compare dated snapshots in your own database without treating an empty action list as clearance.
- Avoid paying a separate event charge for nested history or action entries.

### Getting started

1. Enter one or more firm names, for example `Morgan Stanley`.
2. Set the global maximum number of unique firms, initially 10.
3. Run the Actor and inspect the returned firm names and NFA IDs.
4. Download JSON for nested history/action arrays, or CSV/Excel for a summary.
5. Verify material findings on the public profile before taking action.

Example input:

```json
{
  "firmNames": ["Morgan Stanley"],
  "maxItems": 10
}
```

### Input parameters

| Field | Meaning |
|---|---|
| `firmNames` | Required array of 1–50 names, each 2–150 characters after trimming. Queries execute in supplied order. |
| `maxItems` | Global unique-firm limit, default 10, allowed 1–1000. Zero and unlimited are unsupported. |

NFA's own matching rules are used; this is not guaranteed exact whole-name equality. The Actor trims whitespace but does not impose extra local case, word-boundary or substring filtering. Multiple queries mean the union of results, with deduplication by NFA ID. The first query to find an ID is recorded as `searchQuery`.

Results page in ascending firm-name order, 10 upstream rows at a time. Deduplication and individual exclusions occur before the global limit. When that limit is reached, later queries are not processed. Billing limits or source exhaustion can also end collection earlier. Unsupported input fields fail validation rather than silently enabling a filter.

### Extracted data

| Field | Description |
|---|---|
| `nfaId` | Public identifier as a string, including leading zeros. |
| `firmName` | Name returned by firm search. |
| `searchQuery` | First submitted name query yielding the unique ID. |
| `membershipStatus` | Profile membership banner or search status; nullable. |
| `registrationTypes` | Current public registration types; empty when none reported. |
| `location` | Public street, city, state/province, postal code and country; nullable. |
| `registrationHistory` | Profile category, event description and effective date. |
| `regulatoryActions` | Contributor, case reference, effective date, actions and outcome. |
| `hasRegulatoryActions` | Whether the fetched index contains entries, not a compliance verdict. |
| `profileUrl` | BASIC verification link. |
| `scrapedAt` | UTC observation timestamp. |

Source event dates are preserved, normally in `MM/DD/YYYY`. Missing values are null or empty arrays, not invented status values. No phone numbers, individual biographies, pool records or officers are exported. HTML list markup in case summaries is removed.

### Example output

This abbreviated public firm snapshot illustrates actual returned fields. History contains more events in the full output.

```json
{
  "nfaId": "0181917",
  "firmName": "MORGAN STANLEY",
  "searchQuery": "Morgan Stanley",
  "membershipStatus": "Not an NFA Member",
  "registrationTypes": [],
  "location": null,
  "registrationHistory": [
    {
      "category": "PRN",
      "description": "PRINCIPAL APPROVED",
      "effectiveDate": "02/11/1981"
    }
  ],
  "regulatoryActions": [
    {
      "contributor": "CME",
      "caseNumber": "0525458CTRA",
      "effectiveDate": "06/27/2005",
      "actions": "FLOOR RECORDKEEPING",
      "outcome": "FINE-SUMMARY $1,000"
    }
  ],
  "hasRegulatoryActions": true,
  "profileUrl": "https://www.nfa.futures.org/basicnet/basic-profile.aspx?nfaid=0181917",
  "scrapedAt": "2026-10-09T14:08:11.597Z"
}
```

### How much does it cost to search NFA BASIC firms?

Pay per event: **$0.02 per run start** plus one charge per unique firm snapshot. Nested history and regulatory-action indexes have no separate event charge. Empty searches have only the start charge; invalid input is rejected before charging the start event.

| Apify Store spend tier | Price per firm |
|---|---:|
| FREE | $0.008855 |
| BRONZE | $0.0077 |
| SILVER | $0.006006 |
| GOLD | $0.00462 |
| PLATINUM | $0.00462 |
| DIAMOND | $0.00462 |

Spend tiers depend on qualifying aggregate monthly Apify Store spend, not a private per-Actor volume discount.

BRONZE examples: 1 firm costs about **$0.0277**, 10 firms **$0.097**, and 100 firms **$0.79** in Actor event charges. These are estimates, not guaranteed invoices; consult the active Store pricing and your Apify plan. No paid proxy or external API account is required. Revenue/payout estimates may be adjusted for refunds, fraud, disputes, taxes, corrections and clawbacks.

### Integrations and recurring workflows

Use Apify scheduling to rerun a stable watchlist and send results to a spreadsheet, warehouse, n8n or Make. Join on `nfaId`, retain `scrapedAt`, and compare membership, registration types and case references against the preceding snapshot. Store full exports outside your language-model context.

The Actor itself does not maintain historical snapshots, calculate changes, send alerts or make screening decisions. Scheduling, retention and review belong to your downstream workflow.

### API usage

Use your own Apify API token in an environment variable. Do not paste it into shared examples or datasets.

```bash
curl -X POST \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"firmNames":["Morgan Stanley"],"maxItems":10}' \
  'https://api.apify.com/v2/acts/automation-lab~nfa-basic-registrant-search/runs'
```

The response contains run and storage IDs, not firm rows. Follow that same run to terminal status, then fetch bounded dataset items.

JavaScript:

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/nfa-basic-registrant-search')
  .call({ firmNames: ['Morgan Stanley'], maxItems: 10 });
if (run.status !== 'SUCCEEDED') throw new Error(`Run ${run.id}: ${run.status}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 10 });
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/nfa-basic-registrant-search').call(
    run_input={'firmNames': ['Morgan Stanley'], 'maxItems': 10})
if run['status'] != 'SUCCEEDED':
    raise RuntimeError(run['status'])
items = client.dataset(run['defaultDatasetId']).list_items(limit=10).items
print(items)
```

### MCP access

For compatible clients, select a stable Actor subset:

```bash
claude mcp add --transport http apify \
  'https://mcp.apify.com?tools=automation-lab/nfa-basic-registrant-search'
```

Claude Desktop, Cursor and VS Code can use the equivalent client configuration below when their MCP implementation supports remote HTTP:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/nfa-basic-registrant-search"
    }
  }
}
```

Discover actual names and input schemas with scoped `tools/list`. Selection also adds run/data helper tools; it does not expose exactly one tool. Authenticate as required by your client. Discovery does not grant execution permission.

Example prompts: “Collect up to 5 public firm snapshots for Morgan Stanley.” “Show the NFA IDs and indexed actions from the completed run without a legal verdict.”

Example authorized prompt: “Search Morgan Stanley in NFA BASIC, maximum 10 unique firms; show identifiers, registration and indexed regulatory actions, without making a compliance verdict.”

Start once and retain the returned run ID, status and dataset ID. `call-actor` waits at most 45 seconds; IDs are metadata, not results. If pending, check **the same run** using `get-actor-run`, with bounded 2/4/8-second backoff capped at 10 seconds and a 120-second total deadline. Set the client timeout above the requested wait plus transport margin. On timeout, recover a known run instead of starting another; report uncertainty if its ID is unknown. At deadline report pending and stop. Failed-run rows are partial.

On success, use `get-dataset-items` in pages of 20 source rows, tracking offsets. Example consumer budget: at most 100 rows or 64 KiB of serialized UTF-8 content admitted to model context, whichever comes first. Enforce bytes host-side, including oversized rows; disclose omission/truncation and the next offset. If your client cannot intercept oversized responses, do not claim a hard byte guarantee. Keep full downloads outside model context. Discovery/caching behavior depends on the client; no numeric token-saving claim is made.

### Reliability and limitations

Requests use a fresh anonymous landing session, a bounded in-memory cookie jar and sequential JSON requests. Transient network/429/5xx failures retry at most twice. An initial 403 renews the session once. No unmeasured residential/browser fallback is enabled.

A challenge, rejected RPC, malformed page, mismatched profile or exhausted upstream error fails the run rather than masquerading as an empty search. Already produced rows in failed runs are partial. The NFA source may change, restrict access or omit older data. Search results are not a guaranteed exhaustive registry or legal record.

### Legality and responsible use

Use only public information within applicable law and source/platform rules. The Actor does not grant redistribution rights or replace professional advice.

### Data handling

Use public firm data for legitimate research and screening, and verify important findings directly. No legal, regulatory or investment advice is provided. Absence of indexed actions does not mean approval, clearance or absence of misconduct. Apify's standard user terms apply.

The Actor uses no AI in extraction or output generation. Firm-name input is sent to NFA to perform the lookup. Public firm records are stored in your Apify run dataset; storage/log retention follows your Apify settings, and you control deletion. Session cookies are ephemeral and are never output or intentionally persisted. Public firm names may include personal names; handle exports appropriately.

Failed operations send sanitized diagnostic input, exceptions and actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days. Submitted firm names can remain in sanitized input, so do not submit private annotations or secrets. Runtime logs contain counts and safe failure context, not cookies or raw profiles.

### FAQ and troubleshooting

**Why do multiple firms share a name?** NFA can retain historical entities or several registrations with similar names. Identify the correct counterparty by NFA ID and public profile.

**Why is a location missing?** Some historical profiles have no public address. Null means absent, not a failed geocoding guess.

**Why are fewer firms returned than the limit?** The limit is a maximum. The source may exhaust, IDs may duplicate, individual-classified records may be excluded or a billing limit may stop collection.

**Why did the run fail instead of returning no matches?** Upstream challenge/rejection and unexpected data shapes are errors. A recognized empty search succeeds with zero rows. Check logs and retry later only after checking source availability.

**Can I request individual, pool, principal or advanced-filter enumeration?** Not in this version. Those surfaces are deliberately excluded rather than silently advertised.

### Related Actors and support

For a different regulatory corpus, use [SEC Investment Adviser Public Disclosure Scraper](https://apify.com/automation-lab/sec-investment-adviser-public-disclosure-scraper). SEC investment-adviser records do not replace NFA derivatives registrations.

Report reproducible problems through this Actor's Apify Issues tab. Include a safe firm-name query, run link and the expected public record; never include credentials or confidential counterparties.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/nfa-basic-registrant-search/changelog.md

# Actor input Schema

## `firmNames` (type: `array`):

One to 50 firm-name queries, trimmed before submission. NFA's own name-search matching is used (not guaranteed exact equality); review returned names. Queries run in supplied order, equivalent NFA IDs are emitted only once across the run. No individual search, blank enumeration or advanced filters.

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

Global maximum of unique firm records across all queries, after NFA-ID deduplication. Default 10; 1–1000; zero/unlimited is not supported. Results page in name order, 10 per page. A limit can prevent later queries from running. Source exhaustion or a billing limit can end collection earlier.

## Actor input object example

```json
{
  "firmNames": [
    "Morgan Stanley"
  ],
  "maxItems": 10
}
```

# Actor output Schema

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

One row per unique public NFA firm ID, including nested history and regulatory indexes.

# 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 = {
    "firmNames": [
        "Morgan Stanley"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/nfa-basic-registrant-search").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 = {
    "firmNames": ["Morgan Stanley"],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/nfa-basic-registrant-search").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "firmNames": [
    "Morgan Stanley"
  ],
  "maxItems": 10
}' |
apify call automation-lab/nfa-basic-registrant-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/nfa-basic-registrant-search"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/gXDTHgtrWccQAoNIA/builds/xqlGRrIIM1ZotignA/openapi.json
