Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/bubblio-dev:bubblio-dev-discovery-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Bubblio Dev Discovery API
version: 1.0.0
contact:
email: bubblio@andresio.com
description: 'Operations tagged discovery across 2 of this provider''s published API definitions: openapi.yaml, bubblio-dev-openapi.yaml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://api.bubblio.dev
description: Hosted production platform — the canonical host.
- url: https://andresomar95--bubblio-api-serve.modal.run
description: The same deployment under its infrastructure name (the SDK's default BUBBLIO_API_URL).
tags:
- name: Discovery
description: Credential-free discovery — directory, manifests, llms.txt signposts.
paths:
/agent/directory:
get:
tags:
- Discovery
summary: Every open door, plus the hub block
description: 'Lists doors whose owners opened them (approved accounts with the door enabled)
AND chose to be listed — an owner may run an unlisted door that works for anyone
holding its doorId/MCP URL but appears nowhere here (and does not rank in the
hub''s `find`, nor resolve by handle). Absence from this list therefore never
proves a door does not exist.
Paid actions ride each entry so agent builders can price-shop without knocking.
A transient read failure degrades to an honest empty `doors` list — never a 500;
the `hub` block is served regardless.'
operationId: getDirectory
responses:
'200':
description: The directory.
content:
application/json:
schema:
type: object
required:
- hub
- feed
- doors
properties:
hub:
type: object
description: Where the one-connector hub lives (mcp/llms/manifest URLs).
feed:
type: object
description: 'Where the ACP-compatible feed lives (url + note) — the same listed
doors as product-feed rows, for crawlers without an MCP client.
'
doors:
type: array
items:
type: object
required:
- doorId
- label
- handle
- manifest
- mcp
properties:
doorId:
$ref: '#/components/schemas/DoorId'
label:
type: string
handle:
type: string
description: 'How the hub addresses this door by argument. DERIVED from the
label (never stored) — an owner''s label edit renames it; the
doorId is the name that never moves.
'
blurb:
type: string
manifest:
type: string
format: uri
mcp:
type: string
format: uri
paid_actions:
type: array
description: Present only when the door has priced actions.
items:
type: object
required:
- name
- price
properties:
name:
type: string
price:
type: string
examples:
- $0.40
servers:
- url: https://api.bubblio.dev
description: Hosted production platform — the canonical host.
- url: https://andresomar95--bubblio-api-serve.modal.run
description: The same deployment under its infrastructure name (the SDK's default BUBBLIO_API_URL).
/agent/feed.json:
get:
tags:
- Discovery
summary: The ACP-compatible feed of listed doors and their visible actions
description: 'The directory reshaped for agents WITHOUT an MCP client (ChatGPT/Gemini-class
crawlers and feed ingesters): one row per listed door''s visible action — an
ask-only door gets one door row — carrying the Agentic Commerce Protocol
product-feed REQUIRED fields (item_id, title, description, brand, url, price,
availability, is_eligible_search, is_eligible_checkout, seller_name, seller_url)
plus `bubblio_*` extension fields pointing at the agent-native MCP path.
These are service actions, not retail SKUs, and the divergences are DECLARED in
the body''s `mapping` notes rather than faked: no image_url; availability is
always `in_stock` (a listed action is live — services carry no inventory); free
actions and door rows price at `0.00 USD`; `is_eligible_checkout` is false on
every row because ACP checkout endpoints are not implemented — transacting is
MCP quote→confirm at `bubblio_mcp_url`, where the quote pins the exact price.
Exclusion matches the directory verbatim: unapproved, disabled, and unlisted
doors never appear, hidden (off/orphaned) actions never appear, and no row
carries an identifier that isn''t already public in GET /agent/directory.
Cache-friendly by design: the body is rebuilt at most every `ttl_seconds`
(`generated_at` says when; `Cache-Control`/`Age` say so on the wire). A
transient read failure serves an honest empty `items` list — never a 500,
never cached. Per-caller rate-capped; tripping it is a calm 429 with
Retry-After.'
operationId: getAgentFeed
responses:
'200':
description: The feed.
content:
application/json:
schema:
type: object
required:
- kind
- version
- generated_at
- ttl_seconds
- hub
- directory
- mapping
- items
properties:
kind:
type: string
const: bubblio-agent-feed
version:
type: string
generated_at:
type: string
format: date-time
ttl_seconds:
type: integer
hub:
type: object
description: The one-connector hub block (the headline path stays MCP).
directory:
type: string
format: uri
docs:
type: string
format: uri
mapping:
type: array
description: The honest retail-vocabulary divergences, stated in-band.
items:
type: string
items:
type: array
items:
type: object
required:
- item_id
- title
- description
- brand
- url
- price
- availability
- is_eligible_search
- is_eligible_checkout
- seller_name
- seller_url
properties:
item_id:
type: string
description: '`<doorId>:<action>` for action rows; the doorId alone for door rows.'
title:
type: string
description:
type: string
brand:
type: string
url:
type: string
format: uri
price:
type: string
examples:
- 0.40 USD
availability:
type: string
const: in_stock
is_eligible_search:
type: boolean
const: true
is_eligible_checkout:
type: boolean
const: false
seller_name:
type: string
seller_url:
type: string
format: uri
bubblio_door_id:
$ref: '#/components/schemas/DoorId'
bubblio_handle:
type: string
bubblio_mcp_url:
type: string
format: uri
bubblio_llms_url:
type: string
format: uri
bubblio_connect_available:
type: boolean
description: Whether the door's phone-login connect lane is open (a boolean, never the URL).
bubblio_blurb:
type: string
bubblio_action:
type: string
description: Present on action rows only.
bubblio_action_policy:
type: string
enum:
- keyed
- any
'429':
description: Per-caller cap tripped — calm error body, clamped Retry-After header.
servers:
- url: https://api.bubblio.dev
description: Hosted production platform — the canonical host.
- url: https://andresomar95--bubblio-api-serve.modal.run
description: The same deployment under its infrastructure name (the SDK's default BUBBLIO_API_URL).
/agent/resolve:
get:
tags:
- Discovery
summary: A domain's verified door (#54)
description: '"I know only the platform''s domain — which door?" The answer is VERIFIED or it is
nothing: the resolver crawls the domain''s OWN breadcrumbs (an /mcp redirect,
llms.txt''s first 40 lines, /.well-known/mcp — at most 3 guarded fetches, 4s each,
redirects confined to the domain''s own name tree), and a pointer served from the
domain IS the proof, because only the domain''s owner can serve one. Self-declared
data (door labels, callback URLs) is never consulted and never echoed — an approved
account naming itself "Stripe" can never make this endpoint say so. A pointer at a
door that is not live answers EXACTLY like no pointer at all: closed and nonexistent
read the same here, as at the door''s own 404.
Cost control: the per-caller bucket is spent on EVERY answered request (default
30/hour — cached answers and domain-bucket 429s included, so hammering a crawl-
limited domain runs down the caller''s own budget); the crawl itself runs only on a
cache miss, behind a per-target-domain bucket (default 10/hour, shared across all
callers). The 15-minute cache stores the crawl result, never the verdict — door
liveness is re-checked on every answer, so a door closed a minute ago stops
verifying immediately while its domain keeps its crawl quota.'
operationId: resolveDomain
parameters:
- name: domain
in: query
required: true
schema:
type: string
maxLength: 253
description: 'A bare registrable hostname — lowercase a-z0-9 labels with dashes, at least two
labels; no scheme, no path, no port, no userinfo, no IP literal (dotted,
bracketed, or numeric-in-disguise). Bubblio''s own name trees are refused as
INPUT (400), never answered as misses.
'
responses:
'200':
description: 'The verified door, or the one honest miss — "no pointer" and "pointer at a dead
door" are indistinguishable, and other doors are never mentioned.
'
content:
application/json:
schema:
oneOf:
- type: object
description: Verified — the domain's own pointer named a live door.
required:
- domain
- verified
- door
properties:
domain:
type: string
verified:
type: boolean
const: true
door:
type: object
required:
- label
- handle
- door_id
- mcp
- manifest
- llms
properties:
label:
type: string
handle:
type: string
door_id:
$ref: '#/components/schemas/DoorId'
mcp:
type: string
format: uri
manifest:
type: string
format: uri
llms:
type: string
format: uri
- type: object
description: The uniform miss, teaching the street sign.
required:
- domain
- verified
- notice
properties:
domain:
type: string
verified:
type: boolean
const: false
notice:
type: string
description: Points a domain owner at bubblio.dev/docs/discovery#street-sign.
'400':
description: Not a bare registrable hostname, or one of Bubblio's own.
content:
application/json:
schema:
$ref: '#/components/schemas/CalmError'
'429':
description: 'Either bucket tripped — the per-caller ceiling, or the per-domain crawl ceiling
on a cache miss. The message names which; Retry-After is clamped.
'
headers:
Retry-After:
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/CalmError'
servers:
- url: https://api.bubblio.dev
description: Hosted production platform — the canonical host.
- url: https://andresomar95--bubblio-api-serve.modal.run
description: The same deployment under its infrastructure name (the SDK's default BUBBLIO_API_URL).
/agent/hub:
get:
tags:
- Discovery
summary: The hub manifest
description: 'One claude.ai connector for the whole directory. Stable by construction — no DB
read; the six-tool surface never changes as doors come and go.'
operationId: getHubManifest
responses:
'200':
description: The hub manifest.
content:
application/json:
schema:
type: object
required:
- kind
- version
- label
- capabilities
- endpoints
- tools
- limits
- notice
properties:
kind:
type: string
const: bubblio-agent-hub
version:
type: string
label:
type: string
capabilities:
type: array
items:
type: string
enum:
- find
- ask
- about
- act
endpoints:
type: object
properties:
mcp:
type: string
format: uri
llms:
type: string
format: uri
directory:
type: string
format: uri
feed:
type: string
format: uri
tools:
type: array
items:
type: string
enum:
- about
- ask
- list_actions
- quote
- confirm
- find
limits:
type: object
description: find's own published ceilings; the other five tools spend the resolved door's budgets.
notice:
type: string
docs:
type: string
format: uri
servers:
- url: https://api.bubblio.dev
description: Hosted production platform — the canonical host.
- url: https://andresomar95--bubblio-api-serve.modal.run
description: The same deployment under its infrastructure name (the SDK's default BUBBLIO_API_URL).
/agent/hub/llms.txt:
get:
tags:
- Discovery
summary: The hub's plain-text signpost
operationId: getHubLlmsTxt
responses:
'200':
description: Plain text for LLM crawlers — the one-connector path, tools, and a bare-POST example.
content:
text/plain:
schema:
type: string
servers:
- url: https://api.bubblio.dev
description: Hosted production platform — the canonical host.
- url: https://andresomar95--bubblio-api-serve.modal.run
description: The same deployment under its infrastructure name (the SDK's default BUBBLIO_API_URL).
/agent/{doorId}:
get:
tags:
- Discovery
summary: A door's manifest
description: 'What this door is and what an agent may do here. `act` appears in `capabilities`
only while ≥1 action is visible. EVERY rate ceiling is published so an agent can
pace itself instead of discovering limits as 429s. Priced actions ride `pricing`
with the payment profile (accepted rails + the platform''s Stripe PUBLISHABLE key —
facts only, never a credential).'
operationId: getDoorManifest
parameters:
- $ref: '#/components/parameters/doorId'
responses:
'200':
description: The manifest.
content:
application/json:
schema:
type: object
required:
- kind
- version
- label
- capabilities
- tier
- endpoints
- limits
- notice
properties:
kind:
type: string
const: bubblio-agent-door
version:
type: string
label:
type: string
capabilities:
type: array
items:
type: string
enum:
- ask
- about
- act
tier:
type: string
const: anonymous
endpoints:
type: object
properties:
mcp:
type: string
format: uri
llms:
type: string
format: uri
hub:
type: string
format: uri
pricing:
type: array
description: Present only when the door has priced actions.
items:
type: object
properties:
action:
type: string
amount_cents:
type: integer
currency:
type: string
display:
type: string
payment:
type: object
description: 'Present with pricing: accepted rails + grant target (publishable key).'
limits:
type: object
properties:
asksPerHour:
type: integer
asksPerDayPerCaller:
type: integer
asksPerDayAcrossCallers:
type: integer
quotesPerHour:
type: integer
quotesPerDayPerCaller:
type: integer
quotesPerDayAcrossCallers:
type: integer
notice:
type: string
docs:
type: string
format: uri
'404':
$ref: '#/components/responses/DoorMiss'
servers:
- url: https://api.bubblio.dev
description: Hosted production platform — the canonical host.
- url: https://andresomar95--bubblio-api-serve.modal.run
description: The same deployment under its infrastructure name (the SDK's default BUBBLIO_API_URL).
/agent/{doorId}/llms.txt:
get:
tags:
- Discovery
summary: A door's plain-text signpost
operationId: getDoorLlmsTxt
parameters:
- $ref: '#/components/parameters/doorId'
responses:
'200':
description: Plain text — tools, prices, caps, connect instructions, a bare-POST example.
content:
text/plain:
schema:
type: string
'404':
$ref: '#/components/responses/DoorMiss'
servers:
- url: https://api.bubblio.dev
description: Hosted production platform — the canonical host.
- url: https://andresomar95--bubblio-api-serve.modal.run
description: The same deployment under its infrastructure name (the SDK's default BUBBLIO_API_URL).
components:
parameters:
doorId:
name: doorId
in: path
required: true
schema:
$ref: '#/components/schemas/DoorId'
responses:
DoorMiss:
description: 'UNIFORM: unknown, malformed, disabled, unapproved, and migration-lagged doors all read the same.'
content:
application/json:
schema:
type: object
properties:
error:
type: string
const: No agent door here.
schemas:
CalmError:
type: object
required:
- error
properties:
error:
type: string
code:
type: string
description: Machine-readable refusal code, present where documented — branch on this, never on message text.
DoorId:
type: string
pattern: ^door_[a-f0-9]{12}$
securitySchemes:
apiKey:
type: http
scheme: bearer
description: The platform's Bubblio API key (`bbl_…`). Server-side only.
consentToken:
type: http
scheme: bearer
bearerFormat: JWT
description: The human's passkey-session token from `POST /approvals/session`.
x-refined-from:
- openapi.yaml
- bubblio-dev-openapi.yaml