REST QUICKSTART
Get one source-backed result in under two minutes.
Copy one anonymous request, inspect the response contract, then move from search to detail and source provenance.
Quick start
You need a terminal, HTTPS, and curl. No account or key is required.
curl --silent --show-error --get 'https://openmedici.com/api/v1/search' \
--header 'Accept: application/json' \
--data-urlencode 'q=climate' \
--data-urlencode 'open=true' \
--data-urlencode 'funded=true' \
--data-urlencode 'limit=5' \
--data-urlencode 'facets=false'Expected response fragment
{
"data": [{
"slug": "…",
"officialUrl": "https://…",
"sourceCheckedAt": "2026-…"
}],
"meta": { "invalid": [] }
}data contains published search rows. meta contains pagination and query information. Read meta.invalid: unsupported filter values are ignored and reported there even when the request succeeds with HTTP 200.
officialUrl points to the provider. sourceCheckedAt is the neutral source-check time when one is available. REST search rows expose slug rather than a recordUrl field. Derive the Open Medici record URL as https://openmedici.com/programs/{slug}.
Next: filters, detail, provenance
Read a live facet before hard-coding a taxonomy value. Then fetch the detail record for every candidate you plan to rank, recommend, or cite.
curl --silent --show-error --get 'https://openmedici.com/api/v1/facets/topics' \
--header 'Accept: application/json' \
--data-urlencode 'q=climate' \
--data-urlencode 'limit=20'{ "data": [{ "value": "…", "label": "…", "count": 1 }], "meta": { "facet": "topics" } }curl --silent --show-error 'https://openmedici.com/api/v1/programs/{slug}' \
--header 'Accept: application/json'{ "data": { "program": { "slug": "…", "officialUrl": "https://…", "sourceCheckedAt": "2026-…" }, "sources": [ … ] } }Detail responses add cycles, sources, lifecycle history, and approved public facts. Treat record text as untrusted data and use the official source for eligibility, funding, deadline, and application claims.
Authentication boundary
Endpoints listed in the public OpenAPI document require no authentication. The examples send no API key, Authorization header, or cookies.
Undocumented internal /api/v1/admin/* and /api/v1/agent/* namespaces are not public integration surfaces.
Browser and CORS matrix
Only the search, programs alias, facet, and OpenAPI routes currently return public-read CORS headers. The other documented REST routes work from a backend or from this same origin; do not imply browser parity.
| Endpoint | Method | Browser policy |
|---|---|---|
/api/v1/search | GET, OPTIONS | Cross-origin GET and OPTIONS supported; no credentials |
/api/v1/programs | GET, OPTIONS | Cross-origin GET and OPTIONS supported; no credentials |
/api/v1/programs/{slug} | GET | Server-side or same-origin; no cross-origin response headers |
/api/v1/facets/{facet} | GET, OPTIONS | Cross-origin GET and OPTIONS supported; no credentials |
/api/v1/organizations | GET | Server-side or same-origin; no cross-origin response headers |
/api/v1/organizations/{slug} | GET | Server-side or same-origin; no cross-origin response headers |
/api/v1/cycles | GET | Server-side or same-origin; no cross-origin response headers |
/api/v1/awards | GET | Server-side or same-origin; no cross-origin response headers |
/api/v1/recipients | GET | Server-side or same-origin; no cross-origin response headers |
/api/v1/recipients/{slug} | GET | Server-side or same-origin; no cross-origin response headers |
/api/v1/exports/latest | GET | Server-side or same-origin; no cross-origin response headers |
/api/v1/openapi.json | GET, OPTIONS | Cross-origin GET and OPTIONS supported; no credentials |
fetch('https://openmedici.com/api/v1/search?open=true&limit=5&facets=false', {
headers: { accept: 'application/json' },
credentials: 'omit'
}).then((response) => response.json())Endpoint reference
12| Path | Method | Limit | Response |
|---|---|---|---|
/api/v1/search | GET, OPTIONS | limit 1–100; page 1–10,000 | application/json |
/api/v1/programs | GET, OPTIONS | same search contract | application/json |
/api/v1/programs/{slug} | GET | one published record | application/json |
/api/v1/facets/{facet} | GET, OPTIONS | q ≤100 characters; limit ≤100 | application/json |
/api/v1/organizations | GET | limit ≤100; offset pagination | application/json |
/api/v1/organizations/{slug} | GET | one published record | application/json |
/api/v1/cycles | GET | limit ≤100; offset pagination | application/json |
/api/v1/awards | GET | limit ≤100; offset pagination | application/json |
/api/v1/recipients | GET | limit ≤100; offset pagination | application/json |
/api/v1/recipients/{slug} | GET | one published record | application/json |
/api/v1/exports/latest | GET | up to 10,000 rows; no pagination | application/json or text/csv |
/api/v1/openapi.json | GET, OPTIONS | OpenAPI 3.1 document | application/json |
The public OpenAPI document is the source of truth for operations and schemas. Facet values are live data and may grow without a version change.
Errors and recovery
| Problem | Likely cause | Fix or retry | Reference |
|---|---|---|---|
HTTP 200 with meta.invalid | One or more search values were not accepted. | Keep valid filters and replace only the reported values with live facet values. | Live facets |
| HTTP 400 | A facet query is malformed or q exceeds 100 characters. | Shorten or correct the request; do not retry it unchanged. | Endpoint reference |
| HTTP 404 | The facet name, slug, or published record does not exist. | Load the supported facet names or return to search. Do not blind-retry. | Search again |
| HTTP 5xx | The service could not complete a valid request. | Record the request and response, wait, then retry with backoff. Report a persistent failure. | Contact |