{"openapi":"3.1.0","info":{"title":"BotFriendly 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"}],"paths":{"/services":{"get":{"operationId":"listServices","summary":"List and filter services","description":"Returns a paginated list of bot-friendly services. Supports filtering by category, protocol support, auth method, and search.","parameters":[{"name":"category","in":"query","schema":{"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":"Filter by category id (think, see, hear-speak, create, remember, search, browse, communicate, execute, pay, shop, markets, platforms, identify, monitor, other). A merged category's old slugs (trade, bet, connect, automate, locate, book, sign, hire) are still accepted and select just that slug's listings; the id selects the whole group."},{"name":"has_mcp","in":"query","schema":{"type":"string","enum":["true"]},"description":"Filter to services with MCP servers"},{"name":"accepts_mpp","in":"query","schema":{"type":"string","enum":["true"]},"description":"Filter to services accepting MPP payments"},{"name":"accepts_x402","in":"query","schema":{"type":"string","enum":["true"]},"description":"Filter to services accepting x402 payments"},{"name":"protocol","in":"query","schema":{"type":"string","enum":["x402","mpp","l402","ap2","tap","agent-pay","ace","acp","ucp","actp","icc"]},"description":"Filter to services supporting this agent payment/commerce protocol. Repeat or comma-separate for 'supports all of these'."},{"name":"has_skill_file","in":"query","schema":{"type":"string","enum":["true"]},"description":"Filter to services with skill files"},{"name":"has_llms_txt","in":"query","schema":{"type":"string","enum":["true"]},"description":"Filter to services with llms.txt"},{"name":"auth_method","in":"query","schema":{"type":"string","enum":["api-key","m2m-oauth","bot-token","wallet","x402-none","none"]}},{"name":"search","in":"query","schema":{"type":"string"},"description":"Full-text search across name, description, and tags"},{"name":"min_score","in":"query","schema":{"type":"integer","minimum":1,"maximum":5},"description":"Minimum bot-friendly score"},{"name":"promoted","in":"query","schema":{"type":"string","enum":["true","false"]},"description":"Filter by promoted status: true for promoted only, false for organic only"},{"name":"sort","in":"query","schema":{"type":"string","enum":["score","votes","name","newest"],"default":"score"},"description":"Sort order: score (default), votes, name, newest"},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"List of services","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Service"}},"meta":{"type":"object","properties":{"total":{"type":"integer"},"page":{"type":"integer"},"per_page":{"type":"integer"},"total_pages":{"type":"integer"},"organic_count":{"type":"integer","description":"Number of organic (non-ad) results"},"ad_count":{"type":"integer","description":"Number of ad/promoted results"},"ad_positions":{"type":"array","items":{"type":"integer"},"description":"1-indexed positions of ads in the results array"}}}}}}}}}}},"/services/{slug}":{"get":{"operationId":"getService","summary":"Get a single service by slug","description":"Full details for one listed service: auth method, MCP/OpenAPI/skill-file URLs, supported payment protocols, bot score, and verification status. Slugs come from listServices or searchServices results.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"Service slug, e.g. browserbase"}],"responses":{"200":{"description":"Service detail","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"$ref":"#/components/schemas/Service"}}}}}},"404":{"description":"Service not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/categories":{"get":{"operationId":"listCategories","summary":"List all categories with service counts","description":"Every directory category (think = Models, see = Vision, hear-speak = Speech, create = Media, remember = Memory, search = Search, browse = Browsers, communicate = Messaging, execute = Compute, pay = Payments, shop = Merchants, markets = Markets, platforms = Platforms, identify = Identity, monitor = Observability, other = Other) with its label, description, aliases, and current service count. Category ids are the values accepted by the ?category= filter; aliases are older slugs that are still accepted.","responses":{"200":{"description":"List of categories","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Value for the ?category= filter"},"label":{"type":"string","description":"Human label (Models, Vision, Markets, …)"},"name":{"type":"string","description":"Same as label; kept for older clients"},"icon":{"type":"string"},"description":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"},"description":"Older or merged slugs ?category= also accepts; each selects only its own listings"},"slugs":{"type":"array","items":{"type":"string"},"description":"The category values listings in this group carry"},"service_count":{"type":"integer"}}}}}}}}}}}},"/featured":{"get":{"operationId":"getFeatured","summary":"Get site-wide promoted/featured services (max 3)","description":"Returns site-wide promoted services. These are paid placements clearly marked as promoted.","responses":{"200":{"description":"Featured services","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Service"}},"meta":{"type":"object","properties":{"slots_total":{"type":"integer"},"slots_filled":{"type":"integer"}}}}}}}}}}},"/protocols":{"get":{"operationId":"listProtocols","summary":"List trackable protocols with adoption counts","description":"Returns every protocol the directory tracks (MCP, OpenAPI, and the agent payment/commerce protocols such as x402, MPP, ACP, UCP) with its display name, spec URL, sponsor, and how many listed services support it.","responses":{"200":{"description":"Protocol stats","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Protocol slug, usable as the ?protocol= filter value"},"name":{"type":"string"},"kind":{"type":"string","description":"interface or payment"},"spec_url":{"type":["string","null"],"format":"uri"},"sponsor":{"type":["string","null"]},"service_count":{"type":"integer"}}}}}}}}}}}},"/stats":{"get":{"operationId":"getStats","summary":"Directory statistics","description":"Aggregate stats for the whole directory: total services, per-category counts, and how many services support each protocol. Useful for a quick lay of the land before filtering.","responses":{"200":{"description":"Directory stats including total services and protocol coverage","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object","properties":{"total_services":{"type":"integer"},"categories":{"type":"object","additionalProperties":{"type":"integer"},"description":"Service count per category slug"},"protocols":{"type":"object","additionalProperties":{"type":"integer"},"description":"Service count per supported protocol"}}}}}}}}}}},"/verify":{"post":{"operationId":"verifyService","summary":"Check whether a service really exposes the interfaces it claims","description":"Fetches each claimed endpoint and reports what it found. Nothing is written and no auth is required. This is the same check applied to owner-submitted updates and to automated discovery, exposed so an external workflow doesn't have to reimplement it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"has_mcp_server":{"type":"boolean"},"mcp_server_url":{"type":"string"},"has_openapi_spec":{"type":"boolean"},"openapi_spec_url":{"type":"string"},"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"}}}}}},"responses":{"200":{"description":"Probe verdicts per claimed interface, plus `listable` (true when at least one verified)"},"400":{"description":"Validation error"},"429":{"description":"Rate limit exceeded"}}}},"/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, and we fetch it during the request. If something answers, the listing is published immediately and the response carries its slug; if nothing does, the submission is rejected with 422 and the probe results explaining why. Claimed badges whose endpoint did not answer are dropped from the published listing rather than blocking it. At least one machine-readable interface flag must be true.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"At least one machine-readable interface flag (has_mcp_server, has_openapi_spec, has_skill_file, has_llms_txt, has_structured_feed) must be true — browser-only services are not listed.","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"},"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, no machine-readable interface flag set, 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":"Nothing answered at any claimed interface URL, so the listing was not published. The error message names 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"}}}}}}},"/search":{"get":{"operationId":"searchServices","summary":"Full-text search across services","description":"Search services by keyword. Returns results ranked by bot_score.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"Search query (full-text, websearch syntax)"},{"name":"category","in":"query","schema":{"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":"Filter by category id (same rules as /services)"},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Service"}},"meta":{"type":"object","properties":{"query":{"type":"string"},"total":{"type":"integer"},"page":{"type":"integer"},"per_page":{"type":"integer"},"total_pages":{"type":"integer"}}}}}}}},"400":{"description":"Missing required query parameter 'q'"}}}},"/health":{"get":{"operationId":"healthCheck","summary":"Health check and endpoint discovery","description":"Liveness check that doubles as an index of every machine-readable surface the site offers — REST endpoints, MCP server, llms.txt, and discovery manifests. Call this first when exploring the API.","responses":{"200":{"description":"Service health and available endpoints","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"version":{"type":"string"},"services_count":{"type":"integer"},"last_updated":{"type":"string","format":"date-time"},"endpoints":{"type":"object","additionalProperties":{"type":"string"},"description":"Map of endpoint name to path"}}}}}}}}},"/check":{"get":{"operationId":"checkSite","summary":"Score how bot-friendly a site is","description":"Fetches the target site the way an AI agent would (no JavaScript) and scores access, no-JS content, and machine discovery out of 100, with per-check advice. Rate limited to 6 checks/min per IP.","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string"},"description":"Site to check (scheme optional, https assumed)"},{"name":"format","in":"query","schema":{"type":"string","enum":["json","prompt"],"default":"json"},"description":"prompt returns a ready-to-paste coding-agent prompt (text/markdown) covering every failed or partial finding, instead of the JSON report"}],"responses":{"200":{"description":"Check report","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"data":{"type":"object","properties":{"input_url":{"type":"string"},"final_url":{"type":"string"},"checked_at":{"type":"string","format":"date-time"},"duration_ms":{"type":"integer"},"score":{"type":"integer","minimum":0,"maximum":100},"max_score":{"type":"integer"},"verdict":{"type":"string"},"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["access","content","discovery"]},"label":{"type":"string"},"earned":{"type":"number"},"possible":{"type":"number"},"passed":{"type":"integer"},"total":{"type":"integer"}}}},"checks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"category":{"type":"string","enum":["access","content","discovery"]},"status":{"type":"string","enum":["pass","warn","fail","skip"]},"weight":{"type":"number"},"bonus":{"type":"number"},"detail":{"type":"string"},"advice":{"type":"string"}}}}}}}}}}},"400":{"description":"Missing, invalid, or blocked URL"},"429":{"description":"Rate limit exceeded (6 checks/min per IP)"}}}}},"components":{"schemas":{"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"}}},"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"]}}},"Service":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"category":{"type":"string","description":"Category slug the listing carries; map it to a label via /categories (slugs array)"},"url":{"type":"string","format":"uri"},"docs_url":{"type":["string","null"],"format":"uri"},"icon_url":{"type":["string","null"],"format":"uri","description":"Service icon URL"},"auth_method":{"type":["string","null"],"enum":["api-key","m2m-oauth","bot-token","wallet","x402-none","none"]},"has_mcp_server":{"type":"boolean"},"mcp_server_url":{"type":["string","null"]},"has_openapi_spec":{"type":"boolean"},"openapi_spec_url":{"type":["string","null"]},"payment_protocols":{"type":"array","items":{"type":"string","enum":["x402","mpp","l402","ap2","tap","agent-pay","ace","acp","ucp","actp","icc"]},"description":"Agent payment / commerce protocols this service supports"},"accepts_mpp":{"type":"boolean","description":"Derived from payment_protocols"},"accepts_x402":{"type":"boolean","description":"Derived from payment_protocols"},"supports_acp":{"type":"boolean","description":"Derived from payment_protocols"},"supports_ucp":{"type":"boolean","description":"Derived from payment_protocols"},"has_skill_file":{"type":"boolean"},"skill_file_url":{"type":["string","null"]},"has_llms_txt":{"type":"boolean"},"llms_txt_url":{"type":["string","null"]},"has_structured_feed":{"type":"boolean"},"structured_feed_url":{"type":["string","null"]},"billing_model":{"type":["string","null"]},"has_free_tier":{"type":"boolean"},"has_webhooks":{"type":"boolean"},"programmatic_onboarding":{"type":["string","null"],"enum":["full","partial","none"]},"bot_friendly_tos":{"type":["string","null"],"enum":["explicit-welcome","neutral","restricts-bots","unknown"]},"bot_score":{"type":"integer","minimum":1,"maximum":5},"vote_count":{"type":"integer","description":"Community upvote count"},"tags":{"type":"array","items":{"type":"string"}},"is_pro":{"type":"boolean"},"is_verified":{"type":"boolean"},"verified_interfaces":{"type":"array","items":{"type":"string"},"description":"The machine-readable interface flags we have actually fetched and confirmed. A flag that is true on the service but absent here is claimed rather than verified — the listing says it exists and nothing has reached it yet. Prefer a verified interface when choosing how to call a service."},"promoted":{"type":"boolean","description":"Whether this is a paid promoted placement (ad)"},"ad":{"type":"boolean","description":"Whether this is an ad (redundant with promoted for compatibility)"},"promotion_type":{"type":["string","null"],"enum":["site","category"],"description":"Type of promotion if promoted"},"created_at":{"type":"string","format":"date-time"},"last_verified_at":{"type":["string","null"],"format":"date-time"}}}}}}