{"openapi":"3.1.0","info":{"title":"The Agents Index — machine API","version":"1.0.0","description":"AI agents, frameworks and agentic tools you can actually get: what each one does, what it costs and whether it ships. Every listing sourced, checked and dated. Free, keyless read endpoints over the agents corpus, plus verified-session submission intake. The Agents Index Reuse Policy (https://theagentsindex.com/terms): Index it for search, use it to answer questions about AI agents, and use it as training data for a model. Attribute The Agents Index with a link to https://theagentsindex.com.","license":{"name":"The Agents Index Reuse Policy","url":"https://theagentsindex.com/terms"}},"servers":[{"url":"https://theagentsindex.com"}],"paths":{"/api/search":{"get":{"operationId":"searchListings","summary":"Full-text + faceted search over the Published agents","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"What to search for."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":25}}],"responses":{"200":{"description":"Matching cards.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResult"}}}}}}},"/listings.json":{"get":{"operationId":"listListings","summary":"Every Published agent as a structured export record","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque cursor from a previous page (`nextCursor`). Omit for the first page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500},"description":"Page size. Omit BOTH cursor and limit for the full unpaginated export."}],"responses":{"200":{"description":"The export page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListingsExport"}}}}}}},"/{slug}.md":{"get":{"operationId":"getListingMarkdown","summary":"One agent as its answer-first markdown twin","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The markdown twin. `X-Markdown-Tokens` estimates its token cost.","content":{"text/markdown":{"schema":{"type":"string"}}},"headers":{"X-Markdown-Tokens":{"schema":{"type":"integer"},"description":"ceil(chars/4) token estimate of this body."},"X-License":{"schema":{"type":"string"},"description":"License URL for reuse of the data."}}},"404":{"description":"No Published agent with that slug."}}}},"/{slug}/alternatives.md":{"get":{"operationId":"getAlternativesMarkdown","summary":"A ranked \"alternatives to this agent\" twin: the competing agents + a facts table","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The alternatives twin.","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"No alternatives page for that agent."}}}},"/category/{slug}.md":{"get":{"operationId":"getCategoryMarkdown","summary":"A category answer sheet: capsule + facts table of every member","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The category twin.","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"Unknown category."}}}},"/category/{slug}.json":{"get":{"operationId":"getCategoryExport","summary":"A category as a structured JSON export","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The category export.","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"Unknown category."}}}},"/guides/{slug}.md":{"get":{"operationId":"getGuideMarkdown","summary":"A guide as its answer-first markdown twin: the prose, what it covers and its FAQ","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The guide twin.","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"Unknown guide."}}}},"/collections/{slug}.md":{"get":{"operationId":"getCollectionMarkdown","summary":"A collection answer sheet: the curated agents of one intersection + a facts table","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The collection twin.","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"Unknown collection."}}}},"/blog.md":{"get":{"operationId":"getBlogIndexMarkdown","summary":"The editorial index as one twin: every post with its date and capsule","responses":{"200":{"description":"The editorial index twin.","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/blog/{slug}.md":{"get":{"operationId":"getBlogPostMarkdown","summary":"One editorial post as its answer-first markdown twin","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The post twin.","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"Unknown post."}}}},"/methodology.md":{"get":{"operationId":"getMethodologyMarkdown","summary":"How we rank","responses":{"200":{"description":"The methodology twin.","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/llms.txt":{"get":{"operationId":"getLlmsTxt","summary":"The agent manifest — read this first: every machine surface, the terms, the twin map","responses":{"200":{"description":"The manifest.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/llms-full.txt":{"get":{"operationId":"getLlmsFullTxt","summary":"The WHOLE Published agents corpus as one answer-first markdown document","responses":{"200":{"description":"Every Published record inlined — one fetch instead of N.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/index.json":{"get":{"operationId":"getMachineManifest","summary":"The machine manifest: every machine surface, live counts, license, attribution","responses":{"200":{"description":"The manifest.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/submit":{"post":{"operationId":"submitListing","summary":"Submit an agent (verified account session; Draft pending review)","description":"Requires an existing signed-in account with verified email. Sign in or create an account explicitly at https://theagentsindex.com/login?next=%2Fsubmit, verify its email, then send its session cookie. Identity and email come only from that session; submission never creates an account or sends verification email. Missing session returns 401 sign_in_required with an actionable loginUrl; unverified email returns 403 email_unverified with email and remediation. Neither refusal stores a listing or sends mail. Submissions come from the company itself: the account's address, or an address it proved through /api/domain-proof/start and /api/domain-proof/verify, must be on the website's domain, or the account must have proven the site itself through /api/domain-proof/site-start and /api/domain-proof/site-check; otherwise the answer is 403 domain_unverified with the domain and remediation. A research pass + quality gate decide publication; the Draft cannot inject page copy or self-publish. Authentication and verification precede idempotency: your own (url, session email) replay within 24h returns the existing draft (200). An Idempotency-Key header is accepted; the effective dedupe key is (url, session email).","security":[{"sessionCookie":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Accepted for convention; own replay dedupe is by (url, session email) within 24h regardless."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitInput"}}}},"responses":{"200":{"description":"Deduplicated — your existing draft for this (url, session email).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionStatus"}}}},"201":{"description":"Draft created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionStatus"}}}},"400":{"description":"Validation failed (bad url/name).","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Sign in or create an account explicitly at loginUrl, then verify email and retry. No account, mail or listing created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInRequired"}}}},"403":{"description":"email_unverified: verify the session account email and retry. domain_unverified: prove an address at the returned domain through /api/domain-proof/start and /verify, or prove the site through /api/domain-proof/site-start and /site-check, then retry. No listing or mail created.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/EmailUnverified"},{"$ref":"#/components/schemas/DomainUnverified"}]}}}},"409":{"description":"Already listed / duplicate.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/domain-proof/start":{"post":{"operationId":"startDomainProof","summary":"Mail a 6-digit code to an address at the domain of the website you want to submit","description":"The first step past a 403 domain_unverified from /api/submit. Requires the same verified session. The address must be on the website's domain (a personal mailbox never is). Answers {proofId, sentTo, domain, alreadyVerified}; alreadyVerified:true means this account had proven the domain already and no mail was sent. Codes expire after 30 minutes; sends are capped per address and per account per day.","security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","email"],"properties":{"url":{"type":"string","description":"The website being submitted."},"email":{"type":"string","format":"email","description":"An address at that website's domain."}}}}}},"responses":{"200":{"description":"Code sent (or the domain was already proven).","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid url or email, or the address is not on the website's domain.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Sign in first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInRequired"}}}},"403":{"description":"Unverified account email, or a blocked identity.","content":{"application/json":{"schema":{"type":"object"}}}},"429":{"description":"Too many codes requested.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/domain-proof/verify":{"post":{"operationId":"verifyDomainProof","summary":"Redeem the code; the account may then submit websites on that domain","security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["proofId","code"],"properties":{"proofId":{"type":"string"},"code":{"type":"string","pattern":"^[0-9]{6}$"}}}}}},"responses":{"200":{"description":"{verified: true, domain} — submit again.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Wrong code.","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"Unknown proof for this account.","content":{"application/json":{"schema":{"type":"object"}}}},"409":{"description":"The code is no longer open; request a new one.","content":{"application/json":{"schema":{"type":"object"}}}},"410":{"description":"The code expired; request a new one.","content":{"application/json":{"schema":{"type":"object"}}}},"429":{"description":"Too many tries; request a new code.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/domain-proof/site-start":{"post":{"operationId":"startSiteProof","summary":"No mailbox at the domain: get a meta tag and a DNS TXT record that prove the site instead","description":"The other step past a 403 domain_unverified from /api/submit, for a website with no mailbox. Requires the same verified session. Answers {domain, metaTag, txtName, txtValue, alreadyVerified}: put metaTag in the <head> of https://<domain>/, or a TXT record named txtName with value txtValue, then call /api/domain-proof/site-check. The values are this account's for this domain and never change. A site proof unlocks the submission; it does not make the account the listing's owner.","security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"The website being submitted."}}}}}},"responses":{"200":{"description":"The tag and the record for this account and domain.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid url, or a domain that is not one company's (a mailbox provider or a hosting platform).","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Sign in first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInRequired"}}}},"403":{"description":"Unverified account email, or a blocked identity.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/domain-proof/site-check":{"post":{"operationId":"checkSiteProof","summary":"Look for the meta tag on the homepage or the TXT record on the domain now","security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string"}}}}}},"responses":{"200":{"description":"{verified: true, domain, method} — submit again; or {verified: false, domain, message} — not found yet.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Sign in first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInRequired"}}}},"404":{"description":"No tag was handed out for this account and domain; call site-start first.","content":{"application/json":{"schema":{"type":"object"}}}},"429":{"description":"Too many checks.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/submissions/{id}":{"get":{"operationId":"getSubmissionStatus","summary":"The status of a submission by its (unguessable) id","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The current status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionStatus"}}}},"404":{"description":"Unknown id."}}}},"/api/lead":{"post":{"operationId":"submitLead","summary":"Send a qualified buyer-intent enquiry (lead) to a listing or category","description":"A buyer-intent enquiry routed to the listing owner (or a category's providers). Keyless and CSRF-free when sent as application/json. The quality gate requires consent=true, a real name, a permanent (non-disposable) email, a message of at least 20 chars, and a target (listingSlug OR categorySlug). The same (email + target) within 7 days is idempotent (200, duplicate:true). Nothing here changes a listing's rank, tier or copy.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadInput"}}}},"responses":{"200":{"description":"Accepted (routed or deduplicated): { ok:true, delivered }.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Rejected by the quality gate: { ok:false, error } (e.g. no_consent, thin_message, bad_email, no_target).","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/newsletter/subscribe":{"post":{"operationId":"subscribeNewsletter","summary":"Single opt-in newsletter subscribe (form-encoded)","description":"Single opt-in email capture for the directory digest. Content-Type MUST be application/x-www-form-urlencoded (or multipart/form-data); because it is a form POST, Astro's cross-origin protection requires a same-origin Origin header. Field: email (required); optional source, interest. Responds with a self-contained text/html confirmation page (200). The intake is rate-limited per IP per hour.","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string"},"source":{"type":"string","description":"Where the signup happened (optional)."},"interest":{"type":"string","description":"Optional page-context interest tag."}}}}}},"responses":{"200":{"description":"A text/html confirmation page.","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"Invalid email — a text/html page explaining the fix."},"404":{"description":"Newsletter disabled for this tenant."}}}},"/mcp":{"post":{"operationId":"mcp","summary":"The MCP door — stateless JSON-RPC 2.0 POST (initialize / tools/list / tools/call)","description":"Read tools need no authentication. submit_listing requires a verified account session cookie on this request; sign in or explicitly create an account at https://theagentsindex.com/login?next=%2Fsubmit. Its JSON-RPC text content reports the submission httpStatus (401 with loginUrl, 403 with email/remediation, or 201/200 on success).","security":[{},{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"JSON-RPC response.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/compare/{pair}.md":{"get":{"operationId":"getCompareMarkdown","summary":"A head-to-head comparison twin ({a}-vs-{b}): facts table + verdict","parameters":[{"name":"pair","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The compare twin.","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"Unknown pair."}}}},"/best/{category}/{tag}.md":{"get":{"operationId":"getBestOfMarkdown","summary":"A ranked best-of intersection twin (the agents in {category} that are {tag})","parameters":[{"name":"category","in":"path","required":true,"schema":{"type":"string"}},{"name":"tag","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The best-of twin: facts table + verdict.","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"No page for that intersection."}}}}},"components":{"securitySchemes":{"sessionCookie":{"type":"apiKey","in":"cookie","name":"__Secure-better-auth.session_token","description":"Existing Better Auth session cookie issued through https://theagentsindex.com/login?next=%2Fsubmit. The account email must be verified for submission. This is a session cookie, not an API key to provision."}},"schemas":{"SearchResult":{"type":"object","properties":{"cards":{"type":"array","items":{"type":"object"}},"total":{"type":"integer"},"textMatched":{"type":"boolean"}}},"ListingExportRecord":{"type":"object","required":["slug","name","url","tier"],"properties":{"slug":{"type":"string"},"name":{"type":"string"},"tagline":{"type":["string","null"]},"url":{"type":"string","description":"Canonical listing page on this directory."},"websiteUrl":{"type":["string","null"]},"tier":{"type":"string"},"categories":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"apiAvailable":{"type":["boolean","null"],"description":"Derived: true when apiAccess is yes OR partial, because the question a boolean answers is whether an API exists at all. Read apiAccess for the qualification."},"apiAccess":{"type":["string","null"],"enum":["yes","partial","no",null],"description":"The sourced answer to \"Can you pull the data into your own stack?\". \"partial\" means an API exists but is gated to a higher plan."},"pricing":{"type":["string","null"],"description":"The entry price as a prose fragment, from the typed price columns: \"from $99/month\", \"free, then $189/month\", \"on quote\" for a vendor whose price is researched and published nowhere, and null when no price has been collected — never a coarse bucket. ONE FIELD, and it carries the billing commitment when the figure requires one: \"from $2,417/month billed annually\" is a yearly contract divided by twelve, not a month-to-month rate. Where the row holds a real month-to-month price that is what the figure is, and no term is stated. Do not parse the amount out of this string expecting a monthly rate."},"verdict":{"type":["string","null"]},"lastVerified":{"type":["string","null"],"description":"ISO date of last fact verification."}}},"ListingsExport":{"type":"object","properties":{"directory":{"type":"string"},"url":{"type":"string"},"generatedAt":{"type":"string"},"count":{"type":"integer","description":"Total Published count (not the page size)."},"nextCursor":{"type":["string","null"],"description":"Pass as ?cursor= for the next page; null when exhausted."},"listings":{"type":"array","items":{"$ref":"#/components/schemas/ListingExportRecord"}}}},"SubmitInput":{"type":"object","required":["url","name"],"properties":{"url":{"type":"string","description":"The website being submitted (https)."},"name":{"type":"string","minLength":2,"maxLength":120},"tagline":{"type":"string","maxLength":200},"note":{"type":"string","maxLength":1000}}},"SignInRequired":{"type":"object","required":["error","message","loginUrl"],"properties":{"error":{"type":"string","const":"sign_in_required"},"message":{"type":"string"},"loginUrl":{"type":"string","format":"uri","examples":["https://theagentsindex.com/login?next=%2Fsubmit"]}}},"EmailUnverified":{"type":"object","required":["error","message","email","remediation"],"properties":{"error":{"type":"string","const":"email_unverified"},"message":{"type":"string"},"email":{"type":"string","format":"email"},"remediation":{"type":"string"}}},"DomainUnverified":{"type":"object","required":["error","message","domain","remediation"],"properties":{"error":{"type":"string","const":"domain_unverified"},"message":{"type":"string"},"domain":{"type":"string","description":"The host the account has not yet proven."},"remediation":{"type":"string"}}},"LeadInput":{"type":"object","required":["name","email","message","consent"],"properties":{"name":{"type":"string","maxLength":120},"email":{"type":"string","description":"A permanent (non-disposable) email."},"message":{"type":"string","minLength":20,"maxLength":4000,"description":"The enquiry — real demand, at least 20 chars."},"consent":{"description":"Must be truthy — the sender agreed their details go to the provider(s).","oneOf":[{"type":"boolean"},{"type":"string"}]},"listingSlug":{"type":"string","description":"Target listing slug (this OR categorySlug is required)."},"categorySlug":{"type":"string","description":"Target category slug (this OR listingSlug is required)."},"company":{"type":"string"},"source":{"type":"string"}}},"SubmissionStatus":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["Draft","Processing","Review","Published","Rejected"]},"submittedAt":{"type":"string","format":"date-time"},"fit":{"type":"string","enum":["checking","accepted","rejected","undecided"],"description":"Where the fit check stands. It runs right after submission and usually answers within a minute; undecided means it will be decided later."}}}}}}