# IRS Tax-Exempt Organizations Scraper (US) (`knotty_mistveil/irs-exempt-organizations`) Actor

Every US tax-exempt nonprofit from the IRS Exempt Organizations Business Master File (EO BMF) as flat JSON: EIN, subsection, NTEE, ruling date, 990 financials.

- **URL**: https://apify.com/knotty\_mistveil/irs-exempt-organizations.md
- **Developed by:** [Jason](https://apify.com/knotty_mistveil) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 organization returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## IRS Tax-Exempt Organizations Scraper (US)

Pulls every US tax-exempt organization from the **IRS Exempt Organizations Business Master File (EO BMF)**, the IRS's monthly cumulative extract of all organizations recognised as tax-exempt, published as public-domain regional CSV files. No API key, no account, no browser automation. The Actor **streams** the regional files row by row - a single file runs to ~165 MB - and never buffers one whole, so it filters ~1.96 million organizations client-side and delivers one flat JSON record per organization: the EIN, name and address, the 501(c) subsection (mapped to a plain-English label), the NTEE activity code and its major group, the IRS ruling date, the deductibility, foundation and status codes, and the asset, income and Form 990 revenue amounts from the organization's latest return. Filter by region, state, subsection, NTEE prefix, ruling-date window, minimum income and name keyword. On the September 2026 extract (files dated 7 September 2026) the file held **1,964,958 organizations**.

### What you get

Every record contains:

| Field | Description |
| --- | --- |
| `id` | The organization's EIN (Employer Identification Number), nine digits with leading zeros, e.g. `134038907` - same value as `ein` |
| `source` | `irs-eo-bmf`, constant |
| `url` | `https://apps.irs.gov/app/eos/` - the IRS Tax Exempt Organization Search (TEOS) tool; look the record up by its `ein` (see *Limits and freshness* for why this is not a per-EIN deep link) |
| `ein` | Employer Identification Number, nine digits |
| `name` | Primary name of the organization |
| `inCareOf` | In-care-of name (officer/contact the IRS directs correspondence to); the leading `%` is stripped. `null` when blank. **May be an individual's name** - see *Data source and licence* |
| `street` / `city` / `state` / `zip` | Filing address. `state` is a two-letter US/territory code, or `null` for an international filer; `zip` is `null` when the source records an all-zero placeholder |
| `subsectionCode` | Two-digit 501(c) subsection code, e.g. `03`, `04`, `05` |
| `subsectionDescription` | Plain-English label for the subsection, e.g. `501(c)(3) Charitable, religious, educational, scientific or literary organization`. An unmapped code passes through as `501(c) subsection <code>` |
| `classification` | The IRS classification code(s), a four-character field (each non-zero digit is one classification), e.g. `2000` |
| `rulingDate` | Month the IRS recognised the exemption, as an ISO date fixed to the first of the month, `YYYY-MM-01`. `null` when the IRS records none. The source granularity is monthly |
| `deductibilityCode` | `1` = contributions deductible, `2` = not deductible, `4` = deductible by treaty (foreign) |
| `foundationCode` | Foundation-status code, e.g. `04` private non-operating foundation, `10` church, `15` publicly supported charity, `00` not a 501(c)(3) |
| `statusCode` | Exempt-organization status, `01` unconditional exemption, `02` conditional, `12` 4947(a)(2) trust, `25` terminating private foundation |
| `nteeCode` | National Taxonomy of Exempt Entities code, e.g. `Q22`, `P33`, `X20`. `null` when the source has none |
| `nteeDescription` | The NTEE major group (the code's first letter), e.g. `Human Services`, `Education`, `Religion-Related`. The full code stays in `nteeCode`; an unmapped first character passes through |
| `assetAmount` | Book-value total assets from the latest Form 990 series return, in USD, or `null` |
| `incomeAmount` | Income amount from the latest return, in USD (can be negative), or `null` |
| `revenueAmount` | Form 990 total revenue, in USD (can be negative), or `null` |
| `taxPeriod` | Tax period of the latest return filed, `YYYY-MM`, or `null` when none is on file |
| `filingRequirement` | Filing-requirement code: `01` files 990/990-EZ, `02` files 990-N (income under $50k), `06` church (not required), `00` not required (all other), and others |
| `fetchedAt` | ISO timestamp of the run that produced the record |

The dataset ships three views: **Overview** (EIN, name, subsection, NTEE, location, ruling date), **Financials** (income, revenue, assets, tax period, filing requirement) and **Classification** (subsection, classification, foundation, deductibility, status, NTEE).

### Input

| Input | Type | Default | Description |
| --- | --- | --- | --- |
| `regions` | string\[] | `["eo4"]` | Which regional files to stream. `eo1` Northeast, `eo2` Mid-Atlantic & Great Lakes, `eo3` Gulf Coast & Pacific, `eo4` Other (international, Puerto Rico & territories), `eo_pr` Puerto Rico, `eo_xx` International. The default `eo4` is the smallest file (~4,900 orgs, ~0.9 MB), so a default run is cheap. `eo_pr` and `eo_xx` are the two halves of `eo4`; a run over overlapping regions is de-duplicated by EIN. |
| `states` | string\[] | all | Two-letter states/territories (e.g. `CA`, `NY`, `PR`), matched on the filing address on top of the region(s) you chose. |
| `subsections` | string\[] | all | 501(c) subsection codes. `3` = 501(c)(3) charities, `4` = social welfare, `5` = labor/agricultural, `6` = business leagues, `7` = social clubs, `19` = veterans. A one-digit code is accepted (`3` becomes `03`). |
| `nteePrefixes` | string\[] | all | NTEE code prefixes. A letter selects a major group (`B` education, `E` health, `L` housing, `P` human services, `T` philanthropy, `X` religion); a longer prefix narrows it (`B21`). An org is kept if its NTEE code starts with any prefix. |
| `ruledFrom` | string | - | Lower bound on the ruling date, `YYYY-MM` inclusive (monthly granularity). |
| `ruledTo` | string | - | Upper bound on the ruling date, `YYYY-MM` inclusive. |
| `minIncomeAmount` | integer | - | Keep only organizations whose reported income amount is at least this many USD. Organizations that filed no return, or reported no income, are dropped when this is set. |
| `keywords` | string\[] | none | Keep only organizations whose name contains one of these terms (case-insensitive, ORed). |
| `maxItems` | integer | `1000` | Stop after this many organizations. You are charged per organization delivered, so this is also the cost cap. Rows are read in EIN order within each region, so a value below the match count drops the later EINs. |

Example - Puerto Rico 501(c)(3) charities with at least $1M of income:

```json
{ "regions": ["eo_pr"], "subsections": ["3"], "minIncomeAmount": 1000000, "maxItems": 500 }
```

Example - every education nonprofit (NTEE major group B) in California, newest rulings first is not available (rows are EIN-ordered), so cap generously:

```json
{ "regions": ["eo3"], "states": ["CA"], "nteePrefixes": ["B"], "maxItems": 20000 }
```

Example - a full download of one region, headline fields only:

```json
{ "regions": ["eo1"], "maxItems": 2000000 }
```

Bad input fails fast: an unknown region, an unknown subsection code, a malformed state or a malformed `YYYY-MM` date stops the run with the validation message as its status, before any file is downloaded.

### Output example

A real record from a live run on 10 September 2026 (`{"regions": ["eo_pr"], "keywords": ["CIEE INC"], "maxItems": 1}`), unedited:

```json
{
  "id": "134038907",
  "source": "irs-eo-bmf",
  "url": "https://apps.irs.gov/app/eos/",
  "ein": "134038907",
  "name": "CIEE INC",
  "inCareOf": "LORI SANDS",
  "street": "6471 AVE ISLA VERDE APT 909",
  "city": "CAROLINA",
  "state": "PR",
  "zip": "00979-7121",
  "subsectionCode": "03",
  "subsectionDescription": "501(c)(3) Charitable, religious, educational, scientific or literary organization",
  "classification": "2000",
  "rulingDate": "1999-04-01",
  "deductibilityCode": "1",
  "foundationCode": "16",
  "statusCode": "01",
  "nteeCode": "Q22",
  "nteeDescription": "International, Foreign Affairs & National Security",
  "assetAmount": 207090335,
  "incomeAmount": 218142320,
  "revenueAmount": 218141529,
  "taxPeriod": "2025-08",
  "filingRequirement": "01",
  "fetchedAt": "2026-09-10T00:07:40.523Z"
}
```

A church with no filing requirement looks the same but carries `null` for `assetAmount`, `incomeAmount`, `revenueAmount` and `taxPeriod`, `foundationCode` `10`, `filingRequirement` `06` and `nteeDescription` `Religion-Related`. Every run also writes a `SUMMARY` record to the key-value store with the regions streamed, the filters used, how many rows each filter dropped, the per-region kept counts and whether `maxItems` (`limitReached`) or a download error (`streamError`) ended the run.

### Pricing

Pay per result: **US$0.002 per organization delivered** (US$2 per 1,000 organizations). You are charged only for records pushed to the dataset; a run that returns nothing costs nothing. Runs on the Apify free plan are limited by the platform's free-tier usage allowance.

### Data source and licence

- **Source.** IRS Exempt Organizations Business Master File (EO BMF), regional CSV extracts at `https://www.irs.gov/pub/irs-soi/eo1.csv` … `eo4.csv`, plus `eo_pr.csv` (Puerto Rico) and `eo_xx.csv` (International). Landing page and code definitions: <https://www.irs.gov/charities-non-profits/exempt-organizations-business-master-file-extract-eo-bmf> and the EO BMF information sheet <https://www.irs.gov/pub/foia/ig/tege/eo-info.pdf> (both read 10 September 2026). The endpoints are public and need no authentication.
- **Licence.** The EO BMF is prepared by IRS employees as part of their official duties, which makes it a **work of the U.S. Government**: not subject to domestic copyright under 17 U.S.C. § 105, and free to reuse commercially. The IRS publishes it for public download with no stated use restriction. Attribute as "Source: IRS Exempt Organizations Business Master File" and do not imply IRS endorsement.
- **Personal data.** The `inCareOf` field is the officer, director or contact the IRS lists for the organization, and is frequently an **individual's name** (e.g. `% MARIE ELIZABETH KAISER NAPOLI`); `name` itself is sometimes a person's name for a trust or family foundation. The IRS publishes these lawfully as part of a public register of exempt organizations. Downstream use is the buyer's responsibility under the applicable regime: US marketing by email is subject to **CAN-SPAM**, by phone/SMS to the **TCPA** and to **state Do-Not-Call** rules, and contacting from or into the EU/UK engages GDPR/PECR. The file carries no email addresses or phone numbers. Revoked organizations are removed by the IRS from this extract, and self-declared organizations, non-filing churches and split-interest trusts are not included.

### Limits and freshness

- **Update cadence.** The IRS regenerates the EO BMF **monthly**. On the extract measured here the six files were last modified 7 September 2026 and the landing page reported 1,964,958 organizations. Re-run monthly; there is no intra-month change to capture.
- **File sizes and volumes, measured 10 September 2026** by streaming each file with this Actor: `eo1` Northeast ~48.8 MB / **279,041** orgs; `eo2` Mid-Atlantic & Great Lakes ~126 MB / **721,547**; `eo3` Gulf Coast & Pacific ~165 MB / **959,426**; `eo4` Other ~0.9 MB / **4,943**; `eo_pr` Puerto Rico ~0.44 MB / **2,541**; `eo_xx` International ~0.42 MB / **2,403**. The three region files plus `eo4` sum to the 1,964,958 total; `eo4` is exactly Puerto Rico plus International, so selecting `eo4` together with `eo_pr` or `eo_xx` delivers each organization once (the repeats are counted under `filteredOut.duplicate`).
- **Speed, measured 10 September 2026** on this machine (download-bound): the 48.8 MB Northeast file streamed and filtered end-to-end in **8.7 s** (279,041 rows), and the 165 MB Gulf Coast & Pacific file in **21.6 s** (959,426 rows) - about 44,000 rows/second, dominated by the download. Delivery adds the cost of writing each kept record to the dataset. `maxItems` stops the run the moment the cap is filled and **cancels the rest of the download**, so a small `maxItems` over a big region does not pay for the whole file.
- **Rows are read in EIN order**, the order the IRS sorts each file, not by ruling date or income. A `maxItems` below the number of matches therefore keeps the **lowest** EINs and drops the rest; the run logs `maxItems (N) reached` and the `SUMMARY` record carries `limitReached: true`. Raise `maxItems` (the maximum is 2,000,000, above the whole file) for a complete pull.
- **All filters are applied by this Actor**, row by row, on the streamed data - the IRS publishes flat files with no query API - so `states`, `subsections`, `nteePrefixes`, `ruledFrom`/`ruledTo`, `minIncomeAmount` and `keywords` can be combined freely.
- **The record link is the TEOS search tool, not a per-EIN page.** The IRS Tax Exempt Organization Search is a client-rendered single-page app with no stable shareable per-organization URL (its former `detailsPage.action` deep link now returns 404), so `url` is the tool's entry point and the `ein` field is the lookup key. Paste the EIN into TEOS to open the organization.
- **Financial fields are the agency's, not the Actor's.** `assetAmount`, `incomeAmount`, `revenueAmount` and `taxPeriod` come from the organization's most recent Form 990 series return and are `null` when it has filed none or reported nothing - churches and small filers routinely have all four blank. `rulingDate` is `null` when the IRS records none. These are left `null` rather than guessed.
- **Transient IRS/Akamai blocks.** The static host occasionally answers a burst of requests with HTTP 403 or 503; the Actor retries these with exponential back-off. If a download still fails before any record is delivered the run fails with a clear message (re-run - it is transient); if it fails partway, the delivered records are kept and `SUMMARY.streamError` records what happened.

### Use cases

- **Nonprofit lead generation and market sizing:** pull every 501(c)(3) in a state with `incomeAmount` above a threshold, segmented by NTEE major group, to build a prospecting or grant-target list with the organization's name, address and financial scale already on the record.
- **Due diligence and donor/grant compliance:** confirm an organization's exempt status, foundation type and contribution deductibility by EIN before making or accepting a grant, and spot private foundations (`foundationCode` `02`/`03`/`04`) versus public charities.
- **Nonprofit-sector research and dashboards:** download whole regions and analyse the ~1.96 million organizations by subsection, NTEE, ruling year, state and reported assets/income, refreshed each month.

### Support

Open an issue on the Actor's Issues tab on Apify Store. Replies within 14 days, as the Store terms require. Bug reports that name the run id and the input used are fixed fastest.

# Actor input Schema

## `regions` (type: `array`):

Which regional EO BMF file(s) to stream. The four region files partition the whole country: Northeast, Mid-Atlantic & Great Lakes, Gulf Coast & Pacific, and Other (international, Puerto Rico and territories). Puerto Rico and International are the two slices of Other, published as their own smaller files. Default: Other - about 4,900 organizations in a 0.9 MB file, so a default run is cheap. Gulf Coast & Pacific is the biggest (about 959,000 orgs, ~165 MB).

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

Keep only organizations whose filing address is in one of these two-letter states or territories (e.g. CA, NY, TX, PR). Applied on top of the region you chose, so pick the region that contains the state. Empty = every state in the selected region(s).

## `subsections` (type: `array`):

Keep only these 501(c) subsection codes. 3 = 501(c)(3) charities, 4 = social welfare, 5 = labor/agricultural, 6 = business leagues, 7 = social clubs, 8 = fraternal, 19 = veterans. A one-digit code is accepted (3 becomes 03). Empty = all subsections.

## `nteePrefixes` (type: `array`):

Keep only organizations whose NTEE code starts with one of these prefixes. A single letter selects a major group: A = Arts, B = Education, E = Health Care, L = Housing, P = Human Services, T = Philanthropy, X = Religion. A longer prefix narrows it (B21 = preschool). An organization is kept if any of its NTEE code matches any prefix. Empty = all.

## `ruledFrom` (type: `string`):

Keep only organizations whose IRS ruling/determination date is on or after this month, YYYY-MM. The ruling date is published to monthly granularity. Empty = no lower bound.

## `ruledTo` (type: `string`):

Keep only organizations whose IRS ruling/determination date is on or before this month, YYYY-MM. Empty = no upper bound.

## `minIncomeAmount` (type: `integer`):

Keep only organizations whose reported income amount (from the most recent Form 990 series return) is at least this many US dollars. Organizations that have filed no return, or reported no income, are dropped when this is set. Empty = no income filter.

## `keywords` (type: `array`):

Keep only organizations whose primary name contains one of these terms (case-insensitive; terms are ORed). Empty = no name filter.

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

Stop after this many organizations. You are charged per organization delivered, so this is also the cost cap. Rows are read in EIN order within each region; a value below the number of matches drops the later EINs. Default 1000. The whole file holds about 1.96 million organizations.

## Actor input object example

```json
{
  "regions": [
    "eo4"
  ],
  "states": [],
  "subsections": [],
  "nteePrefixes": [],
  "keywords": [],
  "maxItems": 1000
}
```

# Actor output Schema

## `results` (type: `string`):

All organizations returned by the run, one JSON object each. Append ?format=csv or ?format=xlsx to download a spreadsheet.

## `summary` (type: `string`):

Counts, the regions streamed, the filters applied, how many rows each filter dropped and whether the maxItems cap was hit, written at the end of the run.

# 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 = {
    "regions": [
        "eo4"
    ],
    "states": [],
    "subsections": [],
    "nteePrefixes": [],
    "keywords": [],
    "maxItems": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("knotty_mistveil/irs-exempt-organizations").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 = {
    "regions": ["eo4"],
    "states": [],
    "subsections": [],
    "nteePrefixes": [],
    "keywords": [],
    "maxItems": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("knotty_mistveil/irs-exempt-organizations").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 '{
  "regions": [
    "eo4"
  ],
  "states": [],
  "subsections": [],
  "nteePrefixes": [],
  "keywords": [],
  "maxItems": 1000
}' |
apify call knotty_mistveil/irs-exempt-organizations --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,knotty_mistveil/irs-exempt-organizations"
        }
    }
}
```

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/Ly9MgLUfUT1cp65id/builds/UbShEjLkjJvKpL3Ca/openapi.json
