Charity Verify API
Single EIN verification
Bulk Verify API
Batch EIN verification (up to 20K)
Report API
PDF verification reports
Data API
Base & Pro tiers: financials, comp, peer benchmarks
Organizations API
Every nonprofit in a county, city or state
Research API
Diligence briefs
FaithVerify API
Church verification, no EIN needed
▶ Playground Status Support

Charity Verify API

One API call to check tax-deductible eligibility, revocation status, group exemptions, and OFAC sanctions screening for any U.S. nonprofit.

Overview

The Charity Verify API cross-references multiple IRS data sources and OFAC sanctions lists to give you a single, definitive answer on whether a nonprofit can receive tax-deductible donations.

Data sources checked on every request:

  • IRS Exempt Organizations Business Master File (EO BMF)
  • IRS Publication 78 (eligible donees)
  • IRS Auto-Revocation List (with reinstatement detection)
  • Group Exemption resolution (subordinate → parent lookup)
  • OFAC SDN & Consolidated sanctions lists (fuzzy matching)
  • Form 990 leadership screening against OFAC
BASE URL   https://api.givalgo.ai/v1

Authentication

All API requests require an API key passed in the x-api-key header.

# Include your key in every request
curl -H "x-api-key: YOUR_API_KEY" \
  "https://api.givalgo.ai/v1/verify?ein=53-0196605"

To get an API key, contact support@givalgo.ai. Keys are scoped to a usage plan (Free, Basic, or Pro) which determines your rate limits.

Keep your API key secure. Do not expose it in client-side code or public repositories. If compromised, contact support for a key rotation.

Rate-limit headers

Every response to a request made with your key reports your plan's limits and where you stand against them. The limits apply to all endpoints on the key together.

HeaderMeaning
X-RateLimit-LimitRequests allowed per day (UTC).
X-RateLimit-RemainingRequests left today.
X-RateLimit-ResetWhen the daily count resets, as Unix time in seconds (00:00 UTC).
X-RateLimit-Per-SecondSustained requests per second.
X-RateLimit-BurstRequests allowed in a short burst above that rate.

A request over either limit returns 429. Over the per-second rate, retry after a short pause; over the daily limit, retry after the reset. For type-ahead search, wait for a pause in typing (about 250 ms) before sending a request, and cancel a request still in flight when the user keeps typing.

Quickstart

Get up and running in 60 seconds.

cURL

# Verify American Red Cross
curl -H "x-api-key: YOUR_API_KEY" \
  "https://api.givalgo.ai/v1/verify?ein=53-0196605"

Python

import requests

response = requests.get(
    "https://api.givalgo.ai/v1/verify",
    params={"ein": "53-0196605"},
    headers={"x-api-key": "YOUR_API_KEY"}
)
data = response.json()

if data["deductible"]:
    print(f"{data['organization']['name']} is eligible!")
else:
    print(f"Status: {data['status']} - {data['message']}")

JavaScript

const response = await fetch(
  "https://api.givalgo.ai/v1/verify?ein=53-0196605",
  { headers: { "x-api-key": "YOUR_API_KEY" } }
);
const data = await response.json();
console.log(data.status, data.organization?.name);

Verify Endpoint

GET   /v1/verify?ein={ein}&donor_state={state}&ca_entity_id={id}

Returns a complete verification of the nonprofit identified by the given EIN, including tax-deductible eligibility, revocation history, group exemption status, OFAC sanctions screening, and California state compliance (when applicable).

Parameters

ParameterTypeRequiredDescription
einstringYesEmployer Identification Number. Accepts 13-2875808, 132875808, or 13 1837418.
donor_statestringNoAccepted for backward compatibility; it does not change the result. The California checks run on every request. See California Compliance.
ca_entity_idstringNoCalifornia SOS/FTB entity number for direct lookup in the CA AG Registry and FTB Revocation list. Provides the highest coverage for California compliance checks.

Response Fields

FieldTypeDescription
statusstringOverall verification result (see Statuses)
deductibleboolean | nulltrue: deductible on IRS evidence. false: not deductible (NOT_DEDUCTIBLE, REVOKED). null: IRS data does not settle it (NOT_FOUND, most CAUTION, and a REVOKED governmental unit, code governmental_unit). null is not the same as false. See Statuses.
messagestringHuman-readable explanation
status_reasons Newobject[]Present only on CAUTION and REVOKED, and then never empty. One {code, message} per reason, in the order found; an organization can have several. Build logic on code and show message. See Status reason codes.
in_master_file NewbooleanOn every response. true when the EIN is in the current IRS Business Master File (when organization is present).
pub78_bmf_conflicts Newstring[]On every response, never null. One code for each way Publication 78 and the IRS Business Master File disagree about this organization; empty when they agree. It reports the disagreement and never changes status or deductible. See Pub 78 vs master file conflict codes.
bulletin_revocation NewobjectPresent only when a revocation the IRS announced in the Internal Revenue Bulletin decides the answer (REVOKED, or CAUTION while Publication 78 still lists the organization): the announcement, issue, effective date, the name and location as printed, and a link.
organizationobjectIRS EO BMF data (name, city, state, subsection, foundation type, assets, income, NTEE code)
pub78objectPublication 78 listing and deductibility type (PC, PF, POF, SO, etc.; combined codes such as EO,LODGE are described part by part). via_group_exemption: true when the answer comes from the group parent's listing; church_exception: true when it rests on the IRS church classification.
revocationobjectAuto-revocation history and reinstatement status
group_exemptionobjectPresent when a 501(c)(3) subordinate is covered by a group exemption whose parent is in Pub 78. Shows the parent's details. The status is usually ELIGIBLE, and CAUTION when the subordinate's own record contradicts the coverage.
sanctions_screeningobjectOFAC screening results (see Sanctions Screening below for match object details)
state_complianceobjectAlways present. Contains the california sub-object with the AG May Not Operate list result and FTB status. See California Compliance.
daf_grant Newobject | nullWhat kind of charity this is and whether a donor-advised fund grant needs expenditure responsibility. Present on ELIGIBLE and CAUTION, null otherwise. See DAF grants.
data_freshnessobjectPer source (eo_bmf, pub78, revocation, ofac_sdn, ofac_cons, ca_ag, ca_ftb, irb): the agency's publication date (for irb, the newest Internal Revenue Bulletin issue read) (ISO) of the file version loaded in our database and used for this verdict. Null only when we have no date.
last_checkedobjectPer source: when we last confirmed with the agency that nothing newer than the data_freshness file exists (ISO 8601, UTC). Checked daily at about 01:45 UTC (the Bulletin at about 01:55); if a check fails, the source keeps its last successful time. Null until a check is recorded.
stale_sourcesstring[]The data_freshness sources that may be behind the agency: a newer file published a day or more ago has not loaded, a newer file is held for review because its row count changed far more than usual, or the agency's server has been unreachable for two days. Empty when everything is current.
checked_atstringISO 8601 timestamp of this verification

sanctions_screening object

FieldTypeDescription
statusstringCLEAR, POTENTIAL_MATCH, STRONG_MATCH, or NOT_SCREENED
organization_screeningobjectOrg name screened against OFAC entity entries
  .checked_namestringOrganization name that was screened
  .matches_foundintegerNumber of OFAC matches
  .matches[]arrayArray of match objects (see below)
