From agent-skills
Queries the NIH RePORTER API for federally-funded research projects and publications. Handles criteria payload construction, pagination, rate-limit handling, and field-name mapping.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agent-skills:nih-reporterThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Public API exposing NIH and non-NIH agency awards (and linked publications). No authentication. Two endpoints — projects and publications. Returns JSON.
Public API exposing NIH and non-NIH agency awards (and linked publications). No authentication. Two endpoints — projects and publications. Returns JSON.
| Endpoint | Use |
|---|---|
POST https://api.reporter.nih.gov/v2/projects/search | Awards / projects |
POST https://api.reporter.nih.gov/v2/publications/search | Publications linked to awards |
Both are POST only (GET returns 405). Body is JSON, Content-Type: application/json.
Interactive Swagger docs: https://api.reporter.nih.gov/?urls.primaryName=V2.0
NIH publishes hard guidance, with IP-blocking as the stated consequence:
429 and 5xx; do not retry tight.For any pull > ~50 pages, add time.sleep(1.1) between requests and schedule for off-hours. Use a User-Agent that identifies you.
| Field | Default | Max |
|---|---|---|
limit | 50 | 500 |
offset (projects) | 0 | 14,999 (offset+limit ≤ 15,000) |
offset (publications) | 0 | 9,999 |
The 15k cap is hard — if your result set exceeds it, split the query by fiscal year, IC, or activity code rather than trying to page past it.
To know total results before paginating: the response includes meta.total. Issue one request with limit: 1 first if you need to plan a pull.
curl -sS -X POST https://api.reporter.nih.gov/v2/projects/search \
-H 'Content-Type: application/json' \
-d '{
"criteria": {"fiscal_years": [2024], "pi_names": [{"any_name": "Mitchell"}]},
"include_fields": ["ProjectNum","ProjectTitle","Organization","AwardAmount","FiscalYear","PrincipalInvestigators"],
"limit": 25,
"offset": 0,
"sort_field": "project_start_date",
"sort_order": "desc"
}' | jq .
{
"criteria": { ... filters ... },
"include_fields": [ ... PascalCase field names ... ],
"exclude_fields": [ ... ],
"offset": 0,
"limit": 50,
"sort_field": "project_start_date",
"sort_order": "desc",
"use_relevance": false
}
Three name conventions in the same payload — this is the #1 source of bugs:
| Where | Convention | Example |
|---|---|---|
criteria keys | snake_case | fiscal_years, pi_names, org_states |
include_fields values | PascalCase | FiscalYear, PrincipalInvestigators, OrgState |
| Response JSON keys | snake_case | fiscal_year, principal_investigators, org_state |
If you send "include_fields": ["fiscal_year"] it is silently ignored — the API returns everything. Always PascalCase the include list.
For the full criteria↔include↔response mapping, see references/field-mapping.md.
"advanced_text_search": {
"operator": "and",
"search_field": "projecttitle,abstracttext,terms",
"search_text": "single-cell pancreatic"
}
operator: "and", "or", or "advanced" (advanced supports AND/OR/NOT + quoted phrases).search_field: comma-joined subset of projecttitle, abstracttext, terms."\"breast cancer\""."pi_names": [{"any_name": "Smith"}],
"pi_names": [{"last_name": "Welch", "first_name": "John"}]
Each list element is an object with one of any_name, first_name, middle_name, last_name. Multiple list elements are OR'd. any_name: "" is a no-op placeholder when only specifying a sub-field — keep it if you copy from NIH examples.
"org_names": ["EMORY UNIVERSITY", "JOHNS HOPKINS UNIVERSITY"],
"org_names_exact_match": ["FRED HUTCHINSON CANCER CENTER"],
"org_states": ["WA","CA"],
"org_countries": ["UNITED STATES"],
"org_cities": ["SEATTLE"]
org_names is wildcard/partial by default — "HARVARD" matches HARVARD UNIVERSITY, HARVARD MEDICAL SCHOOL, etc.org_names_exact_match when you need exactly one institution."fiscal_years": [2023, 2024, 2025],
"project_start_date": {"from_date":"2024-01-01","to_date":"2024-12-31"},
"project_end_date": {"from_date":"2024-01-01","to_date":"2026-12-31"},
"award_notice_date": {"from_date":"2024-01-01","to_date":"2024-12-31"},
"date_added": {"from_date":"2024-01-01","to_date":"2024-12-31"}
fiscal_years: [] matches all years. date_added is only populated from 2011-01-01 forward.
"agencies": ["NCI","NIGMS","NHGRI"],
"activity_codes": ["R01","R35","P20","U54"],
"award_types": ["1","2","5","4C","4N"],
"funding_mechanism": ["RP","SB","RC","OR","TR","TI","CO","NSRDC","SRDC","IAA","IM","Other"]
agencies takes IC acronyms (NCI, not CA). Full list in references/ic-codes.md.award_types is the application type code (1=new, 2=renewal, 3=supplement, 4C/4N=Type 4 sub-types, 5=non-competing continuation, etc.).funding_mechanism, value "Other" is a combined search for UK + OT + CP — there is no "UK" value you can pass directly."award_amount_range": {"min_amount": 250000, "max_amount": 5000000}
Both fields required. Costs only populated from FY 2012 onward (and never for SBIR/STTR).
"project_nums": ["R01CA123456*", "5R01HG*"],
"project_num_split": {
"appl_type_code":"1","activity_code":"R01","ic_code":"CA",
"serial_num":"123456","support_year":"01","suffix_code":""
}
project_nums accepts wildcards with *. Matches both core_project_num and project_num.project_num_split matches exactly on components — useful for parsing/joining ExPORTER-style IDs."spending_categories": {"values": [27, 60, 105], "match_all": false}
match_all: true returns projects in all listed categories (intersection); false returns projects in any (union).references/spending-categories.md for the FY2024 list (e.g. 27=Aging, 105=Brain Disorders, 132=Cancer)."covid_response": ["Reg-CV","CV","C3","C4","C5","C6"]
Or "All" for any COVID-funded project. Reg-CV = regular appropriations; CV/C3/C4/C5/C6 = the five supplemental appropriations acts.
"include_active_projects": true,
"exclude_subprojects": true,
"sub_project_only": false,
"multi_pi_only": false,
"newly_added_projects_only": false,
"is_agency_admin": true,
"is_agency_funding": true
include_active_projects: true restricts to currently-active awards (budget end date ≥ today).
sort_field accepts the criteria (snake_case) name of the field. Common ones: project_start_date, project_end_date, award_notice_date, award_amount, fiscal_year, appl_id. sort_order: "asc" or "desc". Default use_relevance: false; set true only when combined with a text search.
Much narrower — only three criteria fields:
{
"criteria": {
"pmids": [12345678, 23456789],
"appl_ids": [10372367],
"core_project_nums": ["R01CA123456", "R01HG*"]
},
"limit": 500,
"offset": 0
}
Response per row: coreproject (core project num), pmid, applid (latest application ID). To enrich (title, journal, authors): pass the pmid list to PubMed via the plugin_pubmed_PubMed MCP tools.
Field mapping, IC code list, and spending category IDs live in references/:
references/field-mapping.md — every criteria → include_fields → response-key triple, with types and notes.references/ic-codes.md — IC acronyms (use in agencies) ↔ org code ↔ full name.references/spending-categories.md — FY2024 numeric IDs ↔ names.references/recipes.md — copy-paste payloads for common questions (who funds X, awards at org Y, all R01s in FY2024, etc.).import requests, time
URL = "https://api.reporter.nih.gov/v2/projects/search"
HEADERS = {"Content-Type": "application/json", "User-Agent": "[email protected]"}
def search_all(criteria, include_fields, page_size=500, sleep=1.1):
"""Paginate through all results. Stops at the 15k offset cap."""
offset, out = 0, []
while offset < 15000:
body = {"criteria": criteria, "include_fields": include_fields,
"offset": offset, "limit": page_size,
"sort_field": "project_start_date", "sort_order": "desc"}
r = requests.post(URL, json=body, headers=HEADERS, timeout=60)
r.raise_for_status()
data = r.json()
out.extend(data["results"])
total = data["meta"]["total"]
if offset + page_size >= total: break
offset += page_size
time.sleep(sleep)
return out, total
If total > 15000, partition by fiscal_years (one year at a time) or by agencies and concatenate.
include_fields is PascalCase, not the criteria name or the response name. FiscalYear, not fiscal_year. Wrong casing → silently ignored, full payload returned (slow).fiscal_years: [] ≠ omitting the key. Empty array = "all years"; omitting the key may default to recent years depending on other filters.org_names is partial-match — "WASHINGTON" matches University of Washington, Washington State, Washington University in St. Louis. Use org_names_exact_match for precision.award_types: ["4"] does not match Type 4 awards — use ["4C","4N"] (the documented sub-types).pi_names / po_names items are objects, not strings. ["Smith"] errors; [{"any_name": "Smith"}] works."2021-03-15T04:00:00Z"), not date-only. Parse as datetime, not date.agency_ic_fundings is a list — a project funded by multiple ICs has multiple rows. Sum total_cost across the list if you want total project funding for that FY.repoRter.nih for R, pynih for Python). The R one is well-maintained as of 2024; useful as a reference for payload shape but not required.Each project has a human-readable page: https://reporter.nih.gov/project-details/{appl_id}. Useful for spot-checking API results and for citations in summaries.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
npx claudepluginhub sage-bionetworks/agent-skills --plugin agent-skills