From 8fd8db636b081cb2427150de0fd76bc60a5f21a9 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sat, 5 Sep 2026 01:45:25 -0700 Subject: [PATCH] Add the LangChain page, document the stdio server, stop a false claim Three fixes, each verified against the live gateway or the published package. `getanyapi.com/docs/langchain` 404s, so our open PR against LangChain's own integrations tables (langchain-ai/docs#5886) had to point `docs_url` at a GitHub README instead of at us. `langchain.mdx` serves that exact URL. Every tool name, argument and default on it came from installing `langchain-anyapi` 0.1.0 from PyPI and printing each tool's `.name` and `.args_schema.model_json_schema()`, not from the README. The MCP page described only the hosted endpoint. `getanyapi-com/mcp` now publishes a local stdio server that forwards to it, for clients that cannot speak remote Streamable HTTP. The npx, Claude Code and Docker recipes are the ones the MCP Registry entry `io.github.getanyapi-com/anyapi` declares at 0.1.2. The page also told customers that `list_apis`, `search_apis` and `get_api` work "without a key". They do not, and have not. Measured against production: an anonymous `initialize` and `tools/list` both return 200, and `tools/call` on each of those three returns HTTP 401 `unauthorized`. What is keyless is the handshake and the tool listing. The note now says that, and points at `GET /catalog` and `GET /catalog/search`, which really are keyless (both 200 with no credential). This documents current behavior and asks for no change to it. Quickstart now says `q` is optional on `/catalog/search`. Measured today: `?platform=reddit` and `?category=social` each return 200, and a bare `/catalog/search` returns 400 `"name q, category, or platform"`. The generated API reference already had this right. Gate: check-docs-nav.sh, check-agent-contract.sh, mint validate, and mint broken-links all pass. Co-Authored-By: Claude Opus 5 (1M context) --- docs.json | 1 + langchain.mdx | 195 +++++++++++++++++++++++++++++++++++++++++++++++++ mcp-server.mdx | 83 ++++++++++++++++++++- quickstart.mdx | 7 ++ 4 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 langchain.mdx diff --git a/docs.json b/docs.json index 98fa51a..2829f24 100644 --- a/docs.json +++ b/docs.json @@ -38,6 +38,7 @@ "speed-and-price", "mcp-server", "n8n", + "langchain", "agent-skills", "agent-self-signup", "agent-payments" diff --git a/langchain.mdx b/langchain.mdx new file mode 100644 index 0000000..1b60088 --- /dev/null +++ b/langchain.mdx @@ -0,0 +1,195 @@ +--- +title: "LangChain" +description: "Give a LangChain agent the whole AnyAPI catalog through five tools, one key, and a USD wallet." +--- + +`langchain-anyapi` is the official LangChain integration for AnyAPI. It gives an agent +hundreds of data and scraping APIs through five tools, one key, and a wallet billed per +request in real US dollars. + +| | | +|---|---| +| **Package** | [`langchain-anyapi`](https://pypi.org/project/langchain-anyapi/) | +| **Source** | [`getanyapi-com/integrations`](https://github.com/getanyapi-com/integrations) | +| **Python** | 3.10 or newer | +| **Auth** | `ANYAPI_API_KEY`, or `api_key=` on any tool or the toolkit | +| **Tools** | `anyapi_search_apis`, `anyapi_list_apis`, `anyapi_get_api`, `anyapi_run_api`, `anyapi_get_balance` | + +## Five tools, not one per API + +AnyAPI publishes hundreds of APIs. Binding one tool per API would exhaust an agent's +context before it asked its first question, so this package teaches the loop instead: +search or list to find an API, read its input schema, then run it. An agent that learns +those five tools once can reach the entire catalog. + +Input schemas are strict. They reject unknown fields rather than ignoring them, so an +input built from a description instead of a schema usually fails. Always call +`anyapi_get_api` before the first `anyapi_run_api` on an API. + +## Install + +The package depends on `langchain-core`, not on `langchain` itself. Install `langchain` +alongside it if you want the `create_agent` helper used below. + +```bash +pip install langchain langchain-anyapi +``` + +## Authenticate + +Set `ANYAPI_API_KEY` in the environment, or pass `api_key=` to the toolkit or to any +individual tool. Need a key? Create one in the [dashboard](https://getanyapi.com/dashboard), +or see the [Quickstart](/quickstart). + +```bash +export ANYAPI_API_KEY=YOUR_ANYAPI_KEY +``` + +The client is built on first use, so importing the package and constructing a tool need +neither a key nor a network connection. A missing key surfaces only when a tool is +actually called. + +Alongside `api_key`, every tool and the toolkit accept `base_url`, `timeout`, and +`max_retries`. Omit them to keep the SDK defaults. + +## Build an agent + +`AnyAPIToolkit().get_tools()` returns all five tools, and `ANYAPI_INSTRUCTIONS` is a +system prompt that teaches the discover, inspect, run loop and its cost discipline. + +```python +from langchain.agents import create_agent +from langchain_anyapi import ANYAPI_INSTRUCTIONS, AnyAPIToolkit + +agent = create_agent( + "anthropic:claude-sonnet-4-5", + tools=AnyAPIToolkit().get_tools(), + system_prompt=ANYAPI_INSTRUCTIONS, +) +``` + +## Or call one tool at a time + +Each tool is a plain LangChain `BaseTool`, so you can invoke it directly or bind a subset +to your own model. + +```python +from langchain_anyapi import AnyAPIGetAPI, AnyAPIRunAPI, AnyAPISearchAPIs + +AnyAPISearchAPIs().invoke({"query": "reddit trending posts"}) +AnyAPIGetAPI().invoke({"sku_id": "reddit.trending_posts"}) +AnyAPIRunAPI().invoke({"sku_id": "reddit.trending_posts", "input": {"limit": 2}}) +``` + +## Tools + + + + Ranked search across the catalog, returning matches with their descriptions and + without their schemas. Never charges. + + | Field | Type | Required | Description | + |---|---|---|---| + | `query` | string | yes | What you need, in your own words | + | `category` | string | no | Category slug to narrow the search | + | `platform` | string | no | Platform slug to narrow the search | + | `limit` | integer | no | Cap on matches returned | + + **Returns** `results`, `total`, and `ranking`. Each result carries `id`, `platform`, + `name`, `description`, `category`, `pricing`, `execution`, and `relevance`. + + This tool requires `query`. The REST endpoint behind it also accepts `category` or + `platform` on their own, so reach for `anyapi_list_apis` when you want to enumerate a + category rather than search it. + + + Browse APIs as lightweight summaries, optionally filtered by category. Descriptions + and schemas are omitted, so listing stays cheap in context. Never charges. + + | Field | Type | Required | Description | + |---|---|---|---| + | `category` | string | no | Category slug to filter by | + + **Returns** `apis`, an array of `id`, `name`, `category`, `pricing`, `heavy`, and + `execution`. + + + Get one API in full, including the strict input schema you need to build a valid + payload. Never charges. + + | Field | Type | Required | Description | + |---|---|---|---| + | `sku_id` | string | yes | The API slug to describe | + + **Returns** the summary fields plus `description`, `provider`, `method`, `path`, + `inputSchema`, `outputSchema`, `lanes`, and `latency`, which is `null` when there are + no successful observations to report. + + + Execute one API with a normalized input payload. This is the only tool that touches + your wallet. + + | Field | Type | Required | Description | + |---|---|---|---| + | `sku_id` | string | yes | The API slug to execute | + | `input` | object | yes | Payload matching that API's input schema | + | `fields` | array of strings | no | Keys to keep on each result item | + | `max_items` | integer | no | Cap the number of result rows returned | + | `summary` | boolean | no | Return a structural outline instead of the full data | + + **Returns** `found`, `data`, `provider`, `costUsd`, `items`, and `resultId`. + + `fields`, `max_items`, and `summary` are response-budget controls. They keep a large + result from flooding the context window and change only what comes back to you, never + what you are charged. + + + Get the remaining wallet balance for the key you authenticate with. Takes no + arguments and never charges. + + **Returns** `usd`, the remaining balance. + + + +## Async + +Every tool has a real async path built on the SDK's async client, not the sync client on +a worker thread. Use `ainvoke` anywhere you would use `invoke`. + +```python +from langchain_anyapi import AnyAPISearchAPIs + +results = await AnyAPISearchAPIs().ainvoke({"query": "tiktok profile"}) +``` + +## Errors + +A failed call returns a readable payload rather than aborting the agent's run. The +payload carries `error` and `status`, plus `code` and `requestId` when the gateway sends +them. An agent can read that, adjust, and carry on within the same turn. + +Calling a tool with no key resolves at call time and returns this, with nothing charged: + +```python +{"error": "no API key: pass api_key= or set ANYAPI_API_KEY", "status": 0} +``` + +## Prices + +Prices come off the wire exactly as AnyAPI published them, and this package never +recomputes one from another. Every static price is quoted twice: `maxUsd` is what one +request is billed, and `maxPer1kUsd` is the same price per 1,000 requests. Per 1,000 is +the denomination AnyAPI quotes customers in, because most of the catalog costs a fraction +of a cent per call. + +There is no quote tool here. `anyapi_get_api` already publishes `pricing.from.maxUsd`, +the most a first-choice run is billed, and `pricing.failoverMaxUsd`, the ceiling for any +run, so an agent can bound its spend before it calls. A completed run reports its actual +charge as `costUsd`. + + + Not using LangChain? The same loop is available over the + [MCP server](/mcp-server), through the typed [Python and TypeScript SDKs](/sdks), and as + an [agent skill](/agent-skills). Need help? Reach out at + [support@getanyapi.com](mailto:support@getanyapi.com). + diff --git a/mcp-server.mdx b/mcp-server.mdx index 8487265..17d23f6 100644 --- a/mcp-server.mdx +++ b/mcp-server.mdx @@ -24,9 +24,12 @@ registry-driven tool surface that covers the entire catalog — no tool-per-API The MCP surface is **API-key only** — session tokens are not accepted. Send your key as - `Authorization: Bearer `. Discovery (`list_apis`, `search_apis`, `get_api`) works - **without a key**, so an agent can browse the catalog and read schemas before credentials are - set up; `run_api` and `get_balance` require authentication. + `Authorization: Bearer `. Connecting is keyless: `initialize` and `tools/list` + answer without a credential, so a client can finish the handshake and read the tool list + before your key is in place. Every tool call needs a key, including the discovery tools + (`list_apis`, `search_apis`, `get_api`); without one they return `unauthorized`. To browse + the catalog with no key at all, use the public REST endpoints `GET /catalog` and + `GET /catalog/search` instead. ## Connect @@ -123,6 +126,80 @@ registry-driven tool surface that covers the entire catalog — no tool-per-API +## Run it locally over stdio + +Some MCP clients can only launch a local server and speak to it over stdio. For those, +AnyAPI publishes a small local server that forwards everything to the hosted endpoint +above. + +| | | +|---|---| +| **npm** | [`anyapi-mcp`](https://www.npmjs.com/package/anyapi-mcp) | +| **Image** | `ghcr.io/getanyapi-com/mcp` | +| **Source** | [`getanyapi-com/mcp`](https://github.com/getanyapi-com/mcp) | +| **Transport** | stdio | +| **Auth** | `ANYAPI_API_KEY` in the environment | +| **Node** | 20 or newer | + +It declares no tools of its own. Every listing and every call is forwarded to +`https://api.getanyapi.com/mcp`, so you get whatever the hosted server publishes, and a +catalog change reaches you without upgrading anything. + + + + ```json + { + "mcpServers": { + "anyapi": { + "command": "npx", + "args": ["-y", "anyapi-mcp"], + "env": { "ANYAPI_API_KEY": "YOUR_ANYAPI_KEY" } + } + } + } + ``` + + + ```bash + claude mcp add anyapi -e ANYAPI_API_KEY=YOUR_ANYAPI_KEY -- npx -y anyapi-mcp + ``` + + + ```json + { + "mcpServers": { + "anyapi": { + "command": "docker", + "args": [ + "run", "-i", "--rm", + "-e", "ANYAPI_API_KEY", + "ghcr.io/getanyapi-com/mcp" + ], + "env": { "ANYAPI_API_KEY": "YOUR_ANYAPI_KEY" } + } + } + } + ``` + + The image is public, so the pull needs no credential. It is built for `linux/amd64`, + so Docker runs it under emulation on an Apple Silicon machine and prints a platform + warning on startup. + + + +`ANYAPI_API_KEY` is optional at startup. Without it the server still starts and lists its +tools, because the hosted server answers `initialize` and `tools/list` unauthenticated. +Set the key before your agent calls a tool: every tool call needs one. + +The server is listed in the official +[MCP Registry](https://registry.modelcontextprotocol.io) as +`io.github.getanyapi-com/anyapi`, which declares the hosted endpoint and both packages. + + + Prefer the hosted endpoint when your client speaks remote Streamable HTTP. It is one less + moving part, with nothing to install and nothing to keep up to date. + + ## Tools Discovery (`list_apis`, `search_apis`, `get_api`) is free; only `run_api` touches your diff --git a/quickstart.mdx b/quickstart.mdx index d2422ab..ee328b3 100644 --- a/quickstart.mdx +++ b/quickstart.mdx @@ -146,10 +146,17 @@ curl -s "https://api.getanyapi.com/catalog?category=social" # Search by intent curl -s "https://api.getanyapi.com/catalog/search?q=tiktok&limit=10" +# Or scope with no query at all: everything one platform can do +curl -s "https://api.getanyapi.com/catalog/search?platform=reddit" + # Inspect one API's input and output schemas curl -s https://api.getanyapi.com/v1/apis/tiktok.profile -H "X-API-Key: YOUR_ANYAPI_KEY" ``` +On `/catalog/search`, `q` is optional. Name any of `q`, `category`, or `platform`, in any +combination: a scope on its own is a complete question, and only a request naming none of +the three is rejected. Both endpoints are free and need no key. + Treat `inputSchema` as the request contract. Send only properties it declares. A field used by one search API, including `limit`, `cursor`, `page`, or `sort`, may be invalid for another.