leadership_screeningobjectForm 990 officers/directors screened against OFAC individuals
  .people_data_tax_yearinteger | nullTax year of the Form 990 people data used for screening (e.g., 2023). Null if no people data found.
  .people_sourcestring | nullform_990 (Part VII roster of the latest Form 990 / 990-EZ) or form_990n_principal_officer (no roster on file; the principal officer named on the latest Form 990-N e-Postcard was screened). Null if no names were available.
  .people_checkedintegerNumber of people screened (Form 990 roster, or 1 for an e-Postcard filer's principal officer)
  .people_screened[]arrayList of officers/directors/key employees screened, each with name and title
    .namestringPerson's name as reported on Form 990
    .titlestringPerson's title/role (e.g., "EXECUTIVE DIRECTOR", "BOARD PRESIDENT")
  .matches_foundintegerNumber of OFAC matches
  .matches[]arrayArray of match objects (see below)
checked_againstarray["OFAC SDN", "OFAC Consolidated"]
screened_atstringISO 8601 timestamp of screening

Match object (within matches[])

FieldTypeDescription
sourcestring"OFAC SDN" or "OFAC Consolidated"
matched_namestringName from OFAC list that matched
matched_viastring"primary_name" or "alias (a.k.a. ...)"
primary_namestringPrimary OFAC name (only present for alias matches)
similarity_scorenumberFuzzy match score (0.7–1.0). ≥0.9 = STRONG, ≥0.7 = POTENTIAL
match_strengthstring"STRONG" or "POTENTIAL"
sdn_typestringOFAC entity type ("individual", "N" for entity/org)
programstringOFAC sanctions program (e.g., "SDGT", "IRAN")
entry_idintegerOFAC entry number
remarksstringOFAC remarks: often contains date of birth, nationality, passport numbers, and other identifying information. Key for false positive disambiguation.
addressesarrayKnown addresses for this OFAC entry. Each has address, city_state_zip, and country fields.

Verification Statuses

Every EIN gets exactly one status, and the message explains the specific reason. Each verification checks the IRS Business Master File, IRS Publication 78, the IRS auto-revocation list and California’s registries.

StatusDeductibleMeaning
ELIGIBLEtrueIRS records confirm the organization can receive tax-deductible contributions: it is listed in Publication 78, covered by a listed parent’s group exemption, or a church (churches need not appear in Publication 78).
CAUTIONnull, or trueThe IRS records conflict or do not settle deductibility, or a California finding applies. deductible stays true when the only caveat is a California finding or a reinstatement the IRS has not yet confirmed in Publication 78. Confirm before a material grant, for example in the IRS Tax Exempt Organization Search.
NOT_DEDUCTIBLEfalseIRS records establish that contributions are not deductible, for example because the organization is not a 501(c)(3).
REVOKEDfalse, or nullThe IRS auto-revoked the organization’s exemption for not filing, and there is no evidence of reinstatement; or the IRS announced a revocation of its 501(c)(3) status in the Internal Revenue Bulletin and Publication 78 no longer lists it.
NOT_FOUNDnullNot in the IRS master file or Publication 78, and not revoked. Churches are not required to apply for recognition, so a church can legitimately be absent.

null means IRS data does not settle it, which is not the same as false. Customers receive our full Verification Methodology, which sets out the rules behind each status, under NDA. Contact sales@givalgo.ai.

Status reason codes

Every CAUTION and REVOKED response lists why in status_reasons. Codes are stable: we may reword a message, but we never rename or reuse a code. We may add codes, so treat one you do not recognize as “review manually”.

CodeStatusMeaning
auto_revokedREVOKEDAutomatically revoked by the IRS for not filing for three consecutive years, with no evidence of reinstatement.
bulletin_revokedREVOKEDThe IRS announced a revocation of its 501(c)(3) status in the Internal Revenue Bulletin, usually after an examination, and Publication 78 no longer lists it. See bulletin_revocation.
governmental_unitREVOKEDAdded to bulletin_revoked when the IRS classifies the organization as a governmental unit: a gift to a governmental unit for public purposes can be deductible under IRC §170(c)(1) without 501(c)(3) status, so deductible is null. Confirm before granting.
not_on_pub78CAUTIONA 501(c)(3) the IRS master file marks deductible, but not listed in Publication 78.
deductibility_undeterminedCAUTIONA 501(c)(3) the IRS has not assigned a deductibility determination, not listed in Publication 78.
treaty_onlyCAUTIONA foreign organization deductible only by tax treaty.
irs_church_classification_contradictedCAUTIONThe IRS classifies it as a church, but its own record says contributions are not deductible.
group_exemption_contradictedCAUTIONCovered by a listed parent’s group exemption, but its own record says contributions are not deductible.
foreign_addressCAUTIONA church or group subordinate whose IRS record places it outside the United States.
auto_revoked_parent_listedCAUTIONAuto-revoked with no recorded reinstatement, but covered by a group exemption whose parent is listed today.
auto_revoked_later_rulingCAUTIONAuto-revoked with no recorded reinstatement; an IRS determination dated after the revocation is the only sign it was recognized again. deductible stays true when this is the only reason.
bulletin_revoked_still_on_pub78CAUTIONThe IRS announced a revocation of its 501(c)(3) status in the Internal Revenue Bulletin, but Publication 78 still lists it. Reliance on the listing ends with the announcement (Rev. Proc. 2018-32, section 4.01). See bulletin_revocation.
ca_not_in_good_standingCAUTIONOn California’s May Not Operate list or revoked by the Franchise Tax Board. Federal deductibility is unaffected, so deductible stays true when this is the only reason.

Pub 78 vs master file conflict codes

pub78_bmf_conflicts lists where IRS Publication 78 and the IRS Business Master File disagree, on any status. It is informational: status and deductible are decided the same way with or without it. Codes are stable and never renamed; we may add codes, so treat one you do not recognize as “review manually”.

CodeMeaning
pub78_not_in_bmfListed in Publication 78 but absent from the master file.
bmf_deductible_not_in_pub78The master file marks it a deductible 501(c)(3), but it is not listed in Publication 78 although it would be expected to be.
subsection_mismatchPublication 78 lists it as a 501(c)(3) type of charity, but the master file classifies it under another subsection.
charity_type_mismatchPublication 78 and the master file name different types of charity (for example public charity and private foundation).

DAF grants

daf_grant tells a donor-advised fund sponsor what kind of charity the organization is and whether a grant to it needs expenditure responsibility under IRC §4966. It is present on ELIGIBLE and CAUTION, and null on REVOKED, NOT_DEDUCTIBLE and NOT_FOUND.

FieldValues
charity_typepublic_charity, supporting_organization, private_operating_foundation, private_foundation, other_170c (eligible for deductible gifts but not a public charity, such as a veterans organization or fraternal lodge), or unknown.
supporting_org_typeFor a supporting organization: type_i, type_ii, type_iii_functionally_integrated, type_iii_non_functionally_integrated, type_i_ii_or_iii_functionally_integrated or unknown. Otherwise null.
expenditure_responsibilitynot_required, required, or review when IRS data cannot settle it.
noteA sentence or two on why, and what to check when the answer is review.
"daf_grant": {
  "charity_type": "supporting_organization",
  "supporting_org_type": "type_i",
  "expenditure_responsibility": "review",
  "note": "A supporting organization (IRC §509(a)(3)). A DAF grant needs no expenditure responsibility unless a donor or advisor of the fund, or a related person, controls an organization it supports (IRC §4966(d)(4)). Check that before granting."
}

Sanctions Screening

Every verification includes an automated OFAC screening of the organization name and its leadership (officers, directors, key employees from Form 990). Matches are checked against both SDN and Consolidated lists, including primary names and aliases.

Overall status

StatusMeaning
CLEARNo OFAC matches found for the organization or its leadership.
POTENTIAL_MATCHFuzzy match with 70–89% similarity. Manual review recommended.
STRONG_MATCHFuzzy match with 90%+ similarity. Very likely the same entity.
NOT_SCREENEDOFAC screening was not performed (e.g., organization not found in IRS database). See the reason field for details.

Disambiguation fields

When matches are found, each match object includes remarks and addresses to help you distinguish true positives from false positives:

  • remarks: OFAC remarks field containing date of birth, nationality, passport numbers, and other identifying information
  • addresses: known addresses for the matched OFAC entry (street, city/state/zip, country)

Example match object

{
  "source": "OFAC SDN",
  "matched_name": "SMITH, John A.",
  "matched_via": "primary_name",
  "similarity_score": 0.912,
  "match_strength": "STRONG",
  "sdn_type": "individual",
  "program": "SDGT",
  "entry_id": 12345,
  "remarks": "DOB 15 Mar 1970; nationality Iran; Passport A1234567",
  "addresses": [
    {
      "address": "123 Example St",
      "city_state_zip": "Tehran",
      "country": "Iran"
    }
  ]
}

In this example, a board member named "John Smith" matched an OFAC entry, but the remarks show an Iranian DOB/passport, and the addresses show Tehran. If your board member is based in Ohio with a different DOB, this is a false positive you can confidently dismiss.

California Compliance

California AB 488 requires fundraising platforms to verify that nonprofits are in good standing with three authorities before facilitating donations involving a California donor or a California-based nonprofit:

  1. IRS: federal tax-exempt status (always checked)
  2. CA Attorney General: May Not Operate or Solicit for Charitable Purposes List (11 CCR § 312). This is a negative list: being on it is the finding, and absence from it is the AB 488 good-standing signal (11 CCR § 316(c)).
  3. CA Franchise Tax Board: state tax-exempt status (revocation check)

The California checks run on every request, for every organization, wherever it is based. The Attorney General’s list includes organizations based in other states, and AB 488 covers every organization a platform solicits for. donor_state is accepted but does not change the result. Pass ca_entity_id, the California SOS/FTB entity number, for a direct lookup.

When an organization based outside California is on the list, ag_registry.notice explains that the finding bars it from soliciting in California and says nothing about its home-state standing, so a grantmaker elsewhere understands why the overall status is CAUTION.

match_method in the response says how the organization was matched to the list: ca_entity_id, fein, or name_match (name within the same city).

state_compliance.california object

FieldTypeDescription
ag_registryobjectResult against the CA Attorney General's May Not Operate list
  .statusstringNOT_LISTED (not on the list), or the finding for a listed org: REVOKED, SUSPENDED, DELINQUENT, CEASE_AND_DESIST, LISTED (unclassified, but still not in good standing)
  .registration_statusstringVerbatim AG registration status (e.g., "Revoked", "Delinquent", "Delinquent Platform Charity"). Null for Cease-and-Desist rows
  .entity_statusstringVerbatim AG entity status (e.g., "Listed", "Cease and Desist Order", "Merged Out")
  .noticestringPresent for NOT_LISTED (what the result does and does not establish) and for a listed organization based outside California (barred from soliciting in California; says nothing about its home-state standing)
  .reg_numberstringState charity registration number (e.g., CT0252232)
  .match_methodstringca_entity_id, fein, or name_match
  .matched_namestringPresent for name_match: the CA AG name that matched
  .name_similaritynumberPresent for name_match: similarity score of the name match
ftbobjectCA Franchise Tax Board revocation result
  .statusstringCLEAR or REVOKED
  .revocation_datestringDate of FTB revocation (if revoked)
state_statusstringRoll-up: CLEAR (not on the AG list, no FTB revocation), NON_COMPLIANT (on the AG list), or REVOKED (FTB)

Impact on overall status

AG RegistryFTBstate_statusOverall status
NOT_LISTEDCLEARCLEARNo change
REVOKED / SUSPENDED / DELINQUENT / CEASE_AND_DESIST / LISTEDCLEARNON_COMPLIANTELIGIBLE → CAUTION
AnyREVOKEDREVOKEDELIGIBLE → CAUTION

Registration details for a specific organization are available in the AG’s Registry Search Tool.

Example response

{
  "status": "CAUTION",
  "message": "This organization is listed in IRS Publication 78 ... However, California state compliance checks indicate that the organization is on the California Attorney General's May Not Operate or Solicit for Charitable Purposes List (status 'Delinquent'). Under California AB 488, fundraising platforms must verify state-level good standing before facilitating donations.",
  "state_compliance": {
    "california": {
      "ag_registry": {
        "status": "DELINQUENT",
        "registration_status": "Delinquent",
        "entity_status": "Listed",
        "reg_number": "CT0252232",
        "match_method": "fein"
      },
      "ftb": {
        "status": "CLEAR"
      },
      "state_status": "NON_COMPLIANT"
    }
  }
}

Playground

Test the API directly from your browser. Enter your API key and an EIN to see a live response.

Try it now


                    

Bulk Verify API

Verify hundreds or thousands of EINs in a single async request. Same 5-step verification, delivered at scale.

Overview

The Bulk Verify API accepts up to 20,000 EINs per job, in a JSON body (a semicolon-separated string or an array) or as a CSV upload, processes them asynchronously via background workers, and delivers results via polling endpoint, webhook callback, or CSV download.

Each EIN goes through the same verification as the single Verify endpoint: IRS data cross-referencing, group exemption resolution, and OFAC sanctions screening.

How it works

  1. Submit: POST your EINs to /v1/bulk-verify. Get back a job_id immediately (202 Accepted).
  2. Process: EINs are split into batches of 10 and verified in parallel by background workers.
  3. Retrieve: Poll GET /v1/bulk-verify/{job_id} for progress and results, or receive a webhook callback on completion.
Input MethodMax EINsFormat
JSON body (semicolon-separated string, or an array)20,000application/json
CSV file upload20,000multipart/form-data
BASE URL   https://api.givalgo.ai/v1

Authentication

All API requests require an API key passed in the x-api-key header, the same key used for the single Verify endpoint.

# Include your key in every request
curl -X POST "https://api.givalgo.ai/v1/bulk-verify" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"eins": "13-2875808;53-0196605"}'

To get an API key, contact support@givalgo.ai. Keys are scoped to a usage plan (Free, Basic, or Pro) which determines your rate limits and max EINs per job.

Job ownership: Jobs are scoped to your API key. You can only view the status and results of jobs you created.

Quickstart

Submit a bulk job and poll for results in under a minute.

cURL: Submit + Poll

# 1. Submit a bulk job
curl -X POST "https://api.givalgo.ai/v1/bulk-verify" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "eins": "13-2875808;53-0196605;06-0646973",
    "format": "both"
  }'

# Response: {"job_id": "bulk_abc123", "status": "running", ...}

# 2. Poll for results
curl -H "x-api-key: YOUR_API_KEY" \
  "https://api.givalgo.ai/v1/bulk-verify/bulk_abc123"

Python

import requests, time

# Submit bulk job
resp = requests.post(
    "https://api.givalgo.ai/v1/bulk-verify",
    headers={"x-api-key": "YOUR_API_KEY"},
    json={
        "eins": "13-2875808;53-0196605;06-0646973",
        "format": "both"
    }
)
job = resp.json()
job_id = job["job_id"]

# Poll until complete
while True:
    status = requests.get(
        f"https://api.givalgo.ai/v1/bulk-verify/{job_id}",
        headers={"x-api-key": "YOUR_API_KEY"}
    ).json()
    if status["status"] == "done":
        print(f"Done! {status['summary']}")
        break
    time.sleep(2)

JavaScript

// Submit bulk job
const resp = await fetch("https://api.givalgo.ai/v1/bulk-verify", {
  method: "POST",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    eins: "13-2875808;53-0196605;06-0646973",
    format: "both"
  })
});
const { job_id } = await resp.json();

// Poll until complete
const poll = async () => {
  const res = await fetch(
    `https://api.givalgo.ai/v1/bulk-verify/${job_id}`,
    { headers: { "x-api-key": "YOUR_API_KEY" } }
  );
  const data = await res.json();
  if (data.status === "done") return data;
  await new Promise(r => setTimeout(r, 2000));
  return poll();
};
const results = await poll();

Submit Bulk Job

POST   /v1/bulk-verify

Submit EINs for bulk verification. Returns 202 Accepted with a job_id for tracking.

JSON body (semicolon-separated EINs, or an array)

curl -X POST "https://api.givalgo.ai/v1/bulk-verify" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "eins": "13-2875808;53-0196605;06-0646973",
    "webhook_url": "https://your-server.com/hook",
    "format": "both"
  }'

CSV file upload

curl -X POST "https://api.givalgo.ai/v1/bulk-verify" \
  -H "x-api-key: YOUR_API_KEY" \
  -F "file=@organizations.csv" \
  -F "webhook_url=https://your-server.com/hook"

CSV must have an ein column header (case-insensitive). Falls back to the first column if no header match.

Request body fields

FieldTypeRequiredDescription
einsstringYes*Semicolon-separated EINs. *Not needed for CSV upload.
webhook_urlstringNoURL to POST results to on job completion.
formatstringNoResult format: "json", "csv", or "both" (default).

