# IRS Private Foundation 990-PF Filings Export (`automation-lab/irs-private-foundation-990pf`) Actor

Export financials, grants and officers from official IRS 990-PF XML filing batches by publication year and optional EIN filter.

- **URL**: https://apify.com/automation-lab/irs-private-foundation-990pf.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 $3.07 / 1,000 item extracteds

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

## IRS Private Foundation 990-PF Filings Export

Export financial and grant fields from official IRS private foundation 990-PF filings. Select **one IRS XML publication year and ZIP batch**; optionally restrict output to nine-digit EINs. Each dataset row is one filing with assets, net investment income, grants paid, selected grant details, officer details, and the source archive. The archive is downloaded once per run and XML is parsed locally.

### Who is it for?

- Foundation researchers comparing filing-level asset and grant figures across repeated exports.
- Grantmaking analysts inspecting disclosed grant purposes and amounts for foundations in a selected IRS archive batch.
- Data engineers loading IRS filing fields into a spreadsheet or warehouse for longitudinal analysis.

This is a **filing export**, not an organization identity/status search. For IRS tax-exempt organization master-file identification and status, use [IRS Tax Exempt Organization Search](https://apify.com/automation-lab/tax-exempt-organization-search).

### Why use the IRS XML archives?

The source is the IRS's published XML, rather than a third-party summary. Every row carries a stable IRS object ID and an archive URL for provenance. The dataset is structured JSON, so you can join filings by EIN and tax period, import the rows into a spreadsheet, and compare values over time. The IRS **publication year** can differ from the filing's **tax period**; both are recorded.

### What data is exported?

| Field | Meaning |
| --- | --- |
| `objectId`, `ein` | IRS filing object ID and foundation EIN. |
| `foundationName`, `taxPeriod`, `publicationYear` | Filing header name, tax period end, archive publication year. |
| `totalAssetsEndOfYear`, `netInvestmentIncome`, `grantsPaid` | Monetary fields in USD when present in XML; otherwise `null`. |
| `grants[]` | Available grant purpose, recipient and amount. Recipient may be `null`. |
| `officers[]` | Available officer name, title and compensation. |
| `sourceArchive`, `sourceXml` | Official ZIP URL and member identifier (`#..._public.xml` is an archive-member reference, not an independently downloadable URL). |

The Actor does **not** export every field of Form 990-PF, all attached schedules, or an Excel workbook. The dataset is available through Apify as JSON/CSV/Excel; nested arrays may require flattening in a spreadsheet workflow.

### Get started

1. Find an IRS [XML batch archive](https://apps.irs.gov/pub/epostcard/990/xml/) for the year you want to analyze.
2. Enter the archive's three-character batch code (for example, `01A`) and publication year.
3. Optionally enter up to 100 EINs (nine digits without hyphens) to filter **within that batch**.
4. Set `maxItems` between 1 and 100; run the Actor and inspect the default dataset. Repeat for another batch or publication year when needed.

A selected batch is downloaded in full even for `maxItems: 1`; choosing an EIN absent from that batch yields no rows. This Actor does not find which batch contains an arbitrary EIN. The IRS CSV index does not map EINs to ZIP batches.

### Choosing a batch for repeatable comparisons

Archive names follow `YEAR_TEOS_XML_BATCH.zip`, for example `2023_TEOS_XML_01A.zip`. The Actor constructs the official IRS URL from the two input fields; do not supply a third-party download URL. To compare a foundation across releases, export the relevant known batches separately, then key rows by `objectId` and review `taxPeriod`. Re-running a fixed batch may reflect an IRS replacement of the underlying archive, so store a snapshot if reproducibility matters.

### Input

```json
{"publicationYear":2023,"batch":"01A","eins":["363781852"],"maxItems":1}
```

| Parameter | Default | Notes |
| --- | --- | --- |
| `publicationYear` | `2023` | IRS archive publication year, 2017–2025; not tax year. |
| `batch` | `01A` | Exact two-digit, one-uppercase-letter ZIP code from the selected year. Nonexistent archives fail with an HTTP error. |
| `eins` | omitted | Up to 100 nine-digit strings; omit to export from the selected archive without EIN filtering. |
| `maxItems` | `5` | Stop after 1–100 qualifying 990-PF filing rows. Multiple filings can share an EIN. |

### Output example

A real filing from the IRS 2023 `01A` XML batch (values are illustrative of that filing, not a guarantee of current archive availability):

```json
{
  "objectId": "202300109349100000",
  "ein": "363781852",
  "publicationYear": 2023,
  "taxPeriod": "2022-06-30",
  "foundationName": "JOSEPH C BELDEN FOUNDATION",
  "totalAssetsEndOfYear": 1214045,
  "netInvestmentIncome": 102063,
  "grantsPaid": 46162,
  "grants": [{"recipient": null, "purpose": "COLLEGE TUITION AND BOOKS", "amount": 46162}],
  "officers": [{"name": "SEE SCHEDULE ATTACHED", "title": "SEE SCHEDULE ATTACHED", "compensation": 0}],
  "sourceArchive": "https://apps.irs.gov/pub/epostcard/990/xml/2023/2023_TEOS_XML_01A.zip",
  "sourceXml": "https://apps.irs.gov/pub/epostcard/990/xml/2023/2023_TEOS_XML_01A.zip#202300109349100000_public.xml"
}
```

The `grants` and `officers` arrays are embedded in the filing row; there is no extra fee for individual grant or officer entries.

Missing XML fields are represented as `null` or empty arrays, not guessed values.

### How much does it cost to export foundation filings?

The Actor charges $0.005 for one `start` event per run and one `item` event for each exported filing. The BRONZE item price is $0.00512; FREE is $0.005888, SILVER $0.0039936, and GOLD/PLATINUM/DIAMOND $0.003072 each. At BRONZE, one filing costs $0.01012, five cost $0.03060, and 25 cost $0.133 (start plus items); these are billed-event estimates, not a promise about refunds, fraud, disputes, taxes, corrections or chargebacks. An EIN with no match still incurs the start charge because the archive is downloaded. The discount tier is determined by your qualifying monthly Apify Store spend, not this run's item count or your subscription plan; check the live Pricing tab before a large run. Limiting output does not avoid the ZIP download. No separate event is charged for individual grants or officers.

### Spreadsheet and integration workflows

- Download the default dataset as CSV or Excel for a one-time foundation-filing review; flatten `grants[]` separately when you need one row per grant.
- Schedule a run for the same known batch as a reproducible export, or run a series of known batches and deduplicate by `objectId` in your own warehouse.
- Join rows by EIN and `taxPeriod` for cross-year work. A publication-year change alone does not indicate the foundation's fiscal year changed.

This Actor does not monitor IRS updates or discover new batches automatically. To compare revisions, store the prior export outside the Actor and compare object IDs in your pipeline.

### API usage

Use your own Apify token; do not paste it into public tasks. For asynchronous runs:

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~irs-private-foundation-990pf/runs?token=YOUR_APIFY_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"publicationYear":2023,"batch":"01A","eins":["363781852"],"maxItems":1}'
```

With JavaScript:

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/irs-private-foundation-990pf').call({
  publicationYear: 2023, batch: '01A', eins: ['363781852'], maxItems: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

With Python:

```python
from apify_client import ApifyClient
import os
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/irs-private-foundation-990pf').call(
    run_input={'publicationYear': 2023, 'batch': '01A', 'eins': ['363781852'], 'maxItems': 1})
