Plinth US Grants Data · Authentication Profile
Plinth Us Grants Data Authentication
Authentication
Plinth US Grants Data secures its APIs with apiKey and oauth2 across 2 declared security schemes, as derived from its OpenAPI definitions.
PhilanthropyGrantsNonprofitsFoundationsIRS 990Open DataGovernment SpendingResearchAgentsRESTJSONMCPSQL
Methods: apiKey, oauth2
Schemes: 2
OAuth flows:
API key in: header
Security Schemes
ApiKeyAuth apiKey
· in: header (X-API-Key)
ConnectorOAuth oauth2
Source
Authentication Profile
generated: '2026-08-14'
method: searched
source: https://data.useplinth.com/developers#auth
corroboration:
- openapi/plinth-us-grants-data-openapi.json # components.securitySchemes.ApiKeyAuth
- https://data.useplinth.com/.well-known/api-onboarding
- https://data.useplinth.com/.well-known/oauth-authorization-server
derived_baseline: derive-authentication.py (2026-08-14) — upgraded to `searched` with the docs and onboarding descriptor.
checked: '2026-08-14'
summary:
types: [apiKey, oauth2]
api_key_in: [header]
note: >-
TWO independent auth systems on one host, and they do not overlap. The REST API takes a static
API key in a header. The MCP connector takes OAuth 2.1 with PKCE and dynamic client
registration. A REST key will not authenticate the connector and an OAuth token is not
documented for REST. One operation — GET /api/search — is deliberately unauthenticated.
schemes:
- name: ApiKeyAuth
surface: REST (https://data.useplinth.com/api)
type: apiKey
in: header
parameter: X-API-Key
alternative_parameter: Authorization
alternative_form: 'Bearer <key>'
alternative_evidence: >-
"Send your key in an X-API-Key header (or as a Authorization: Bearer token — both work)"
(/developers#auth). NOTE: only X-API-Key is declared in the OpenAPI — the Bearer form is
documented but not in the contract, so a generated client will only know the header form.
token_prefix: plinth_sk_
token_prefix_source: >-
/developers#auth ("it starts plinth_sk_") and /.well-known/api-onboarding
(authentication.methods[0].tokenPrefix).
env_var: PLINTH_API_KEY
env_var_source: /.well-known/api-onboarding (authentication.methods[0].env)
applied: global
applied_evidence: 'Root-level `security: [{ApiKeyAuth: []}]` in the OpenAPI, with a per-operation override on searchOrganizations.'
issuance:
mechanism: console-only
url: https://data.useplinth.com/account
programmatic: false
cost: free tier requires no card
sign_in: Google sign-in or an email link
one_time_display: true
keys_per_account: 1
note: >-
"The REST key is minted by a signed-in human at /account (one key per account, rotatable,
shown once). There is no programmatic issue endpoint." — /.well-known/api-onboarding.
rotation:
supported: true
url: https://data.useplinth.com/account
semantics: immediate-revoke
note: '"Rotating retires the previous key immediately." No overlap window — plan for a hard cutover.'
ttl: none
handling_guidance: >-
"Keys are secrets — keep them server-side, never in browser JavaScript, a mobile binary or a
committed file." (/developers#auth)
sources:
- https://data.useplinth.com/developers#auth
- openapi/plinth-us-grants-data-openapi.json
- name: ConnectorOAuth
surface: MCP (https://data.useplinth.com/api/connector/mcp)
type: oauth2
profile: OAuth 2.1 (authorization_code + PKCE S256), public client
declared_in_openapi: false
declared_in_openapi_note: >-
The REST OpenAPI does not model this scheme — correctly, since it protects a different
surface. It is discoverable only from the RFC 8414 metadata document.
metadata: well-known/plinth-us-grants-data-oauth-authorization-server.json
protected_resource_metadata: well-known/plinth-us-grants-data-oauth-protected-resource.json
issuer: https://data.useplinth.com
authorization_endpoint: https://data.useplinth.com/oauth/authorize
token_endpoint: https://data.useplinth.com/oauth/token
registration_endpoint: https://data.useplinth.com/oauth/register
dynamic_client_registration: true
grant_types: [authorization_code, refresh_token]
pkce: S256
token_endpoint_auth_methods: [none]
bearer_methods: [header]
scopes: [plinth:read]
see: scopes/plinth-us-grants-data-scopes.yml
token_ttl: short-lived; refresh_token grant supported
revocation: 'Remove the connector in the assistant, or revoke from https://data.useplinth.com/account.'
challenge_observed:
url: https://data.useplinth.com/api/connector/mcp
status: 401
header: >-
www-authenticate: Bearer error="invalid_token", error_description="No authorization
provided", resource_metadata="https://data.useplinth.com/.well-known/oauth-protected-resource"
checked: '2026-08-14'
sources:
- https://data.useplinth.com/connect
- https://data.useplinth.com/.well-known/oauth-authorization-server
anonymous_surface:
operation: searchOrganizations
path: GET /api/search
security: []
metered: false
evidence: >-
Per-operation `security: []` in the OpenAPI overrides the global requirement, and the spec says
so in prose: "**No API key required and not metered.**" Verified live 2026-08-14: GET
/api/search?q=barancik returned 200 with a result body and no auth header sent.
significance: >-
Rare and agent-friendly. Entity resolution — the step that must precede every other call when
you have a name rather than an EIN — is free, unkeyed and unmetered. An agent can resolve
"Barancik Foundation" to EIN 363442474 and a canonical URL before it needs any credential at
all. Plinth's own onboarding descriptor calls this "the intended first call."
agent_readiness_notes:
can_an_agent_self_provision: false
blocker: >-
A human must sign in at /account to mint the REST key, and must hold the For consultants plan
before the MCP consent step can succeed. The provider documents both as gaps in its own
onboarding descriptor rather than leaving an agent to discover them at a 401.
strongest_signal: >-
The credential requirements are published as MACHINE-READABLE data, not prose: token prefix,
env var name, rotation semantics, one-time display, plan gates and the registration mechanisms
all live in /.well-known/api-onboarding. Very few providers in the catalog publish this.