Query parameters

ParameterTypeDefaultDescription
skip_invalidbooleanfalseIf true, skip invalid EINs and process only valid ones. If false, return a 400 error listing all invalid EINs.

Response (202 Accepted)

{
  "job_id": "bulk_a1b2c3d4e5f6",
  "status": "running",
  "total_eins": 247,
  "duplicates_removed": 3,
  "estimated_seconds": 8,
  "poll_url": "/v1/bulk-verify/bulk_a1b2c3d4e5f6",
  "created_at": "2026-03-25T14:30:00Z"
}

Validation error (400)

If any EINs fail validation (and skip_invalid is not set), you get a 400 with details:

{
  "error": "VALIDATION_ERROR",
  "message": "3 EINs failed validation",
  "invalid_eins": [
    { "ein": "123", "error": "EIN must be exactly 9 digits" }
  ],
  "valid_count": 247,
  "hint": "Add ?skip_invalid=true to process only valid EINs."
}

Poll Job Status

GET   /v1/bulk-verify/{job_id}

Check the progress and retrieve results of a bulk verification job. Jobs are scoped to your API key: you can only access jobs you created.

In progress

{
  "job_id": "bulk_a1b2c3d4e5f6",
  "status": "running",
  "progress": {
    "total": 247,
    "completed": 180,
    "failed": 2,
    "percent_complete": 73.7
  }
}

Completed

For jobs with 1,000 or fewer EINs, results are included inline. Larger jobs return a csv_download_url only. Each result in the array is a full single-verify response, identical to what you'd get from the /v1/verify endpoint, including organization details, Pub78, revocation history, and OFAC sanctions screening.

{
  "job_id": "bulk_a1b2c3d4e5f6",
  "status": "done",
  "summary": {
    "eligible": 200,
    "not_deductible": 30,
    "revoked": 5,
    "not_found": 8,
    "ofac_flags": 3
  },
  "results": [
    {
      "ein": "13-2875808",
      "ein_raw": "132875808",
      "status": "ELIGIBLE",
      "deductible": true,
      "message": "Organization is eligible to receive tax-deductible donations.",
      "organization": { /* full org details: name, city, state, NTEE, assets, ... */ },
      "pub78": { /* listed, deductibility_code, description */ },
      "revocation": { /* ever_revoked, currently_revoked, dates */ },
      "sanctions_screening": { /* status, org & leadership screening, matches */ },
      "checked_at": "2026-03-25T23:11:10Z",
      "api_version": "v1"
    },
    // ... one entry per EIN, same structure as single /v1/verify response
  ],
  "csv_download_url": "https://...presigned...",
  "completed_at": "2026-03-25T14:30:07Z"
}

CSV download

The csv_download_url (pre-signed S3 URL, valid 1 hour) contains a flattened version of the full results with 49 columns covering organization details, Pub78 status, revocation history, and OFAC screening. Column headers:

ein, status, deductible, message, reason_codes,
org_name, org_city, org_state, subsection_code, subsection_label,
deductibility_code, deductibility_label, foundation_code, foundation_label,
status_code, status_label, affiliation_code, affiliation_label,
group_exemption_number, ruling_date, ntee_code,
asset_amount, income_amount, revenue_amount,
pub78_listed, pub78_deductibility_code, pub78_deductibility_description,
pub78_via_group_exemption,
ever_revoked, currently_revoked, revocation_date, revocation_posting_date,
reinstatement_date,
sanctions_status, ofac_org_matches_found, ofac_leadership_matches_found,
screened_at,
ca_state_status, ca_ag_status, ca_ag_registration_status,
ca_ag_entity_status, ca_ag_reg_number, ca_ag_match_method,
ca_ftb_status, ca_ftb_revocation_date,
error_message, checked_at,
in_master_file, pub78_bmf_conflicts

The eight ca_* columns are populated on every row, because the California checks run for every organization. ca_state_status is CLEAR, NON_COMPLIANT or REVOKED; ca_ag_status is NOT_LISTED or the finding for an organization on the Attorney General's May Not Operate list. Same meanings as state_compliance.california on GET /v1/verify.

reason_codes holds the code of every entry in status_reasons, separated by ;, and is empty unless the status is CAUTION or REVOKED. See Status reason codes.

in_master_file and pub78_bmf_conflicts are the fields of the same name on GET /v1/verify; the conflict codes are separated by ; and the column is empty when the records agree. See Pub 78 vs master file conflict codes.

sanctions_status is sanctions_screening.status from GET /v1/verify: CLEAR, POTENTIAL_MATCH, STRONG_MATCH or NOT_SCREENED (no name to screen, e.g. an EIN not found in IRS records). The summary's ofac_flags counts the POTENTIAL_MATCH and STRONG_MATCH rows.

Job statuses

StatusMeaning
runningBeing processed by background workers.
doneAll EINs have been verified. Results are available.
failedThe job stopped before finishing. Partial results may be available.

Webhooks

If you provide a webhook_url when submitting a bulk job, we'll POST a notification when the job completes. This is the recommended approach for large jobs instead of polling.

Webhook payload

# POST to your webhook_url
# Headers:
#   Content-Type: application/json
#   User-Agent: Givalgo-Webhook/1.0
#   X-Givalgo-Event: bulk_verify.completed
#   X-Givalgo-Job-Id: bulk_a1b2c3d4e5f6

{
  "event": "bulk_verify.completed",
  "job_id": "bulk_a1b2c3d4e5f6",
  "status": "done",
  "total_eins": 247,
  "completed": 245,
  "failed": 2,
  "summary": {
    "eligible": 200,
    "not_deductible": 30,
    "ofac_flags": 3
  },
  "csv_download_url": "https://...presigned-url...24hr-expiry...",
  "poll_url": "/v1/bulk-verify/bulk_a1b2c3d4e5f6"
}

Retry policy

Webhook delivery is attempted up to 3 times with exponential backoff (1s, 5s delays). If your endpoint returns a 2xx status, delivery is marked as successful. The csv_download_url is a pre-signed S3 URL valid for 24 hours.

Throughput

EINsEstimated Time
10~5 seconds
50~15 seconds
250~30 seconds
1,000~2 minutes
20,000~40 minutes

Job Statuses

A bulk job progresses through these statuses:

StatusMeaning
runningBeing processed by background workers. Poll for progress updates.
doneAll EINs have been verified. Results are available inline (for jobs ≤ 1,000 EINs) and via CSV download URL.
failedThe job stopped before finishing. Partial results may be available in progress.

Verification Statuses

Each EIN in a bulk job receives the same verification statuses as the single Verify endpoint:

StatusDeductibleMeaning
ELIGIBLEtrueListed in IRS Pub 78 (directly or via group exemption), or a domestic church under the statutory church exception. Eligible for tax-deductible donations.
NOT_DEDUCTIBLEfalseIn IRS records but not tax-deductible (e.g., 501(c)(4), 501(c)(6)).
REVOKEDfalseTax-exempt status auto-revoked by the IRS for non-filing. May appear even if the organization has dropped off the Business Master File.
CAUTIONnull, or trueThe IRS records do not settle it, or a California finding applies. The reason_codes column lists every reason, separated by ; (see Status reason codes), and message explains them. Confirm via the IRS Tax Exempt Organization Search.
NOT_FOUNDnullNot in IRS records and not on the revocation list. Churches and self-declared orgs may not appear.

Limits & Throughput

These limits apply to every bulk job.

LimitValue
Max EINs (JSON body)20,000
Max EINs (CSV upload)20,000
Max concurrent jobs5 per API key
Result retention30 days from submission (results_expire_at), then 410 JOB_EXPIRED
CSV download URL expiry24 hours

Estimated processing times

EINsEstimated Time
10~5 seconds
50~15 seconds
250~30 seconds
1,000~2 minutes
20,000~40 minutes

Processing time depends on current load and Lambda warm-start state. For time-sensitive workloads, use a webhook for instant notification on completion.

Report API

Generate professional, branded PDF verification reports for any U.S. nonprofit, ready to share with donors, board members, or compliance teams.

Overview

The Report API takes an EIN and returns a multi-page PDF document containing the same verification data as the Verify endpoint, presented in a polished, print-ready format with Givalgo branding.

Each report includes a cover page, verification outcome with status badge, IRS data breakdown, OFAC sanctions screening results, financial snapshot, and a branded back page.

GET   /v1/report?ein={ein}  →  application/pdf

Authentication

The Report API uses the same API key authentication as the Verify and Bulk Verify endpoints. Include your key in the x-api-key header:

curl -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/pdf" \
  "https://api.givalgo.ai/v1/report?ein=13-2875808" \
  -o report.pdf

Quickstart

Generate your first PDF report in seconds:

# Download a verification report for Human Rights Watch (EIN 13-2875808)
curl -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/pdf" \
  "https://api.givalgo.ai/v1/report?ein=13-2875808" \
  -o verification-report.pdf

# Open the PDF
open verification-report.pdf

Tip: Include the Accept: application/pdf header to receive decoded binary PDF. In Postman, use Send and Download (dropdown next to Send) to save the file.

Report Endpoint

GET   /v1/report?ein={ein}

Takes an EIN and returns a professionally formatted PDF verification report. The underlying data is identical to the /v1/verify endpoint; the Report API simply renders it into a branded, multi-page PDF document.

Parameters

ParameterTypeRequiredDescription
einstringYesEmployer Identification Number. Accepts 13-2875808, 132875808, or 13 1837418.

Headers

HeaderRequiredDescription
x-api-keyYesYour API key
AcceptRecommendedSet to application/pdf for decoded binary response

Success Response

PropertyValue
Status Code200 OK
Content-Typeapplication/pdf
Content-Dispositioninline; filename="npo-verify-{ein}.pdf"

Error Responses

Error responses are returned as JSON (not PDF):

StatusError CodeDescription
400MISSING_EINThe ein query parameter was not provided
400INVALID_EINEIN format is invalid (not 9 digits)
403ForbiddenMissing or invalid API key
500INTERNAL_ERRORServer-side error during PDF generation

Note: All valid verification statuses (ELIGIBLE, REVOKED, NOT_FOUND, CAUTION, NOT_DEDUCTIBLE) return a 200 with a PDF. Only auth or input errors return JSON.

Report Sections

Each generated PDF contains the following pages:

PageSectionContent
1Cover PageGivalgo branding, organization name, EIN, report generation date
2Outcome & DetailsLarge status badge (color-coded), deductibility verdict, organization details (city, state, subsection, NTEE code, ruling date)
3IRS Status BreakdownEO BMF status & deductibility, Pub 78 listing, group exemption details, revocation history with dates
4OFAC ScreeningOrganization & leadership sanctions screening status, match details (if any), data source year
5Financial SnapshotTotal assets, annual income, annual revenue (from IRS BMF data)
6Back PageDisclaimer, API version, generation timestamp, contact info

Verification Statuses

The report displays the same verification statuses as the Verify endpoint, rendered as color-coded badges:

StatusBadge ColorDeductibleMeaning
ELIGIBLE● GreenYesListed in IRS Pub 78 (directly or via group exemption), or a domestic church under the statutory church exception.
NOT_DEDUCTIBLE● OrangeNoIn IRS records but not tax-deductible.
REVOKED● RedNoTax-exempt status auto-revoked by the IRS.
CAUTION● YellowUnconfirmedThe IRS records do not settle it, or a California finding applies. The report states the case.
NOT_FOUND● GrayUnknownNot in IRS records. Churches and self-declared orgs may not appear.

Important Notes

Response Time

PDF generation typically takes 2–4 seconds (cold start may add 3–5 seconds on first request). The API Gateway timeout is 29 seconds.

Data Source

Reports contain the same data as the /v1/verify endpoint. The PDF is generated on-the-fly from live verification data and is not cached. Each request produces a fresh report with the current timestamp.

File Size

Reports are typically 115–130 KB depending on the amount of data (e.g., OFAC matches increase size slightly).

Disclaimer