for filing in client.dataset(run['defaultDatasetId']).iterate_items():
    print(filing['ein'], filing['grantsPaid'])
```

### MCP

Connect the Actor as an Apify tool in Claude Code:

```bash
claude mcp add --transport http apify \
  'https://mcp.apify.com?tools=automation-lab/irs-private-foundation-990pf'
```

For Claude Desktop, Cursor, and VS Code with an HTTP MCP server configuration:

```json
{"mcpServers":{"apify":{"type":"http","url":"https://mcp.apify.com?tools=automation-lab/irs-private-foundation-990pf"}}}
```

Example prompts: “Export one 990-PF filing from IRS 2023 ZIP batch 01A and show its assets and grants.” “Export EIN 452590105 from the same archive and list its grant purposes.” Inspect the returned dataset. MCP authentication and tool availability depend on your Apify account/client setup.

### Limits and reliability

An IRS batch may be over 100 MB. Allow sufficient disk and runtime, even for a one-row output. Requests use bounded retries on network, 429 and 5xx failures; a missing ZIP (404) or malformed input fails rather than silently returning an empty result. The archive is deleted from temporary storage at the end of a run. Apify retains input, output dataset and run logs under your account's storage settings; delete runs/datasets according to your retention policy. EIN input is sent only to the Apify runtime and used locally to filter the downloaded IRS archive; no AI model or paid third-party API receives it. The Actor stops after the configured number of accepted filing rows; ordering follows the ZIP contents and is not a ranked search.

Only selected, recognizable XML element names are normalized. IRS form revisions or supplemental schedules may contain data not extracted here. IRS published data can be corrected or republished. Validate critical figures against the original return and source publication before making financial or eligibility decisions.

### Legality and responsible use

The source is public IRS tax-exempt organization filing XML. A filing may contain names and other personal data. Use it for legitimate research, respect applicable privacy obligations and IRS publication terms, and do not infer grant eligibility or legal compliance from a single data point. This is not legal or tax advice.

### Troubleshooting and FAQ

**Why did I get zero rows?** Check whether the selected batch actually contains the EIN and whether its return type is `990PF`. The Actor does not search other batches. Try omitting `eins` to inspect the first matching 990-PF rows in that batch.

**Why did a run fail with HTTP 404?** Verify the exact published archive year and batch code in the IRS directory. Not every syntactically valid code exists for every year.

**Why is the tax period older than the publication year?** IRS publication year is when that ZIP was released, not the filing period. Use `taxPeriod` for fiscal-year analysis.

**Are grant recipients always populated?** No. A filing may use different IRS XML structures or attachments; missing recipient fields remain `null`. The totals are not computed by summing parsed grant entries.

**Where do I report a problem?** Open an issue on the Actor's Apify Store page and include the publication year, batch code, and run ID; avoid sharing sensitive input in a public issue.

### Related Actor

[IRS Tax Exempt Organization Search](https://apify.com/automation-lab/tax-exempt-organization-search) is for master-file organization discovery/status. Use this Actor instead when you already know a ZIP batch and need 990-PF filing-level financials and disclosed grants.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/irs-private-foundation-990pf/changelog.md

# Actor input Schema

## `publicationYear` (type: `integer`):

Publication year of the official IRS XML archive (not the foundation tax period).

## `batch` (type: `string`):

IRS archive batch code within the selected publication year, such as 01A. The Actor downloads this whole batch once; it does not search all batches for an EIN.

## `eins` (type: `array`):

Optional nine-digit EINs without dashes. Only filings in the selected batch are returned.

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

Stop extracting after this many matched 990-PF filings.

## Actor input object example

```json
{
  "publicationYear": 2023,
  "batch": "01A",
  "maxItems": 5
}
```

# Actor output Schema

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

One row per 990-PF filing with assets, investment income, grants and officers.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/irs-private-foundation-990pf").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/irs-private-foundation-990pf").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 '{}' |
apify call automation-lab/irs-private-foundation-990pf --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/irs-private-foundation-990pf"
        }
    }
}
```

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/L2yQcRP5nT9mb0hCN/builds/rn23KccXlyjqowf0x/openapi.json
