Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/plinth-us-grants-data:plinth-us-grants-data-pulse-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Plinth Grants Pulse API
summary: US foundation and nonprofit funding data from public IRS Form 990 and 990-PF filings.
description: 'A read API over the US grantmaking universe, read from public IRS Form 990, 990-EZ and 990-PF e-file filings.
The funder-grantee graph traverses in BOTH directions: filter by `funder_id` for everything a foundation funded, or by `recip_id` for every funder behind a nonprofit.
Start with `/api/v1/search` to turn an organization name into an EIN and a canonical page URL — it needs no key. The grant endpoints need a free key (50 calls a day); the organization endpoints (/compliance, /essentials, /premier) need a paid one.
**No CORS headers, deliberately.** This is a server-side API: a key in browser JavaScript is a key you have published, and every allowance here is per-key. Call it from your backend, or from an agent. There is no browser-callable subset planned.
Figures are read directly from named IRS filings. The IRS releases e-file data on a 12-24 month lag, so every figure is dated to its fiscal year rather than to today. Funding relationships are reported as association, never as causation. Organizations that do not e-file may be absent.'
version: 1.0.0
contact:
name: Plinth
url: https://data.useplinth.com/developers
license:
name: 'Source data: IRS public domain'
url: https://www.irs.gov/charities-non-profits/tax-exempt-organization-search-bulk-data-downloads
termsOfService: https://data.useplinth.com/terms
servers:
- url: https://data.useplinth.com
description: Plinth US philanthropy data
security:
- ApiKeyAuth: []
tags:
- name: Pulse
description: 'Recurring monitoring over a set of EINs: compliance, filings, grants, government funding, governance, and web signals, delivered as a page, email, or PDF. Pro keys only.'
paths:
/api/v1/pulse:
get:
summary: List your monitoring subscriptions
operationId: listPulse
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
tags:
- Pulse
description: Every subscription created with this key, newest first. Subscriptions are scoped to the key that created them; another key's subscriptions are not listed and answer 404 by id.
post:
summary: Create a monitoring subscription
operationId: createPulse
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PulseCreate'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Pulse
description: 'Watch up to 200 EINs on a schedule you choose (hourly to monthly). Each run diffs the organizations against the last run and reports what moved: compliance screen changes, new Form 990 data, new grants in and out of the graph, government funding movement, officer and trustee roster changes, and web press and job-posting signals. The digest is available as JSON, an HTML page, a PDF, and optionally by email.'
/api/v1/pulse/{sub_id}:
get:
summary: One subscription, with its latest run summary
operationId: getPulse
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
title: Sub Id
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Pulse
description: The subscription's configuration plus the id, timestamp and severity totals of its most recent digest, if one has run.
patch:
summary: Update EINs, frequency, checks, delivery, or pause
operationId: updatePulse
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
title: Sub Id
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PulsePatch'
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Pulse
description: 'Any field omitted is left as it is. Changing the frequency re-anchors the next run from now; `active: false` pauses scheduled runs without deleting state, so unpausing resumes the diff rather than re-baselining.'
delete:
summary: Delete a subscription and its stored runs
operationId: deletePulse
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
title: Sub Id
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Pulse
description: 'Removes the subscription, its check state and every stored digest. There is no undo; pausing with `active: false` is the reversible form.'
/api/v1/pulse/{sub_id}/run:
post:
summary: Run now and return the digest
description: 'Runs the checks synchronously and returns the digest. The first run is a baseline: it records what is on file today and reports changes from the next run on. `checks` narrows the run; `format=html|pdf` renders it.'
operationId: runPulse
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
title: Sub Id
- name: checks
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: 'Narrow the run to these checks, comma-separated: compliance, filings, grants, gov, people, web. Omit for the subscription''s full set.'
title: Checks
description: 'Narrow the run to these checks, comma-separated: compliance, filings, grants, gov, people, web. Omit for the subscription''s full set.'
- name: format
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Format
examples:
- json
enum:
- json
- xml
default: json
description: json (default) or xml.
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Pulse
/api/v1/pulse/{sub_id}/latest:
get:
summary: The most recent digest (JSON, HTML, or PDF)
description: The digest payload as JSON by default; `format=html` renders the self-contained page (the same document the email carries), `format=pdf` the retainable report. Every finding is dated and carries its source; the provenance footer states the filing lag and what was not checked.
operationId: getPulseLatest
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
title: Sub Id
- name: format
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Format
examples:
- json
enum:
- json
- xml
default: json
description: json (default) or xml.
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Pulse
/api/v1/pulse/{sub_id}/runs:
get:
summary: Run history for a subscription
operationId: listPulseRuns
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
title: Sub Id
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Pulse
description: Stored run ids, oldest first. Each loads via /pulse/{sub_id}/runs/{run_id}.
/api/v1/pulse/{sub_id}/runs/{run_id}:
get:
summary: One stored digest (JSON, HTML, or PDF)
operationId: getPulseRun
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
title: Sub Id
- name: run_id
in: path
required: true
schema:
type: string
title: Run Id
- name: format
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Format
examples:
- json
enum:
- json
- xml
default: json
description: json (default) or xml.
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Pulse
description: A specific past digest, unchanged since it ran — the same three formats as /latest.
components:
schemas:
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
PulsePatch:
properties:
eins:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: Eins
name:
anyOf:
- type: string
- type: 'null'
title: Name
frequency:
anyOf:
- type: string
- type: 'null'
title: Frequency
checks:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: Checks
email_to:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: Email To
email_mode:
anyOf:
- type: string
- type: 'null'
title: Email Mode
active:
anyOf:
- type: boolean
- type: 'null'
title: Active
unsubscribe_url:
anyOf:
- type: string
- type: 'null'
title: Unsubscribe Url
type: object
title: PulsePatch
PulseCreate:
properties:
eins:
items:
type: string
type: array
title: Eins
description: Up to 200 EINs to watch. Any format — normalized per EIN.
examples:
- - 043407816
- 13-1837418
name:
anyOf:
- type: string
- type: 'null'
title: Name
description: A label for the digest header.
frequency:
anyOf:
- type: string
- type: 'null'
title: Frequency
description: hourly | daily | weekly (default) | monthly.
checks:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: Checks
description: 'Subset of: compliance, filings, grants, gov, people, web. Default all.'
email_to:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: Email To
description: Up to 5 addresses for digest delivery.
email_mode:
anyOf:
- type: string
- type: 'null'
title: Email Mode
description: 'activity (default: email only when something changed) | always.'
unsubscribe_url:
anyOf:
- type: string
- type: 'null'
title: Unsubscribe Url
description: An https link the digest email carries for recipients to stop it.
type: object
required:
- eins
title: PulseCreate
ApiError:
type: object
description: Every failure answers with this envelope; `message` is safe to show a user.
properties:
code:
type: integer
message:
type: string
required:
- code
- message
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
input:
title: Input
ctx:
type: object
title: Context
type: object
required:
- loc
- msg
- type
title: ValidationError
responses:
PaymentRequired:
description: The call allowance is spent, the account is inactive, or the endpoint needs a paid plan — the message says which, and names the period it resets on (daily on free and non-profit plans, monthly on Pro).
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
example:
code: 402
message: API limit of 50 calls a day reached.
Unauthorized:
description: No API key, or a key we do not recognize.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
example:
code: 401
message: missing or invalid API key
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: Free key includes 50 calls a day across /grants/*; the organization endpoints and SQL need a paid plan. Get one at https://data.useplinth.com/pricing.
externalDocs:
description: Developer documentation
url: https://data.useplinth.com/developers