BotFriendly Submissions API
The Submissions API from BotFriendly — 1 operation(s) for submissions.
The Submissions API from BotFriendly — 1 operation(s) for submissions.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/botfriendly-xyz:botfriendly-xyz-submissions-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a 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: BotFriendly Submissions API
description: A directory of services AI agents can actually use. Query, filter, and discover bot-friendly APIs, tools, and merchants by category, interface (MCP, OpenAPI, llms.txt) and agent payment/commerce protocol.
version: 1.0.0
contact:
url: https://botfriendly.xyz
servers:
- url: https://botfriendly.xyz/api/v1
tags:
- name: Submissions
paths:
/submissions:
post:
operationId: submitService
summary: Submit a new service
description: 'Submit a service to the directory. No auth required, and no review queue: each claimed interface URL must sit on the service''s own domain or be linked from the service''s own site (the listed page or its llms.txt), and we fetch it during the request. If the MCP server, OpenAPI spec or API completes a handshake (MCP initialize succeeds or answers 401 with the MCP OAuth challenge; the spec parses; an API host — api.example.com, or a path under /api or /v1 — answers in JSON), the listing is published immediately and the response carries its slug; otherwise the submission is rejected with 422 and the probe results explaining why. Other claimed badges whose endpoint did not answer are dropped from the published listing rather than blocking it. Listing URLs that look like articles (/blog/, /news/, dated or article-title paths) are rejected, and a domain can add at most 2 listings per 24 hours and 3 in total.'
requestBody:
required: true
content:
application/json:
schema:
type: object
description: has_mcp_server, has_openapi_spec or has_api must be true — an interface an agent can call. has_skill_file, has_llms_txt and has_structured_feed add badges but don't qualify a listing on their own.
required:
- name
- url
- category
- description
properties:
name:
type: string
url:
type: string
format: uri
docs_url:
type: string
format: uri
category:
type: string
enum:
- think
- see
- hear-speak
- create
- remember
- search
- browse
- communicate
- execute
- pay
- shop
- markets
- platforms
- identify
- monitor
- other
- locate
- book
- hire
- sign
- trade
- bet
- connect
- automate
description: Category id from /categories (a merged category's old slugs are accepted too)
description:
type: string
maxLength: 120
icon_url:
type: string
format: uri
description: Link to the service's icon. Fetched, downscaled to fit 512px, converted to PNG and re-hosted. Max 1MB. If neither icon_url nor icon_base64 is given, the service's own site is checked for a declared icon or a favicon; a listing with no icon at all is rejected.
icon_base64:
type: string
description: 'The icon inline instead of by URL — a data: URI or a bare base64 payload, max 1MB decoded. Takes precedence over icon_url.'
auth_method:
type: string
enum:
- api-key
- m2m-oauth
- bot-token
- wallet
- x402-none
- none
has_mcp_server:
type: boolean
mcp_server_url:
type: string
has_openapi_spec:
type: boolean
openapi_spec_url:
type: string
has_api:
type: boolean
api_url:
type: string
description: A live API base URL (api.example.com, or a path under /api or /v1) that answers in JSON
accepts_mpp:
type: boolean
accepts_x402:
type: boolean
payment_protocols:
type: array
items:
type: string
enum:
- x402
- mpp
- l402
- ap2
- tap
- agent-pay
- ace
- acp
- ucp
- actp
- icc
has_skill_file:
type: boolean
skill_file_url:
type: string
has_llms_txt:
type: boolean
llms_txt_url:
type: string
has_structured_feed:
type: boolean
structured_feed_url:
type: string
billing_model:
type: string
enum:
- pay-per-request
- pay-per-token
- pay-per-second
- subscription
- per-transaction
- free
has_free_tier:
type: boolean
tags:
type: array
items:
type: string
contact_email:
type: string
format: email
responses:
'200':
description: Accepted. status is 'approved' when the listing published (slug and url are set) or 'pending' in the rare case it was held for a look.
content:
application/json:
schema:
type: object
properties:
ok:
type: boolean
data:
type: object
properties:
id:
type: string
name:
type: string
status:
type: string
enum:
- approved
- pending
created_at:
type: string
format: date-time
slug:
type:
- string
- 'null'
description: Set when the listing published.
url:
type:
- string
- 'null'
description: Canonical listing URL when published.
probes:
type: array
description: What we fetched for each claimed interface, and what came back.
items:
$ref: '#/components/schemas/Probe'
dropped_flags:
type: array
description: Badges that were claimed but whose endpoint did not answer, so they are not on the listing.
items:
type: string
icon:
type: object
description: The stored icon and where it came from.
properties:
source:
type: string
enum:
- icon_url
- icon_base64
- discovered
found_at:
type:
- string
- 'null'
description: Set when the icon was found on the service's own site.
width:
type: integer
height:
type: integer
meta:
type: object
properties:
message:
type: string
'400':
description: Validation error — missing fields, bad category, neither has_mcp_server nor has_openapi_spec set (code no_callable_interface), a ticked badge with no URL, an interface URL on a different domain from the service, or no usable icon (icon_url unreachable, icon_base64 undecodable, and nothing found on the site)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: 'Not published. `error.code` names the rule: content_url (the URL is an article, not a product), domain_daily_limit (2 listings from this domain in the last 24h), domain_listing_limit (the domain already has 3 listings), no_callable_interface (no MCP server, OpenAPI spec or API completed a handshake), nothing_answered, interface_url_invalid, or admission_unavailable (try again shortly). The message says what was tried.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: Submission rate limit exceeded
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
tags:
- Submissions
components:
schemas:
Error:
type: object
description: Standard error envelope returned by every non-2xx response
properties:
ok:
type: boolean
enum:
- false
error:
type: object
properties:
message:
type: string
code:
type: string
hint:
type: string
required:
- message
Probe:
type: object
description: The result of fetching one claimed interface URL.
properties:
flag:
type: string
description: Which badge was checked.
url:
type:
- string
- 'null'
description: What we fetched.
verdict:
type: string
enum:
- pass
- responded
- fail
description: 'pass: the artifact was fetched and is what it claims to be. responded: a server answered but would not show it (401). fail: nothing there, or provably not the artifact.'
detail:
type: string
status:
type: integer
checkedAt:
type: string
format: date-time