Authentication
A key is optional, not required. The catalog is public, so a request with no key is served —
on the Explore plan. Make GET requests directly against https://apis.io/api/v1.
An agent does not need a key to register itself. POST /api/v1/agents/register with the URL of
your A2A agent card puts you in the agent registry — the one door here
built for an unattended agent; see onboarding.
Nothing an API call does mutates the published catalog: the POST and DELETE operations write
to your own workspace or open a request that a person works. The ranked and
cross-catalog resources — ratings, insights, cohorts, capabilities, exports — and everything about
managing your own listing need a key on a paid plan; see Plans.
Code
This matches APIs.io's open-discovery mission: the catalog is public, and there is no endpoint that mutates it.
Sending a key
A key does two things: it raises your rate limit and daily quota, and it unlocks the tier-gated resources (industries, regions, ratings, Insights depth, the Saved Workspace). Send it as a header:
Code
A Learn key is free with a GitHub sign-in. Understanding and Influence are self-serve from your account page.
A gated resource answers one of two ways, and the difference is whether you sent a credential:
- No key at all →
401 Unauthorized. The body names the resource, the lowest plan that unlocks it, and what the call would cost — but a401means "identify yourself", and a client that treats it as "upgrade your plan" has no plan to upgrade. Register, or start OAuth. Where the call is sold per call, you can also pay for it with x402, which needs no credential at all — see the "Pay per call" door on onboarding. - A valid key below the required tier →
402 Payment Required. A payment signal, distinct from being rate limited (429) and from a permission failure. This is the response to branch an upgrade prompt on.
Both bodies carry a resolution object. "self_serve": true means an x402 offer (accepts) is on
the response and the caller can settle it itself; false means a person has to act, at the url
it names.
Neither is ever 403. A client that refreshes credentials on every 401 and only shows a paywall
on 402 behaves correctly against both.
Which key am I using?
GET /api/v1/auth/me answers with an ordinary API key — not only with a signed request or a
browser session:
Code
It returns your login, your tier, principal_type: "api_key", the key masked, and usage: the
seven-day total plus today — used, remaining and seconds to reset for the daily quota that is
actually enforced. Its note says which operations need the signed-in web session instead (key
rotation, deletion, billing). Every account and billing operation is in the
API reference under Account and Billing, each marked when it needs a person.
The challenge, and the ceiling behind it
Every refusal carries an RFC 9728 Bearer challenge naming the resource metadata and the scope the
route needs — apis:pro on an Understanding route, apis:business on an Influence one. On apis.io
it arrives as x-auth-challenge (API Gateway renames WWW-Authenticate).
Those scopes are granted only to a person. A client that registers itself — dynamic client
registration, or an agent principal — is capped at apis:read whatever it requests, and the token's
own scope says what was granted. So an agent that follows the challenge, registers, asks for
apis:pro and retries will see the same challenge again: that loop is by design, not a fault in
your client. Paid tiers are reached by a signed-in person — the refusal body's resolution object
says so (self_serve: false) and carries the URL to hand them. Do not write a retry for it.
The OpenAPI contract declares this as optional authentication: security: [{}, {ApiKeyAuth: []}] —
the empty object says a keyless call is acceptable, the second entry says a key is understood.
CORS
The API is served same-origin with apis.io, and responses are browser-friendly, so you can call it
directly from web apps and from the agent surfaces.