Each report includes a footer disclaimer: "This report is generated from publicly available IRS data and OFAC sanctions lists. It does not constitute legal or tax advice."

Data API

Search 1.9M+ U.S. nonprofits by EIN, name, cause, geography and financials, and get back clean, lean org records built from 3.3M+ Form 990 filings.

Overview

The Data API is a nonprofit search engine. One endpoint, POST /v1/data, accepts two kinds of criteria, alone or together:

  • search_terms: an EIN ("13-2875808") or an organization name ("Human Rights Watch"). Keywords match organization names, not mission text and not cause. To search by cause use filters.organization.causes (plain English) or ntee_major_codes (codes).
  • filters: structured criteria for cause (plain English or NTEE), geography (state / city / zip), org type, IRS standing, and financial screens (revenue, assets, grants, program expense ratio, months of cash, growth streaks, revenue concentration)

Every request returns the same lean org record (~40 fields): identity, geography, cause, IRS standing, latest financial toplines, plus the organization's logo, website, and website-scraped contact details where available.

When you've found the organization you care about, go deep with the Data Pro API:

  • POST /v1/data Data: search & discovery; lean records
  • GET /v1/data/suggest Data: autocomplete for a search box, organizations as the user types a name or EIN
  • GET /v1/data-pro Pro: the full single-org profile (~410 fields: full financial detail, compensation cube, governance, schedules, registration)
BASE URL   https://api.givalgo.ai/v1

Migrating from another nonprofit data API? Familiar request-body conventions and field aliases are accepted where the data overlaps; unsupported fields return a 400 with a hint naming the nearest supported equivalent. See Filters.

Tiers: Data vs Data Pro

The two tiers differ by how you interact, not just field count. The Data API answers "which organizations?" (search and filter) with lean records. Data Pro answers "tell me everything about this one" with a single-EIN deep profile. Both require the same x-api-key header.

/v1/data Data/v1/data-pro Pro
InteractionSearch: EIN / name / keywords, structured filtersLookup: one EIN per request
ResponsePage of lean org records (~40 fields each)One deep profile (~410 fields)
Best forDiscovery, screening, autocomplete, pipeline building, monitoringDue diligence, profiles, analysis

The deep-profile sections below (organization, financials, trends, compensation, …) document the Data Pro response:

Section/v1/data-pro Pro
organization: identity, NTEE, mission✓
financials: revenue, expenses, balance sheet✓
trends: 6-year annual series + CAGR + trend labels✓
compensation: officers, by_role, peer benchmarks✓
funding_sources: grantmakers received from✓
programs: program descriptions + expenses✓
governance: board + policies✓
fundraising: events, professional fundraisers✓
financial_profile: liquidity, HHI, expense composition New✓
related_organizations✓
foreign_activities, lobbying_and_political, donor_advised_funds✓
data_freshness✓
financials.detail: Part VIII/IX/X line items + Schedule D blocks Pro✓
governance.detail: full board roster, policy flags, compliance filings✓
disclosures: Schedules K/L/M/N/F/A/O✓
registration: IRS BMF + Pub 78 + California AG/FTB✓

New indicates fields added in the April 2026 release.

Authentication

All requests require an API key passed in the x-api-key header. Keys are scoped to a usage plan (Free, Basic, Pro) which determines rate limits.

# Data API: search
curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"search_terms": "Human Rights Watch"}' \
  "https://api.givalgo.ai/v1/data"

# Data Pro API: deep single-org profile
curl -H "x-api-key: YOUR_API_KEY" \
  "https://api.givalgo.ai/v1/data-pro?ein=53-0196605"

To upgrade to Pro or request a trial key, contact support@givalgo.ai.

Quickstart

cURL: look up one org (EIN or name)

curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"search_terms": "13-2875808"}' \
  "https://api.givalgo.ai/v1/data"

cURL: structured search

curl -X POST -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "search_terms": "community foundation",
    "filters": {"geography": {"state": ["FL"], "city": "Miami"}},
    "size": 10
  }' \
  "https://api.givalgo.ai/v1/data"

cURL: autocomplete as the user types

curl -H "x-api-key: YOUR_API_KEY" \
  "https://api.givalgo.ai/v1/data/suggest?q=red%20cro&session_token=8f14e45f-ceea-467a-9b7e-5d4f8c1a2b3c"

See Autocomplete for the parameters and response.

Search by cause: three worked examples

Plain English in filters.organization.causes, resolved to NTEE codes and composed with any other filter. Every response reports what the phrase became in causes_resolved.

# 1. A cause in one city: Plymouth Housing Group, Bellwether Housing
{
  "filters": {"organization": {"causes": "homelessness"},
              "geography": {"city": "Seattle", "state": ["WA"]}},
  "sort": {"by": "total_revenue", "order": "desc"},
  "size": 5
}

# 2. A cause + an efficiency screen: Feeding America, Houston Food Bank
{
  "filters": {"organization": {"causes": "hunger relief"},
              "financials": {"program_expense_ratio": {"min": 0.90},
                             "total_revenue": {"min": 5000000}}},
  "sort": {"by": "total_revenue", "order": "desc"},
  "size": 5
}

# 3. Grantmakers working on a cause: Dana-Farber, AACR
{
  "filters": {"organization": {"causes": "cancer research",
                               "org_type": "foundation"}},
  "sort": {"by": "grants_paid", "order": "desc"},
  "size": 5
}

Python: search, then go deep with Data Pro

import requests

HEADERS = {"x-api-key": "YOUR_API_KEY"}

# 1. Find California food banks with $10M+ revenue, biggest first
search = requests.post(
    "https://api.givalgo.ai/v1/data",
    json={
        "filters": {
            "geography": {"state": ["CA"]},
            "organization": {"ntee_major_codes": ["K"]},
            "financials": {"total_revenue": {"min": 10000000}},
        },
        "sort": "revenue_desc",
    },
    headers=HEADERS,
).json()

top = search["results"][0]
print(top["name"], top["ein"], top["website"])

# 2. Full profile for the top hit
deep = requests.get(
    "https://api.givalgo.ai/v1/data-pro",
    params={"ein": top["ein"]},
    headers=HEADERS,
).json()
print(deep["financial_profile"]["expense_composition"]["program_expense_ratio"])

JavaScript

const r = await fetch("https://api.givalgo.ai/v1/data", {
  method: "POST",
  headers: { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ search_terms: "Human Rights Watch" }),
});
const { results, total } = await r.json();
console.log(total, results[0].name, results[0].mission);

Endpoints

POST   /v1/data   Data

The Data API. JSON body with search_terms (EIN / organization name), filters, or both; see Request Body. Returns a page of lean org records; the full field list is under Data API Response.

The same endpoint also answers GET /v1/data?ein={ein}, a body-less convenience for single-org lookups, identical to POSTing {"search_terms": "{ein}", "size": 1}. The deep Form 990 profile is served on /v1/data-pro.

GET   /v1/data/suggest?q={text}   Data

Autocomplete: up to 10 organizations matching a partly typed name or EIN, for a search box dropdown. See Autocomplete.

GET   /v1/data-pro?ein={ein}&tax_year={year}   Pro

The deep single-org profile (~410 fields): all core sections plus financials.detail, governance.detail, disclosures, and registration. Documented under Data Pro Response.

Parameters (GET endpoints)

ParameterTypeRequiredDescription
einstringYesEmployer Identification Number. Accepts 53-0196605, 530196605, or 53 0196605.
tax_yearintegerNo/v1/data-pro only: 4-digit fiscal year (e.g. 2022). Omit to get the most recent filing. If the requested year has no filing, the response is a 404 with a years_available array. Ignored on GET /v1/data (lean records are latest-year only).

There is no include parameter. The endpoint is the tier contract: /v1/data-pro returns every Pro section on every request, and data_freshness.includes_applied is always ["all"]. An ?include= on the query string is ignored: a request with and without it returns byte-identical JSON. You never need it to obtain a section.

Search Request Body

POST /v1/data takes a JSON body. At least one of search_terms and filters is required.

FieldTypeDescription
search_termsstringEIN ("13-2875808") or organization name. Keywords match organization names only, not mission or cause. Combines with filters. For cause, use filters.organization.causes or ntee_major_codes.
filters.organization.causesstring | string[]Search by cause in plain English: "hunger relief", "adult literacy". Resolved to NTEE codes server-side; composes with every other filter. Only the cause is read; place and money inside the phrase are ignored (use filters.geography / filters.financials). A phrase that maps to nothing returns 400 UNRESOLVED_CAUSES, never an empty page. Max 5, 200 chars each. Daily limit on new phrases: each key can resolve 100 phrases a day that have never been resolved before (resets at UTC midnight). A phrase already resolved once is free and unlimited, as are NTEE codes passed directly. Every response that uses causes carries causes_quota {period, limit, used, remaining, resets_at}.
filtersobjectStructured search. See Filters.
sortstring | objectrelevance (default), revenue_desc, revenue_asc, assets_desc, grants_desc, name_asc; or a {"sort_by": "total_revenue", "ascending": false} object.
fromintegerResult offset (default 0). Must be a multiple of size; from + size ≤ 2000.
sizeintegerResults per page, 1–50 (default 25).

Filters

Three sections: geography, organization, financials. Filters AND together across fields; lists within a field are OR. Alternate field nesting (organization.properties.*, financials.most_recent_year.*) is accepted for painless migration from other nonprofit data APIs, as are the aliases noted below. Unsupported fields (e.g. subject_codes, county, last_updated) return 400 UNSUPPORTED_FILTER with a hint naming the nearest equivalent.

geography

FieldTypeDescription
statestring | list2-letter state code(s), e.g. ["OH", "MI"].
citystring | listCity name(s). Metro aliases expand: "New York" covers all five boroughs.
zipstring5-digit ZIP.

organization

FieldTypeDescription
ntee_major_codeslistNTEE major letters, e.g. ["B", "E"].
ntee_codeslistFull NTEE codes, e.g. ["B21"]. Alias: ntee_minor_codes.
ntee_modestringany (default) or all.
subsection_codeslistIRS subsection. Accepts "03", 3, "501c3", or "501(c)(3) Public Charity".
org_typestringfoundation (grantmakers) or nonprofit (grantseekers). Roles are non-exclusive.
exclude_revoked_organizationsboolDrop revoked orgs. Also accepted under properties and as exclude_defunct_or_merged_organizations.

financials

Ranges are {"min": n, "max": n} objects (either bound optional). Also accepted nested under most_recent_year.

FieldDescription
total_revenue, total_assets, net_assetsLatest-filing dollar ranges.
grants_madeGrants the org paid out (latest filing). Alias: total_giving.
revenue_5y, grants_5yPeak revenue / grants paid across the last 5 filings, which catches orgs whose latest year understates them.
program_expense_ratio0–1. Program share of total expenses.
months_of_cashLiquid reserves in months of operating expenses.
revenue_concentration0–1 concentration of revenue sources (higher = more concentrated).
revenue_growth_streak_min, revenue_decline_streak_min, grants_growth_streak_min, grants_decline_streak_minInteger 1–5: minimum consecutive years of YoY growth / decline.

The financial-health screens (ratio, cash, streaks, concentration) aren't available in traditional nonprofit data APIs; they're computed from the full filing history.

Data API Response: the Lean Org Record

{
  "mode": "search",
  "result_kind": "orgs",
  "total": 512,
  "total_is_capped": false,
  "from": 0, "size": 25, "took_ms": 412,
  "suggestion": null,
  "results": [ { /* lean org record */ } ]
}

Each result is a lean org record (~40 fields):

