API Docs Finder - OpenAPI/Swagger Spec, llms.txt & Docs URLs
Pricing
from $3.85 / 1,000 domain locateds
API Docs Finder - OpenAPI/Swagger Spec, llms.txt & Docs URLs
For AI agents about to call an unfamiliar API: give a company domain, get its OpenAPI/Swagger spec (version, endpoints, auth), llms.txt, docs, API reference, status page, changelog, official npm/PyPI SDKs and GraphQL endpoint, each URL verified. On 42 API companies: llms.txt found for 35.
Pricing
from $3.85 / 1,000 domain locateds
Rating
0.0
(0)
Developer
NeverEmpty
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
23 days ago
Last modified
Categories
Share
Give your AI agent a company domain and get back where that company's developer resources live, each URL verified: the OpenAPI/Swagger spec (downloaded and read: spec version, API version, endpoint count, auth types, servers), llms.txt and llms-full.txt, the developer docs home, the API reference, the status page (and its current status when allowed), the changelog (and the RSS/Atom feed it declares), the official npm and PyPI SDK packages with the evidence that they are official, and whether a GraphQL endpoint answers. One call, one row per domain, with a one-sentence summary the agent can read as is.
Only verified resources are returned as found: the URL answered 2xx and the content has the expected shape (a JSON/YAML file with an openapi or swagger version, a Markdown llms.txt rather than an HTML page, a docs page whose title or place says so). Links it could not verify (too large, over your download limit, behind a wall) are listed separately in notVerified with the reason. Every place it tried is listed in searched with what it answered, so "not found" comes with the list of places that were checked.
Example
Input:
{ "domains": ["stripe.com"] }
Output (production run pJMr2xEKNHE4SKcBg, shortened):
{"domain": "stripe.com","summary": "stripe.com: OpenAPI 3.0.0 spec \"Stripe API\" version 2026-09-30.endive (612 operations, auth: http basic, http bearer) at https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json; llms.txt at https://stripe.com/llms.txt; docs at https://docs.stripe.com/; API reference at https://docs.stripe.com/api; status page at https://status.stripe.com/; changelog at https://stripe.com/blog/changelog; npm: stripe@22.6.2, @stripe/extensibility-sdk@3.0.0, @stripe/ui-extension-sdk@9.3.0; PyPI: stripe 15.6.1. Not found: GraphQL endpoint.","found": ["openapi", "llms", "docs", "apiReference", "status", "changelog", "sdks"],"missing": ["graphql"],"openapiUrl": "https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json","openapiSpecVersion": "3.0.0","apiVersion": "2026-09-30.endive","endpointCount": 612,"authTypes": ["http basic", "http bearer"],"llmsTxtUrl": "https://stripe.com/llms.txt","docsUrl": "https://docs.stripe.com/","apiReferenceUrl": "https://docs.stripe.com/api","statusPageUrl": "https://status.stripe.com/","statusPageProvider": "custom","changelogUrl": "https://stripe.com/blog/changelog","npmPackages": [{ "name": "stripe", "latestVersion": "22.6.2", "evidence": "it is published or maintained by an @stripe.com account" }],"pypiPackages": [{ "name": "stripe", "latestStableVersion": "15.6.1", "evidence": "its releases are published with the author e-mail @stripe.com" }],"searchedCount": 60}
With no input at all it searches stripe.com, so the Actor always returns a row.
What it finds, per domain
found value | How it is found | How it is verified |
|---|---|---|
openapi | Well-known paths on api., the site and docs hosts (/openapi.json, /openapi.yaml, /swagger.json, /.well-known/openapi.json, /v3/api-docs and more), spec links on the home page, docs pages and in llms.txt, and the free APIs.guru catalog (original source first, then its mirror copy) | Downloaded (up to 20 MB) and parsed as JSON or YAML; must carry openapi: 3.x or swagger: 2.0. You get spec version, title, API version, path and operation counts, auth types from the security schemes, server URLs, and publishedBy (company-domain, company-github, apis-guru-mirror or third-party) |
llms | /llms.txt on the site and docs hosts, then llms-full.txt next to it | Must be Markdown, not HTML, and not a redirect to another page. You get its H1 title, the > summary, the ## section names and the link count |
docs | docs., developer., developers., platform. hosts, /docs, /developers, and docs links on the home page | A real HTML page (not the home page, not a soft 404) whose title says docs/developers/reference, or that sits on a docs host or path |
apiReference | /api, /reference, /api-reference on the docs hosts, and API reference links from the docs and llms.txt | Title says API reference/REST API/reference, or it sits on a docs host with such a path |
status | status.<domain>, <brand>status.com, and status links on the site | The page is a status page (Atlassian Statuspage, incident.io, Instatus, Better Stack, Status.io and others); for Statuspage, /api/v2/status.json is read for currentStatus only when robots.txt allows it |
changelog | /changelog, /docs/changelog, /release-notes on the site and docs hosts, and changelog links | Title or path says changelog/release notes/what's new. The RSS/Atom feed the page declares is returned as changelogFeedUrl as declared (not fetched) |
sdks | npm registry search for the brand name, PyPI release feeds for the likely package names | npm: the package's homepage/repository points to the domain, or it is published or maintained by an account at the domain, or its GitHub org is linked from the company's own pages. PyPI: the release feed's author e-mail is at the domain. Platform-only binary packages are left out |
graphql | api.<domain>/graphql, /graphql, /api/graphql | A GET answer only a GraphQL server gives ("Must provide query string", an errors list with locations/extensions, a 401 pointing to GraphQL docs) or a GraphiQL/Playground page. Nothing is queried; authRequired shows a 401/403 answer |
Measured
One production run on 42 well-known API companies (Stripe, GitHub, Slack, OpenAI, Anthropic, Apify, Shopify, Twilio, Notion, Resend, Supabase, Vercel, Linear, Clerk, HubSpot, Cloudflare, Sentry, Airtable, Mailchimp, Render, Postmark, Zapier, Discord, Dropbox, SendGrid, Spotify, Atlassian, GitLab, DigitalOcean, PagerDuty, Datadog, Algolia, Mapbox, PayPal, Plaid, Zoom, Figma, freee, SmartHR, Chatwork, Asana, PAY.JP), default settings, 540 seconds (run XcCnurPxU1fUmbJux):
| found | domains |
|---|---|
| developer docs | 42 of 42 |
| official SDK packages | 40 |
| status page | 38 |
| llms.txt | 35 |
| API reference | 30 |
| changelog | 29 |
| OpenAPI/Swagger spec (downloaded and read) | 23 |
| GraphQL endpoint | 1 |
4,371 requests, 378 of them robots.txt; 312 URLs were not requested because robots.txt disallows them. A single domain takes about 8 seconds (stripe.com: 67 requests, including its 8 MB spec). Checked against a browser on three production runs (spec version, title and API version; llms.txt title; docs, status, changelog and API reference pages; npm latest version): 60 of 62 checks agreed, and the other 2 were limits of the checking script (an 11.6 MB spec the browser could not hold, and an llms.txt whose first line is not a heading, which this Actor correctly reports as having no title).
Input
| Field | Default | What it does |
|---|---|---|
domains | (example stripe.com) | Domains, 1 to 100 per run. URLs and subdomains are read as their domain (https://docs.stripe.com/api = stripe.com); the same domain twice is searched and charged once. |
include | all | Any of openapi, llms, docs, apiReference, status, changelog, sdks, graphql. Fewer means a faster run; the price per domain is the same. |
verifySpecs | 3 | How many linked or catalog-listed spec files are downloaded and read per domain (0 to 10). Specs at well-known paths are always read. |
onlyChanges | false | Monitor mode: return only domains where something changed since the last run with the same watchName. |
watchName | Name of the remembered state for monitor mode. | |
resetMonitoringState | false | Forget the remembered state before this run. |
maxConcurrency | 3 | Domains searched in parallel (1 to 5). Each site gets at most 3 requests at a time. |
requestTimeoutSecs | 20 | Time limit per request (5 to 60 s); spec downloads get up to 45 s. Each domain gets at most 120 s in total. |
Output columns
status (ok, nothing-found, unreachable, bad-input, budget-reached, no-change), domain, input, summary, found, missing, openapiUrl, openapiTitle, openapiSpecVersion, apiVersion, endpointCount, authTypes, openapiSpecs (each: url, foundVia, publishedBy, format, specVersion, title, apiVersion, pathCount, operationCount, authTypes, serverUrls, sizeBytes, apisGuruKey, description), llmsTxtUrl, llmsFullTxtUrl, llmsTxt (each: url, title, summary, sections, linkCount, sizeBytes, followsSpec), docsUrl, docs, apiReferenceUrl, statusPageUrl, statusPageProvider, currentStatus, statusJsonUrl, changelogUrl, changelogFeedUrl, changelogs, npmPackages and pypiPackages (each with name, latestVersion, url, evidence), graphqlUrl, graphql (url, httpStatus, evidence, authRequired), githubOrgsLinked, notVerified (kind, url, why), skippedByRobots, timeLimitReached, searchedCount, searched (kind, url, result), monitor columns (changeType, changedFields, previous, previousCheckedAt, watchName), note, checkedAt.
Monitor mode
Set watchName and onlyChanges: true and schedule the run. The first run returns every domain. Later runs return only domains where something changed: a spec added or removed, a new API version or endpoint count, new llms.txt sections, a new docs, status or changelog URL, or a new SDK release, with changedFields and the previous values. A resource that could not be read this time (timeout, 5xx, 429, a challenge page) is never reported as removed; it is compared again on the next run.
Pricing
Pay per event:
- Domain located: each domain where at least one resource was verified.
- Domain re-checked (monitor mode only): each domain that was searched and compared but had no change, so no row was returned.
- Run start: once per run that returned or re-checked at least one domain.
Free: domains where nothing could be verified (the row lists every place tried), domains that do not resolve, invalid input, and the rows that say a run hit its maximum total charge. A run whose maximum total charge cannot fit the start fee plus one domain searches nothing and costs nothing.
Use it from an AI agent (MCP)
Through the Apify MCP server the input can be just {"domains": ["example.com"]}. Read summary first; use openapiUrl to load the spec, llmsTxtUrl to load the model-friendly docs, and statusPageUrl/currentStatus to check an outage before blaming your own code.
Limits and rules it follows
- It reads a URL only when that site's robots.txt allows it (
ApiDocsFinder, otherwiseUser-agent: *, RFC 9309 with prefix matching). URLs it skipped for that reason are listed inskippedByRobots. A robots.txt that answers 5xx or not at all means that host is treated as disallowed. - It never signs in, never solves or bypasses a challenge page or CAPTCHA, and does not spoof browser fingerprints. It uses the Actor's own server, no proxy.
- pypi.org project pages answer with a JavaScript check, so PyPI packages are read from their public release feeds (
/rss/project/<name>/releases.xml) instead; the npm registry search and APIs.guru are public APIs. - Domains are searched in parallel (default 3) and each domain gets at most 120 seconds; places not tried in time are listed in
searchedasskipped-time-limitand the row saystimeLimitReached. - It finds what a company publishes on its own domain, subdomains and linked GitHub orgs. Docs on a separate domain that nothing links to (for example a product-branded developer site) may be missed; they then do not appear in
found. - Two-level country suffixes such as
co.jp,co.uk,com.plandgov.auare read as suffixes (allegro.com.plis searched asallegro.com.pl); a bare suffix such ascom.plis refused as input. - Not affiliated with any of the companies named here.
Support
Open an issue on the Issues tab with the domain and the run ID.