GroupFields
Identityein, name, legal_name (the IRS legal name; the same as name unless a familiar name is shown instead, e.g. name “University of Michigan”, legal_name “Regents of the University of Michigan”), dba_name, mission, logo_url, logo_tone, logo_bg, logo_view_url (logo_tone: "light" = white/light mark, invisible on white; render it on logo_bg or any dark tile. logo_url is the raw transparent asset: opening a light logo's URL directly shows a blank white page. To see any logo in a browser, open logo_view_url, which renders tone-aware)
Geographycity, state, zip, address
Contactwebsite, contact.email, contact.phone, contact.contact_url (website-scraped)
Classificationntee_code, ntee_description (its major group), ntee_sub_description (the code’s own title: “Universities” for B43), ntee_inferred (our predicted cause code for the ~259K organizations the IRS never coded; present only when ntee_code is null, so an inferred code never overwrites an IRS one), subsection, deductibility, org_type, is_grantmaker, is_grantseeker, legal_status
Standingirs_status, pub78_verified, delisted, delisted_reason, ruling_year
Financialslatest_tax_year, total_revenue, total_expenses, total_assets, net_assets, grants_paid, grants_received_total, total_employees
Screensprogram_expense_ratio, months_of_cash, revenue_growth_streak, revenue_decline_streak, grants_growth_streak, grants_decline_streak, revenue_concentration, relevance_score

Autocomplete

GET /v1/data/suggest fills a search box dropdown as the user types. Call it with the text typed so far and show the rows it returns. It matches names and EINs only, with no causes or financial filters. When the user submits the box, run the full search with POST /v1/data; when they pick a row, open that organization by its ein.

GET   /v1/data/suggest?q={text}   Data

Parameters

ParameterTypeRequiredDescription
qstringYesThe text typed so far: part of a name (red cro) or of an EIN (13-28). Every word must match the start of a word in a name, so any word may be partly typed. Three or more digits alone (with or without the hyphen) are read as the start of an EIN. Fewer than 2 characters returns an empty list; at most 100.
sizeintegerNoRows to return, 1 to 10 (default 8).
statestringNoTwo-letter state code. Only organizations in that state are returned.
session_tokenstringRecommendedAn ID you create for one search session (a UUID works) and send on every call in that session: 8 to 64 letters, digits, hyphens or underscores. It lets usage be measured per search session rather than per keystroke. Start a new token when the box is cleared or a row is picked.

Response

# GET /v1/data/suggest?q=doctors%20witho&size=1
{
  "suggestions": [
    {
      "ein": "13-3433452",
      "name": "Medecins Sans Frontieres USA Inc",
      "dba_name": "Doctors Without Borders USA Inc",
      "city": "New York",
      "state": "NY"
    }
  ],
  "took_ms": 24
}

# GET /v1/data/suggest?q=care%20usa&size=1
{
  "suggestions": [
    {
      "ein": "13-1685039",
      "name": "Cooperative for Assistance and Relief Everywhere Inc",
      "matched_name": "Care USA",
      "city": "Atlanta",
      "state": "GA"
    }
  ],
  "took_ms": 21
}
FieldTypeDescription
suggestionsobject[]Up to size rows, best match first. Empty when nothing matches.
suggestions[].einstringHyphenated EIN. Pass it to /v1/data, /v1/data-pro or /v1/verify.
suggestions[].namestringThe organization’s name, the same name POST /v1/data returns. It does not change with what was typed.
suggestions[].dba_namestringPresent when the organization does business under another name: the same dba_name POST /v1/data returns.
suggestions[].matched_namestringPresent when the text matched a name the organization is widely known by, such as an abbreviation or a common form of its name (MSF, Care USA), rather than name or dba_name. Show it so users can see why the row matched, for example as “Also known as”.
suggestions[].city, suggestions[].statestring | nullLocation, cased for display.
suggestions[].close_matchbooleanPresent, and true, only on rows found by a close spelling (harvrad). They come only when 3 or fewer organizations match the text as typed, and always after those rows. Show them as their own group, for example under “Did you mean”.
took_msintegerServer time for the request.

Which name to show first is up to you. A common layout leads with dba_name when the typed text starts it, otherwise name, and shows the other name on a second line.

How matching works

  • A word matches the start of a word in any of the organization’s names: its legal name, the names it does business as, and short names it is widely known by. Typing more only narrows the regular rows: one more letter never adds an organization to them.
  • Names that start with the text come first. Among equally good matches, larger organizations come first.
  • Suggestions are not an eligibility check. An organization whose tax exemption was revoked can still appear, ranked below an equally good active match. Confirm with GET /v1/verify before you grant.
  • Names are updated daily.

Calling it from a search box

  • Wait about 150 ms after the last keystroke before calling, and ignore any response that arrives after a newer one.
  • Call it from your server, not the browser, so your API key stays private.

Errors

See Error Codes; the suggest-specific ones are listed there under GET /v1/data/suggest.

Data Pro Response: Top-Level

The top-level blocks of the deep single-org response served by GET /v1/data-pro. (For the Data API's lean search records, see Data API Response.) Customers receive the complete field-by-field reference; see Full field reference.

Form 990-N (e-Postcard) filers. Organizations that have only ever filed the postcard return a profile with identity and full IRS registration, and empty financial sections, because the IRS collects no financial data on that form. Check data_availability to tell that apart from a gap.

FieldTypeDescription
einstringHyphenated EIN (e.g. "53-0196605").
tax_yearintegerFiscal year of the filing returned.
return_typestring"990", "990EZ", "990PF", or "990N" (e-Postcard).
data_availabilityobjectPresent only when return_type is "990N". {financials: false, reason: "…"}. The IRS collects no financial data on the e-Postcard, so those sections are empty because nothing was filed, not because we are missing it. The key’s absence means a full return, where empty financials would be a real gap.
filing_datestring (ISO date)Date IRS received the filing.
organizationobjectIdentity, address, NTEE code, mission, formation year, total employees/volunteers.
financialsobjectSummary, revenue breakdown, expenses, balance sheet, and (Pro only) detail sub-object.
trendsobjectUp to 6 years of annual data + computed CAGRs + trend labels.
compensationobjectPeople array, by-role highlights, per-employee average, peer benchmark cubes.
funding_sourcesobjectGrants received: total_unique_grantmakers, grantmakers[], annual_totals[].
grants_madeobject[]Outbound grants (Schedule I), top 50 for the year. Data Pro only, always present.
programsobject[]Program service accomplishments.
governanceobjectBoard structure, policies, meeting minutes flags.
fundraisingobjectmetrics, events[], contractors[], activity_methods, licensed_states.
financial_profileobjectLiquidity, revenue concentration (HHI), operating sustainability, expense composition New. All with peer benchmarks.
related_organizationsobject[]Schedule R-style related entities.
foreign_activitiesobject[]Schedule F Part I region-level summary.
lobbying_and_politicalobjectSchedule C summary.
contributor_summaryobjectAlways an empty object. The IRS redacts Schedule B in public filings, so no contributor data is available to return.
donor_advised_fundsobject | nullDAF flags + balances (if Schedule D.I present).
conservation_easements NewobjectSchedule D Part II. Always-on; present only when held.
art_collections NewobjectSchedule D Part III. Always-on; present only when held.
private_foundation NewobjectForm 990-PF metrics. Always-on; 990-PF filers only.
hospitals NewobjectSchedule H. 501(c)(3) hospital filers only; always present for them on Data Pro.
disclosures ProobjectSchedules K/L/M/N/F/A/O. Data Pro only, always present.
registration ProobjectIRS BMF + Pub 78 + California AG/FTB status, joined at request time.
data_freshnessobjectfiling_source, most_recent_tax_year, filing_date, last_filing_date, years_available[], includes_applied[].

Full field reference

Data Pro returns about 410 fields across the blocks above: line-item financials, Schedule detail, governance and compensation records, registration status, and peer benchmarks across size, sector and geography. The complete field-by-field reference, with definitions and benchmark methodology, is provided to customers, and to prospects under NDA. Contact sales@givalgo.ai.

data_freshness

FieldTypeDescription
filing_sourcestringAlways "IRS TEOS e-file".
most_recent_tax_yearintegerLatest filing year ingested for this EIN.
filing_datestringFiling date of the response year.
last_filing_datestringMost recent filing date for this EIN overall.
years_availableinteger[]All tax years on file.
includes_appliedstring[]Diagnostic: which internal bundles applied to this response (empty on base).

Error Codes

StatusErrorMeaning
400MISSING_EINThe ein query param is required.
400INVALID_EINEIN is not 9 digits / not parseable.
400INVALID_TAX_YEARtax_year must be a 4-digit integer.
403IP_NOT_ALLOWEDThe calling IP is not on the allowlist for this API key.
403ENDPOINT_NOT_LICENSEDYour key reached an endpoint your agreement does not include; access is scoped per product. The body names the product you asked for (requested_product) and lists the ones your key holds (licensed_products), so you can tell a licensing boundary apart from a bug without contacting us.
403ForbiddenMissing or invalid x-api-key header.
404NOT_FOUNDNo filing of any kind for this EIN, including Form 990-N (or none for the requested year). Response includes years_available[], which counts postcard years too. Data Pro only.
500INTERNAL_ERRORUnexpected server error. request_id included for support tickets.

Search-specific errors (POST /v1/data)

StatusErrorMeaning
400EMPTY_REQUESTBody needs search_terms, filters, or both.
400PROMPT_NOT_SUPPORTEDprompt is not accepted. Send search_terms and filters instead; filters.organization.causes takes a cause in plain English.
400UNKNOWN_FIELDUnrecognized field; the message names the JSON path.
400UNSUPPORTED_FILTERA recognized field from other nonprofit data APIs that we don't carry; the hint names the nearest equivalent.
400INVALID_FILTERS / INVALID_RANGE / INVALID_SORT / INVALID_SIZE / INVALID_PAGINATION / INVALID_SEARCH_TERMSField-level validation; the message says which field and why.
400MULTI_EIN_NOT_SUPPORTEDOne EIN per request (multi-EIN monitoring is planned).
429CAUSE_QUOTA_EXCEEDEDThe request has more never-resolved causes phrases than the key has left today. Nothing ran. Body carries new_causes (the phrases that would count) and causes_quota; Retry-After header set. Phrases resolved before, and NTEE codes, still work.
502UPSTREAM_ERRORSearch engine temporarily unavailable; retry.
504UPSTREAM_TIMEOUTSearch engine timeout; retry.

Suggest-specific errors (GET /v1/data/suggest)

StatusErrorMeaning
400UNKNOWN_PARAMETERA query parameter other than q, size, state and session_token.
400INVALID_QUERYq is longer than 100 characters.
400INVALID_SIZEsize is not a whole number from 1 to 10.
400INVALID_STATEstate is not a two-letter code.
400INVALID_SESSION_TOKENsession_token is not 8 to 64 letters, digits, hyphens or underscores.
503SUGGEST_UNAVAILABLESuggestions are temporarily unavailable; retry.

FaithVerify API

Verify a church from its name, city and state. No EIN required; for most churches, none exists.

POST /v1/faith-verify
{"name": "First Baptist Church", "city": "Dallas", "state": "TX"}

Every response carries a verdict with a plain-language recommendation, and the evidence behind it.

Why churches are different

A church is exempt under 501(c)(3) automatically, without applying, and is not required to file a Form 990 or appear in Publication 78 (IRC §508(c)(1)(A)).

Two consequences shape this API:

  • The EIN often does not exist. GET /v1/verify starts from one, so it cannot answer for a congregation that never applied.
  • Absence from IRS lists is normal, not suspicious. A church missing from Publication 78 has not failed anything: it was never required to be there. Treating that absence as a negative signal would decline legitimate churches at scale.

So FaithVerify gathers evidence in layers and tells you which layer answered, rather than reducing everything to present-or-absent in one list.

Quickstart

curl -X POST https://api.givalgo.ai/v1/faith-verify \
  -H "x-api-key: $GIVALGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "First Baptist Church", "city": "Dallas", "state": "TX"}'

A church the IRS or a denomination vouches for answers immediately:

{
  "triage":   { "status": "resolved", "matched_in": ["denominational_directory", "irs_bmf"] },
  "irs":      { "ein": "75-0800696", "irs_classified_church": true },
  "verdict":  { "status": "ELIGIBLE", "deductible": true, "basis": "group_exemption" }
}

If the records do not settle it, you get 202 and a poll URL instead. See Website evidence.

Verify a church

POST /v1/faith-verify

FieldRequiredNotes
nameyesAs you know it. Abbreviations resolve: “First UMC” finds “First United Methodist Church”.
cityyes
stateyesTwo-letter code (OH) or the full name (Ohio), either case. Send DC or District of Columbia for the District; Washington alone is the state.
streetnoNeeded only when a call returns needs_street. Supplying it up front never hurts.
skip_website_evidencenoStay synchronous: never return 202, take the record answer as final.
force_refreshnoRe-crawl even if a review from the last 30 days exists.

Poll a website review

GET /v1/faith-verify/{job_id}

Returns the complete verification (triage, irs, denominational, website_evidence and verdict), not just the crawl. You never hold half an answer.

The record-layer findings are exactly as they stood when the job was created, so the verdict stays pinned to the facts that produced it.

Cadence: most reviews finish in under two minutes. Poll first at ~20s, then every 10s, and give up after 5 minutes.

status is queued or running while it works, then done, including when the review was inconclusive, which is a result rather than a failure. failed means our own error and is safe to retry.

Job ids are scoped to the key that created them; polling another key’s returns 404.

Three outcomes

All three return 200. Branch on triage.status.

StatusMeaningWhat to do
resolvedWe know which church you mean.Read the verdict.
needs_streetWe cannot pin the church to one record from the name alone: either several match, or the closest listing names a town rather than a congregation and does not carry your name.Re-send with street; triage.candidate_addresses lists what we hold. When reason is location_only_listing the website review runs too, so a street adds a record match rather than identifying the church.
not_foundNo record matched: not in the IRS BMF, not in the 28 denominational directories, in your city or within 5 miles of it. It is not a statement that the church does not exist.Nothing: this is what launches the website review. If that review comes back empty too, check the spelling or try the name as it appears on the building.

A name that identifies a town is not an answer. Some denominations file congregations under the place rather than a congregation name: an LDS ward listed as Batesville, a Kingdom Hall listed as its address. Such a record is only returned when you wrote that same name; ask about a different church in that town and you get needs_street with the addresses to choose from, never the ward.

A church one town over is still found. Congregations routinely file with the IRS under a neighbouring municipality, so matching your city exactly reports real churches as missing: A Restoration Church, Glenview IL is in the IRS file under Northbrook, 3.8 miles away. When your city matches nothing, we search the towns within 5 miles and return triage.nearby_city_match naming the city we actually found it in and the distance. A church in the town you named always wins; the radius is only a fallback.

When we hold no record of your church, the website review runs. That is what it is for. We ask you to choose an address only when the choice is real: several congregations we genuinely cannot tell apart, or a record carrying your church's name at a different address. If nothing we hold matches the name you gave, you get not_found and a website review, not a list of other people's churches.

needs_street and not_found are answers, not errors. Your request was well-formed; we could not settle it. A 4xx would put them in the same bucket as a malformed body and invite a retry instead of an action.

A city holds six Kingdom Halls and four St Mary’s. When a name identifies a denomination and a town rather than a congregation, guessing would produce a confidently wrong answer, so we ask instead.

The three evidence layers

Gathered strongest first, stopping at the first that answers. An answer from the IRS is not improved by reading a website.

LayerSourceResponse
1 · IRSEO BMF → Publication 78, directly or via the parent’s group exemption. Includes revocation and OFAC screening.Immediate
2 · DenominationalCurrent congregation directories published by denominations. A listing shows the congregation exists and is affiliated.Immediate
3 · WebsiteThe five characteristics the IRS uses to recognise a church, read from the church’s own site.202 → poll

Many churches are covered by a denomination’s group exemption rather than holding their own determination. We follow that link: a subordinate not listed in Publication 78 is checked against its parent, and irs.group_exemption reports the parent’s EIN and standing.

Website evidence

Reached only when neither record layer settles the question. We find the church’s website, confirm it belongs to that congregation, read it, and grade five criteria.

CriterionWhat comes back
ministersOrganized leadership: every named leader or officer with their title, plus how leadership is constituted. Satisfied by ordained clergy or a named governing body: the IRS criterion is "an organization of ordained ministers" or "a definite and distinct ecclesiastical government", and many traditions ordain nobody.
doctrineThe denomination claimed, the topics the statement covers, and the church’s own wording of what it believes.
servicesEach regular gathering as day, time, and the schedule line exactly as printed.
calendarNamed events with dates, recurring programmes, and what the church’s month looks like.
contactStreet address, city, state, ZIP, phone and email.

Each carries a status, a summary, and evidence_url (the page the finding came from), so you can check it yourself.

Every value is verified against the page

Anything you might act on (an address, a phone number, an email, a service time, a person’s name) is dropped if we cannot find it on the page we read, and listed in facts.unverified_dropped. That distinction matters: “the church publishes no phone number” and “a phone number was produced that we could not confirm” are different facts.

Doctrinal statements are kept with verbatim: false when they are close paraphrases rather than quotes.

not_found is not fail

A criterion is not_found when no page addressing it was available to read, and fail when the topic was covered and the characteristic was genuinely absent. A church with a thin website has not failed the test.

What a denominational listing means, and what it does not

A listing is evidence the congregation exists and is affiliated with that body. It is not confirmation of IRS group-exemption coverage. Where coverage exists we report it from the organisation’s own IRS record, under irs.group_exemption, never inferred from the directory, and never estimated from how the denomination usually files.

What the sanctions screening covers

The organization’s name, and nothing else. A church does not file a Form 990 (that is the premise of this endpoint), so we hold no officer or director records for one. Rather than return an empty leadership_screening block that reads as “we checked the leadership and found nothing”, the block is omitted and sanctions_screening.scope says what was covered.

Leaders named on the church’s own website appear under website_evidence and have not been screened. If a church does have officers on record, the leadership_screening block is present and real.

We do not score it

evidence.tally counts the five criteria by status: pass, partial, fail, not_found. That is all it does. There is no threshold and no pass mark, because the IRS sets none: its own guidance says “there is no minimum number of criteria an organization must meet” and warns that a numerical approach “might unconstitutionally favor established churches at the expense of newer, less traditional institutions”. We collect the evidence; the rule for acting on it is yours.

Results are cached for 30 days per church. Two requests arriving together share one crawl.

Limited release

FaithVerify is enabled per API key while we finish validating it against real queries. If your key does not have access yet, every FaithVerify route answers 403 with error: "FORBIDDEN". Your key is fine, this endpoint just is not switched on for it. Email support@givalgo.ai to request access. Every other endpoint is unaffected.

Verdicts

The status says where the church was found, not how eligible we judge it. What was found lives in deductible, basis and message.

StatusMeaning
FOUND_IRSAn IRS record: Publication 78, a group exemption whose parent is listed, or the church classification itself.
REVOKED_ON_IRSThe IRS has auto-revoked the organization’s exempt status. Also an IRS record, but kept separate because it is the one finding that means no; it overrides every positive basis, and carries deductible: false.
FOUND_DENOMINATIONAL_DIRECTORYThe congregation appears in a denomination’s own current directory, and nowhere in IRS data.
FOUND_WEBSITE_EVIDENCENo record in either, so we read the church’s own website against the five IRS characteristics.
UNDETERMINEDWe looked everywhere and came back empty: no IRS record, no directory listing, and either no website found or a review that produced nothing. Not the same as ineligible: no evidence, not evidence against.

Revocation is the exception, and it has its own status. Everything else the status tells you is where we found the church; deductible and basis tell you what we found. Two cases are not visible in the status at all: a church whose IRS row contradicts itself returns FOUND_IRS with deductible: null and basis: "irs_church_classification_contradicted", and a group subordinate whose own row contradicts the group listing returns basis: "group_exemption_contradicted", also with deductible: null. Branch on deductible and basis if that matters to you.

verdict.basis names what the answer rests on, and verdict.recommendation says what to do next in plain words.

Deductibility code 2 does not by itself decline a church. Where the church's own Publication 78 listing settles the question, deductible stays true and the contradictory code is reported next to it in caveat. Where the answer rests on a group ruling or on the church classification alone, the code is not outvoted: deductible becomes null and the basis is group_exemption_contradicted or irs_church_classification_contradicted. /v1/verify answers the same way for the same organization, with CAUTION.

Website evidence never moves the verdict at all. It stays UNDETERMINED with basis: "website_evidence", and the marks, the extracted facts and the tally are attached for you to judge. Five characteristics on a website are not an IRS determination, and neither is a count of them, which is why we stopped publishing one.

Research API

Send an EIN and get back a Diligence Brief: the same brief grantmakers generate on Givalgo Discover, as JSON and as a branded PDF.

A brief reads the organization's Form 990 filings, runs the compliance checks of Verify (IRS status, Pub 78, auto-revocation, OFAC on the organization and its leaders, California registries), benchmarks it against its peers, reads its website and checks what the website says against what it filed. It leads with at most three findings that matter, and ends with the questions worth asking the organization.

Every section says where it comes from. IRS, Treasury and state records are verified; what the organization says on its own website is self_reported; the website checked against the filings is reconciled; the summary, the wording of each finding and the questions are written by AI and marked synthesis. The AI works only from the facts in the brief. See Provenance and AI.

A brief is decision support, not a score, rating or recommendation to donate.

Authentication

Send your API key in the x-api-key header on every request, exactly as with the other Givalgo APIs.

curl -X POST https://api.givalgo.ai/v1/research \
  -H "x-api-key: YOUR_API_KEY" \
  -H "content-type: application/json" \
  -d '{"ein": "12-3456789"}'

Don't have a key? Contact support@givalgo.ai.

Quickstart

Step 1: request the brief.

curl -X POST https://api.givalgo.ai/v1/research \
  -H "x-api-key: YOUR_API_KEY" \
  -H "content-type: application/json" \
  -d '{"ein": "12-3456789"}'
{
  "brief_id": "br_Q2x9mVt4cL0aZ7pK1nYw3g",
  "status": "running",
  "ein": "12-3456789",
  "cached": false,
  "poll": "/v1/research/br_Q2x9mVt4cL0aZ7pK1nYw3g",
  "pdf": "/v1/research/br_Q2x9mVt4cL0aZ7pK1nYw3g/pdf",
  "expected_ready_in_seconds": 180,
  "quota": {"plan": "pro", "period": "month", "limit": 50, "used": 3,
            "remaining": 47, "resets_at": "2026-11-01T00:00:00Z"}
}

Step 2: poll every 15 seconds until status is done. A new brief takes about 2 minutes, and most finish within 4.

curl https://api.givalgo.ai/v1/research/br_Q2x9mVt4cL0aZ7pK1nYw3g \
  -H "x-api-key: YOUR_API_KEY"
{
  "brief_id": "br_Q2x9mVt4cL0aZ7pK1nYw3g",
  "status": "done",
  "ein": "12-3456789",
  "requested_at": "2026-10-06T15:02:11Z",
  "completed_at": "2026-10-06T15:04:03Z",
  "pdf": "/v1/research/br_Q2x9mVt4cL0aZ7pK1nYw3g/pdf",
  "brief": {
    "name": "Example Community Food Bank",
    "verdict": {
      "standing": {"state": "items_to_confirm"},
      "org_one_liner": "A county food bank distributing groceries through 40 partner pantries.",
      "at_a_glance": {"compliance": "ELIGIBLE", "revenue_latest": 4210000,
                      "months_of_cash": 5.2, "program_pct": 0.91},
      "provenance": "synthesis"
    },
    "glaring_flags": [
      {"id": "funder_concentration", "severity": "medium", "category": "funding",
       "title": "Concentrated institutional funding",
       "detail": "One funder supplies about 60% of the grants tracked in IRS filings.",
       "provenance": "verified"}
    ],
    "question_set": [
      {"question": "How would you replace your largest funder's support if it ended?",
       "why": "One funder supplies most institutional grants.",
       "category": "financial", "basis": "desk_limit"}
    ],
    "compliance": {"verdict": "ELIGIBLE", "deductible": true, "provenance": "verified"},
    "financials": {"latest": {"tax_year": 2024, "total_revenue": 4210000}, "provenance": "verified"},
    "website_research": {"fetch_status": "ok", "provenance": "self_reported"}
  }
}

The example is shortened; the full shape is in What a brief contains.

Step 3 (optional): download the PDF.

curl https://api.givalgo.ai/v1/research/br_Q2x9mVt4cL0aZ7pK1nYw3g/pdf \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/pdf" -o brief.pdf

Request a brief

POST /v1/research

FieldTypeRequiredDescription
einstringYesThe organization's EIN, with or without the hyphen. Send it as a string: a JSON number drops leading zeros.
force_refreshbooleanNoWrite a new brief even when one from the last 30 days is on file. Counts against your quota. If a brief is already being written, you join that one instead.

Any other field is refused with 400 UNKNOWN_FIELD. Briefs written against a funding mandate are not available through the API yet (400 MANDATE_NOT_AVAILABLE).

Always returns 202 with a brief_id. status is running while the brief is being written, or done when one was already on file ("cached": true). The brief is never in this response, so always poll; done only means your first poll returns it.

FieldMeaning
brief_idYour id for this request. Scoped to your API key.
cachedtrue when a brief from the last 30 days answered this request. Only a request that starts a new brief counts against your quota.
poll, pdfThe paths to poll and to download the PDF.
expected_ready_in_secondsA guide for your first poll. 0 when the brief is already written.
quotaYour monthly allowance as of this response: limit, used, remaining, resets_at. See Quota and caching.

Poll a brief

GET /v1/research/{brief_id}

statusMeaning
runningBeing written. Poll again in 15 seconds.
doneThe full brief is in brief, and the PDF is available.
failedWe could not write it. message says what to do. A failed brief does not count against your quota; request it again.

A brief_id always returns the brief it was issued for, even after the organization's brief is rewritten for someone else. Brief ids are scoped to the API key that created them; another key's id returns 404 BRIEF_NOT_FOUND. We keep your requests for 12 months.

Download the PDF

GET /v1/research/{brief_id}/pdf with Accept: application/pdf

The brief as a branded PDF, the same document Discover users download. Available once polling returns done; before that you get 409 BRIEF_NOT_READY. Downloading does not count against your quota.

Errors

ErrorStatusMeaning
MISSING_EIN, INVALID_EIN400No EIN, or not nine digits. Send the EIN as a string.
UNKNOWN_FIELD, INVALID_FIELD400A field this endpoint does not take, or force_refresh that is not true or false.
MANDATE_NOT_AVAILABLE400Briefs written against a funding mandate are not available through the API yet.
BRIEFS_NOT_IN_PLAN403Your plan does not include briefs. Contact sales@givalgo.ai.
NO_FILINGS404We hold no Form 990 filings for this EIN, so there is nothing to brief. Organizations that do not file can still be checked with Verify.
BRIEF_NOT_FOUND404No brief with that id for your API key.
BRIEF_NOT_READY, BRIEF_FAILED409PDF only: poll until done, or request a new brief.
BRIEF_QUOTA_EXCEEDED429This request needs a new brief and your monthly allowance is used up. Carries Retry-After and a quota object. Briefs already on file are still served.
UPSTREAM_TIMEOUT, UPSTREAM_ERROR504, 502Our research service did not answer. Retry; the request did not count against your quota.

What a brief contains

Sections arrive in the order a reader meets them in the PDF: the answer first, then the evidence.

SectionProvenanceWhat it holds
verdictsynthesisstanding.state (below), a one-sentence org_one_liner, and at_a_glance: compliance status, latest revenue, months of cash, program share, number of institutional funders.
exec_summarysynthesisheadline and narrative.
glaring_flagsper flagAt most three findings that matter: id, severity (high, medium, low), category, title, detail, provenance.
appendix_flagsper flagEvery other finding, same shape.
question_setsynthesisQuestions to put to the organization, each with why, a category, and a basis: divergence (the website and the filings disagree) or desk_limit (public records cannot answer it).
complianceverifiedThe Verify answer for this EIN when the brief was written: verdict, deductibility, Pub 78, revocation history, OFAC screening of the organization and its leaders, California registries.
financialsverifiedOne row per filed year, the latest year, and computed trends.
fundersverifiedGrants received, from the funders' own IRS filings, with the top funder's share. Supporters named on the website are checked against them (reconciled).
programs, program_reconciliationverified, reconciledPrograms as filed, matched against the programs the website describes.
governance, leadershipverifiedBoard size and independence, governance policies, the highest-paid person and their compensation.
peersverifiedEach metric with the organization's value and its peer group's 25th percentile, median and 75th percentile, and the peer group (cause, size and state).
website_researchself_reportedWhat the website states (mission, leadership, impact claims, named supporters, board, staff, audit firm), each with its source_url; each claim checked against the 990 in audit_findings; and which annual reports, impact reports and audited statements we found.

verdict.standing.state

Computed from the compliance checks and the glaring findings, never by AI.

ValueMeaning
compliance_issueRevoked, or not eligible for tax-deductible gifts.
material_itemsA sanctions screening returned a potential match, or a high-severity finding.
items_to_confirmFindings worth confirming with the organization.
clearNo glaring issues in the verified data.

When we could not read the website (website_research.fetch_status other than ok or partial), the brief stands on the filings alone. On a private foundation (org.return_type 990PF), governance and peer sections can be empty: the brief is designed for public charities.

Provenance and AI

provenanceSource
verifiedIRS filings, Pub 78 and revocation lists, Treasury OFAC, California registries.
self_reportedThe organization's own website, as it states it.
reconciledThe website checked against the filings.
synthesisWritten by AI from the facts in this brief.
A brief is written with AI. A language model reads the organization's website and writes the summary, the wording of each finding and the questions to ask, working only from the facts shown in the brief. It does not add facts of its own, but it can misread a source, so check anything material against the source before relying on it. If you show briefs to your own users, label them as AI-written. The PDF says so on its cover and in its disclaimer.

Quota and caching

One brief is kept per organization, fresh for 30 days, and shared: whoever asked first, a request inside those 30 days gets the same brief at no cost to your quota. The same is true while a brief is already being written. generated_at in the brief says when it was written; force_refresh: true writes a new one.

Each API key has a monthly allowance of new briefs, shown on every request as quota. It resets on the first of the month (UTC).

CountsDoes not count
A request that starts a new brief, including force_refreshA brief already on file ("cached": true), joining one being written, a brief that failed, polling, the PDF

At the limit, briefs already on file are still served; only a request that needs a new brief returns 429 BRIEF_QUOTA_EXCEEDED, with Retry-After. For a larger allowance, contact sales@givalgo.ai.

Organizations API

List every nonprofit in a place (a county, a city, a ZIP, or a whole state) with a lean identity-and-location record and a cursor that does not run out.

Built for the case where you need the whole set rather than the best matches: a community foundation mapping its service area, a directory keeping a county in sync, a dashboard counting nonprofits per county.

When to use this instead of the Data API

/v1/organizations Organizations/v1/data Data
Question“Give me every org in here”“Which orgs best match this?”
OrderingStable, by EINBy relevance
Depth limitNone: page until has_more is false2,000 results
RecordLean: identity + locationRich: financials, mission, contact, NTEE detail
County filterYesNo
Free-text searchNoYes

Rule of thumb: if you would page through more than a couple of hundred results, or you need a county, use this endpoint. If you are searching by name, mission or cause, use the Data API.

Quickstart

Every 501(c)(3) in Harris County, Texas, a hundred at a time:

curl -H "x-api-key: $GIVALGO_API_KEY" \
  "https://api.givalgo.ai/v1/organizations?state=TX&county=Harris&subsection=03&limit=100"
{
  "organizations": [
    {
      "ein": "01-0610009",
      "name": "HOPE CENTER INC",
      "dba_name": null,
      "street": "3618 STASSEN ST",
      "city": "HOUSTON",
      "state": "TX",
      "zip": "77051-1544",
      "county": "Harris County",
      "ntee_code": "T20",
      "ntee_source": "irs",
      "ntee_inferred": null,
      "subsection": "03"
    }
  ],
  "count": 100,
  "next_cursor": "010610009",
  "has_more": true
}

Then walk the cursor until has_more is false:

curl -H "x-api-key: $GIVALGO_API_KEY" \
  "https://api.givalgo.ai/v1/organizations?state=TX&county=Harris&subsection=03&limit=100&cursor=010610009"

List organizations

GET   /v1/organizations   Organizations

One endpoint. view chooses what comes back: organizations (the default), counts, or the change feed. Every filter below means the same thing in all three.

viewReturns
list (default)Organizations.
facetsCounts along one dimension. Needs by.
changesThe change feed: events, not organizations.

A scope is required: at least one of state, county, city or zip. Enumerating the whole Business Master File is a bulk export rather than a page; contact us if that is what you need.

Typical latency is ~90ms state-wide and ~180ms for a county, regardless of how large the county is.

A city is not a metro

These three scopes answer three different questions about “Houston”:

ScopeOrganizations
city=HOUSTON15,977
county=Harris20,207
The nine-county Houston metro31,951

The city field is the municipality the organization files under, which is not the same as the area it serves. For a metro, request each county and merge.

Filters

All four endpoints take the same filter vocabulary, so learning one teaches you the rest.

ParameterTypeDescription
statestringTwo-letter code. Required when county is given.
regionstringA named region: bay area, midwest, north florida… Counts as a scope on its own. See Regions.
countystringOne county or a comma-separated list of up to 25. With or without the County suffix. Louisiana parishes and Alaska boroughs match under their census names. Requires state.
citystringCity as the IRS records it, matched case-insensitively.
zipstring5-digit ZIP. A ZIP+4 is accepted and truncated.
nteestringComma-separated NTEE codes, matched by prefix. B matches every B*; B21 matches B21.
ntee_sourcestringirs (default), inferred, or inferred_soft. See below.
subsectionstringComma-separated 501(c) subsections. 03, 3, 501c3 and 501(c)(3) all mean the same thing.
viewstringlist (default), facets, changes.
ruling_sincestringYYYY-MM. Only organizations ruled at or after this month.
sortstringein (default), revenue_desc, revenue_asc, assets_desc, name_asc, ruling_desc. See Sorting.
limitinteger1–2000, default 250. Bigger is markedly cheaper per organization and costs fewer calls (see Sorting and Pagination).
cursorstringOpaque. Pass back the previous response's next_cursor.

Regions

Rather than naming counties, ask for a region. region uses the same vocabulary as Ask on Givalgo Discover, so “Bay Area” means the same set of organizations in both products. It counts as a geographic scope on its own; no state needed.

Multi-state regions (these constrain the state):

deep south, east coast, great lakes, gulf coast, mid-atlantic, midatlantic, midwest, new england, northeast, northwest, pacific northwest, rust belt, rustbelt, southeast, southwest, sun belt, sunbelt, tri-state, tristate, west coast

Sub-state regions (these constrain state and city):

StateRegions
CAbay area, central coast, central valley, nocal, nor cal, norcal, northern california, so cal, socal, southern california
FLcentral florida, fl panhandle, florida panhandle, ne florida, north florida, northeast florida, northern florida, northwest florida, nw florida, se florida, south east florida, south florida, south west florida, southeast florida, southern florida, southwest florida, space coast, sw florida, tampa bay, tampa bay area, tampa bay region, treasure coast
NYupstate, upstate new york, upstate ny
TXcentral texas, dallas fort worth, dfw, east texas, greater houston, gulf coast texas, houston metro, metroplex, north texas, south texas, texas gulf coast, texas triangle, west texas

Hyphens, underscores and capitalisation are ignored, so Bay Area, bay-area and BAY_AREA are the same request.

How a sub-state region is defined

A sub-state region matches its counties OR its cities: a union, not an intersection.

Counties are the primary definition: socal is the ten Southern California counties (Imperial, Kern, Los Angeles, Orange, Riverside, San Bernardino, San Diego, San Luis Obispo, Santa Barbara, Ventura). The city list is kept as a safety net, because 7.9% of organizations have a ZIP the US Census crosswalk cannot place and so carry no county at all (1,566 of them in Houston alone). Matching on counties only would silently drop them.

Regions deliberately overlap where geography does: Santa Barbara and Ventura are both socal and central coast; Kern is both socal and central valley; Brevard is both central florida and space coast. A nonprofit in Ventura is honestly in both, and forcing a partition would mean picking a loser.

Passing a state the region does not contain returns 400 REGION_STATE_CONFLICT rather than an empty page, because an empty page reads as “no organizations here” when the truth is that the two filters contradict each other.

region and county answer different questions

region is a curated list of city names. county is exhaustive by geography. For the same metro they do not return the same set: the county form finds more, because county lines also catch the smaller towns nobody lists by name. Use region to match how Ask on Givalgo Discover talks about a place; use county when you need every organization in it.

County names are not unique

There are 30 Washington Counties and 25 Jefferson Counties in the United States, so county without state returns 400 COUNTY_NEEDS_STATE rather than guessing. A name we do not recognise returns 400 UNKNOWN_COUNTY with the near misses:

{
  "error": "UNKNOWN_COUNTY",
  "message": "No county named 'Harrs' in TX.",
  "hint": "Did you mean: Harris County, Harrison County?"
}

A typo gets an answer that names the problem, never an empty page that reads as “no organizations here”.

Choosing an NTEE source

Many registered nonprofits were never assigned an NTEE code by the IRS. We infer codes for many of them, and ntee_source decides whether the ntee filter considers those:

ValueMatches
irs (default)Only codes the IRS assigned. Authoritative.
inferredAlso codes we assigned to uncoded organizations with high confidence.
inferred_softAdditionally our lower-confidence assignments.

Every record reports which tier produced its code in ntee_source, so an inferred code is never mistaken for an IRS one. Reach for inferred when recall matters (building a candidate list), and stay on irs when you are going to act on the classification.

ntee_source only decides what the filter matches and what fills ntee_code. Whatever you pass, a record the IRS never coded still carries our guess in ntee_inferred, so a full load gets every inferred code without a second pass.

Note: POST /v1/data folds inferred codes into its NTEE matching with no way to opt out. The same ntee=B therefore returns different counts on the two endpoints. This endpoint defaults to IRS-only on purpose.

The record

FieldDescription
einHyphenated 9-digit EIN.
nameLegal name as the IRS records it.
dba_nameThe organization's doing-business-as name: the newest one it filed on a return, else the IRS's secondary name for it when that is a name the organization goes by. The same value /v1/verify and /v1/data-pro return as also_known_as, display-cased (“Doctors Without Borders USA Inc”). Sparse, because most organizations have none.
streetStreet address from the Business Master File.
city, state, zipAs filed. zip may be ZIP or ZIP+4.
countyDerived from the ZIP via the US Census crosswalk. null where the ZIP is not in that file.
ntee_codeNTEE code. Read together with ntee_source.
ntee_sourceirs, inferred, inferred_soft, or null when the organization has no code at any tier.
ntee_inferredOur inferred cause code for an organization the IRS never coded, on every record whatever ntee_source you passed. The same object /v1/data returns: code, description, confidence, precision, secondary, tier (gated or soft; check it before you trust the code) and method. null whenever the IRS assigned a code, and when we have no guess. It never changes ntee_code or what the ntee filter matches.
subsection501(c) subsection, zero-padded. 03 is a public charity.
display_nameHow the organization is shown on Discover: a hand-set name, else name cased and repaired (“Hope Center Inc”; a word the IRS split or cut completed). Additional: name itself is unchanged. The same value /v1/verify and /v1/data-pro return. null until the nightly refresh reaches the organization.
display_streetstreet, cased for display.
display_citycity, cased for display.

Two forms of every string

name, street and city come back exactly as the IRS publishes them, which means ALL CAPS: HOPE CENTER INC, HOUSTON, 3618 STASSEN ST. That is the stable form, and the one to match against your own records.

Alongside each is a display-cased twin:

VerbatimDisplay
name: HOPE CENTER INCdisplay_name: Hope Center Inc
street: 3618 STASSEN STdisplay_street: 3618 Stassen St
city: HOUSTONdisplay_city: Houston

These are additional fields, not replacements. Match on the verbatim ones, render the display ones. Nothing you already integrated against changes.

Casing is computed once per Business Master File reload and shared with the rest of the platform, so the same organization can never appear differently in two places. It is a hard problem (YMCA is an acronym, KNIGHTS is a word, CT is Connecticut in a name and Court in an address), so a display field is null rather than wrong when we have not computed one yet.

Sorting

Default is ein, a stable enumeration order, which is what you want when mirroring a place. Add sort to rank instead.

curl -H "x-api-key: $GIVALGO_API_KEY" \
  "https://api.givalgo.ai/v1/organizations?state=TX&county=Harris&subsection=03&sort=revenue_desc&limit=1000"

That is the 1,000 largest charities in Houston: Memorial Hermann Health System, Methodist Hospital Group, Texas Children's Hospital, and on down to about $700K.

ValueMeaning
ein (default)By EIN ascending. Stable; use it to enumerate.
revenue_descLargest first.
revenue_ascSmallest first. Read the warning below.
assets_descMost assets first.
name_ascAlphabetical.

Many organizations have no revenue figure

An organization filing the 990-N postcard reports no revenue at all, and the IRS records that as 0 as often as it leaves it blank. So revenue_desc ranks the organizations that have a figure and then trails everything else in EIN order.

Those organizations are unmeasured, not small. A top-1,000 or top-5,000 is sound. Paging deep into a revenue sort is paging through unranked rows.

revenue_asc is rarely what you want. “Smallest first” returns every zero-revenue organization before it reaches anything with a real figure. It exists for symmetry with the Data API.

Keep sort constant across a walk

The cursor encodes the ordering it was issued under. Handing a cursor to a different sort returns 400 INVALID_CURSOR rather than silently skipping or repeating rows. Every response echoes sort so you can assert it never changed.

Pagination

Cursor-based, with no depth ceiling. Read next_cursor from each response and pass it back as cursor; stop when has_more is false.

cursor = None
while True:
    params = {"state": "TX", "county": "Harris", "limit": 500}
    if cursor:
        params["cursor"] = cursor
    r = requests.get("https://api.givalgo.ai/v1/organizations",
                     params=params, headers={"x-api-key": KEY}).json()
    yield from r["organizations"]
    if not r["has_more"]:
        break
    cursor = r["next_cursor"]

Pick a page size deliberately

Larger pages are markedly cheaper per organization: the maximum page size takes a fraction of the calls and time of the default.

If you are mirroring a whole state, use limit=2000. Fewer calls is also a smaller bill.

Responses are compressed

Send Accept-Encoding: gzip (every HTTP client does by default) and responses come back gzipped. This JSON compresses well because the same keys repeat once per organization, so large pages are much smaller on the wire.

If your client does not send the header you still get plain JSON, byte for byte as before.

Treat the cursor as opaque: a value we did not issue returns 400 INVALID_CURSOR. Ordering is stable (by EIN), so a page never repeats or skips a record while you walk.

Counts (view=facets)

GET   /v1/organizations?view=facets&by={dimension}   Organizations

The dashboard question (“how many nonprofits in each county in Texas?”) answered without transferring every record.

Takes the same filters as the list endpoint plus a required by: county, city, state, ntee or subsection.

curl -H "x-api-key: $GIVALGO_API_KEY" \
  "https://api.givalgo.ai/v1/organizations?view=facets&state=TX&by=county&limit=5"
{
  "dimension": "county",
  "facets": [
    { "value": "Harris County",  "count": 20207 },
    { "value": null,             "count": 18709 },
    { "value": "Dallas County",  "count": 14117 },
    { "value": "Tarrant County", "count":  9691 },
    { "value": "Travis County",  "count":  9143 }
  ],
  "total": 153779,
  "truncated": true
}

null is a real bucket

That second row is not a bug. Those organizations have a ZIP that is absent from the US Census crosswalk, so we cannot place them in a county. They are included as their own bucket so the counts sum to total; if we hid them you would have no way to tell missing coverage from genuine absence.

total counts everything matching your filters across every bucket, so it stays correct even when limit truncates the list. truncated tells you whether it did.

by=county and by=city require a state (or narrower) scope, because counting every county in the country reads the entire dataset.

Newly formed: a filter, not a view

GET   /v1/organizations?ruling_since={YYYY-MM}&sort=ruling_desc   Organizations

“Who just formed in my service area?” is not a separate view; it is a filter (ruling_since) and an ordering (sort=ruling_desc) over the same rows. Every record carries a ruling_month.

Because it is a plain filter it composes with any sort, which a dedicated endpoint could not do. The largest organizations formed since 2015:

curl -H "x-api-key: $GIVALGO_API_KEY" \
  "https://api.givalgo.ai/v1/organizations?state=TX&county=Harris&ruling_since=2015-01&sort=revenue_desc"
curl -H "x-api-key: $GIVALGO_API_KEY" \
  "https://api.givalgo.ai/v1/organizations?state=TX&county=Harris&ruling_since=2026-01&sort=ruling_desc"

since is a month, not a day, because that is the resolution the IRS publishes.

Two things this date is not

  • A determination can be backdated, so a ruling month is not the month the organization appeared in our data.
  • The IRS Business Master File reloads monthly, so “new since last month” can lag reality by up to that reload.

It is still the right field: it is the only date the IRS publishes for this, and it never changes. Re-running the same query tomorrow gives the same answer for the same period.

The cursor here is a composite YYYYMM:EIN, because ordering is by ruling month and many organizations share one. Treat it as opaque, like any other cursor.

Changes (view=changes)

GET   /v1/organizations?view=changes&since_id={id}   Organizations

An append-only feed of IRS status changes (revocations, and removals from and returns to Publication 78), so you can keep a mirror current without re-enumerating a whole county.

Call it once without since_id and keep the next_cursor; from then on pass that cursor as since_id and you receive only what has happened since. A geographic scope is optional here, since the cursor already bounds the response.

curl -H "x-api-key: $GIVALGO_API_KEY" \
  "https://api.givalgo.ai/v1/organizations?view=changes&state=TX&county=Harris&since_id=448817"
Event typeMeaning
revocation_addedThe organization appeared on the IRS auto-revocation list.
pub78_droppedThe organization left IRS Publication 78.
pub78_restoredIt returned to Publication 78 after a drop.

Filter with event_type, comma-separated: event_type=revocation_added,pub78_dropped. For an organization's current status, including an OFAC screen, use the Verify Endpoint.

What this feed does not cover

This reports things that happened to an organization. It does not report edits to the fields this API returns: an organization that renames itself or moves across town emits no event.

Every response repeats this in a covers object, because assuming otherwise is the expensive mistake: you would hold a stale name indefinitely with nothing to tell you. To pick those up, re-enumerate the scope periodically; monthly matches the IRS reload cadence.

The feed is bursty, not daily

The IRS republishes its revocation list and Publication 78 about once a month. A daily poll sees nothing for weeks and then thousands of events at once: that is normal, not an outage. Page the cursor rather than assuming a day fits in one response.

Coverage & caveats

FieldCoverage
ein, name, street, city, state, zip, subsectionEvery record, from the IRS Business Master File.
countyDerived from ZIP via the US Census crosswalk; null where the ZIP is not in that file.
ntee_codeIRS-assigned where the IRS assigned one; ntee_source=inferred fills most of the rest.
dba_nameOnly where the organization filed a DBA or the IRS records a secondary name it goes by.

What the list contains

The universe is the IRS Business Master File: organizations registered with the IRS as tax-exempt. The file also keeps organizations whose exemption has since been revoked, so this endpoint tells you an organization exists and is registered, not that a donation to it is deductible today. That is Charity Verify, which checks Publication 78, the auto-revocation list and OFAC. Enumerate here, verify there before you grant.

Freshness

The Business Master File reloads monthly, so registration facts move on that cadence. The change feed checks for new filings daily, and the IRS publishes them in batches every few weeks.

▶ API Playground live calls · your key stays in your browser

Request preview