From a33eef2a837f23fe966d6615630142ea1f5f6e3b Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Thu, 17 Sep 2026 12:44:20 -0400 Subject: [PATCH 1/2] Say why a call answered found: false, from the reason list every output schema publishes Co-Authored-By: Claude Fable 5.1 --- mcp-server.mdx | 3 ++- quickstart.mdx | 8 +++++--- sdks.mdx | 14 +++++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/mcp-server.mdx b/mcp-server.mdx index e70add6..dde25bf 100644 --- a/mcp-server.mdx +++ b/mcp-server.mdx @@ -535,7 +535,8 @@ Tool failures come back as MCP tool errors with a short machine-readable prefix: A completed lookup that returns a normalized empty result such as `found: false` is not a tool error. It is a successful answer: the lookup ran and the account, post, video, or -page does not exist, was removed or suspended, or the search matched nothing. It is +page does not exist, was removed or suspended, or the search matched nothing, and +`output.reason` says which (`not_found`, or `suspended` for a suspended account). It is charged only when the source charged AnyAPI for the lookup; a miss the source did not bill is free. Read `costUsd` for what the call actually cost, which is `0` when nothing was charged for it. diff --git a/quickstart.mdx b/quickstart.mdx index 4fee987..55f576b 100644 --- a/quickstart.mdx +++ b/quickstart.mdx @@ -119,9 +119,11 @@ Prefer to do it yourself? Follow the manual steps below. ``` - A legitimate "not found" is a success: `output` is `{ "found": false, "data": null }`. - You get it when the account, post, video, or page you named does not exist, has been - removed or suspended, or a search matched nothing. You are charged for it only when the + A legitimate "not found" is a success: `output` is + `{ "found": false, "data": null, "reason": "not_found" }`. You get it when the + account, post, video, or page you named does not exist, has been removed, or a search + matched nothing; a suspended account says `"reason": "suspended"`. The reason is one + word from a fixed list published in every API's output schema. You are charged for it only when the source charged AnyAPI for the lookup; a miss the source did not bill is free. Failed calls are never charged, and `costUsd` is always what this call actually cost you - `0` when nothing was charged for it. See [Not found vs error](/sdks#not-found-vs-error) diff --git a/sdks.mdx b/sdks.mdx index c4f779f..24e0592 100644 --- a/sdks.mdx +++ b/sdks.mdx @@ -164,12 +164,15 @@ flag: `output.found` is `false` when the upstream had no matching entity, which successful answer, not an error. Use `unwrap` to get the data or raise `ResultNotFoundError` when the result is empty. -Every outcome maps to exactly one shape: +Every outcome maps to exactly one shape, and every `found: false` answer says why in +`output.reason`. The reason is one word from a fixed list that is part of every API's output +schema, so it is typed in both SDKs and never the source's own wording: | You asked for | You get | | --- | --- | -| An account, post, video, or channel that does not exist, or a search with no matches | `200` with `output.found: false` and `data: null` | -| An account that is suspended, deleted, or removed by the platform | `200` with `output.found: false` | +| An account, post, video, or channel that does not exist, or a search with no matches | `200` with `output.found: false`, `data: null`, and `reason: "not_found"` | +| An account the platform has suspended | `200` with `output.found: false` and `reason: "suspended"` | +| An account deleted or removed by its owner or the platform | `200` with `output.found: false` and `reason: "not_found"`; the source cannot tell a removed account from one that never existed | | A target the first source returns nothing for, on an API that has more than one source | Some APIs ask the next source before answering `found: false`; a source that states the target does not exist is taken at its word | | An API slug that does not exist | `404` `NotFoundError` | | Input the API cannot accept, such as a malformed URL or a field combination no source serves | `400` `BadRequestError`, never charged | @@ -180,6 +183,11 @@ A `found: false` answer is billed only when the source charged AnyAPI for the lo miss the source refused without billing is free, and `costUsd` / `cost_usd` on the result is always what the call actually cost you. +`reason` is present only on a miss. In TypeScript it is the `NotFoundReason` union on the +`found: false` branch of `Output`; in Python it is `OutputNotFound.reason`. New words can +be added to the list; read it from the API's `outputSchema` in `/v1/apis/{slug}` or the +OpenAPI document rather than hardcoding it. + ```ts TypeScript import { unwrap, ResultNotFoundError } from "@getanyapi/sdk"; From 349548c270de497794e9420e3d7e9c235a08086e Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Thu, 17 Sep 2026 13:59:17 -0400 Subject: [PATCH 2/2] Say that each API's output schema lists only the miss reasons that API can answer with Co-Authored-By: Claude Fable 5.1 --- quickstart.mdx | 4 ++-- sdks.mdx | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/quickstart.mdx b/quickstart.mdx index 55f576b..74edbf9 100644 --- a/quickstart.mdx +++ b/quickstart.mdx @@ -122,8 +122,8 @@ Prefer to do it yourself? Follow the manual steps below. A legitimate "not found" is a success: `output` is `{ "found": false, "data": null, "reason": "not_found" }`. You get it when the account, post, video, or page you named does not exist, has been removed, or a search - matched nothing; a suspended account says `"reason": "suspended"`. The reason is one - word from a fixed list published in every API's output schema. You are charged for it only when the + matched nothing; a suspended account says `"reason": "suspended"` on an API whose source + can tell. Each API's output schema lists the exact reasons it can answer with. You are charged for it only when the source charged AnyAPI for the lookup; a miss the source did not bill is free. Failed calls are never charged, and `costUsd` is always what this call actually cost you - `0` when nothing was charged for it. See [Not found vs error](/sdks#not-found-vs-error) diff --git a/sdks.mdx b/sdks.mdx index 24e0592..750e6b2 100644 --- a/sdks.mdx +++ b/sdks.mdx @@ -165,8 +165,9 @@ successful answer, not an error. Use `unwrap` to get the data or raise `ResultNotFoundError` when the result is empty. Every outcome maps to exactly one shape, and every `found: false` answer says why in -`output.reason`. The reason is one word from a fixed list that is part of every API's output -schema, so it is typed in both SDKs and never the source's own wording: +`output.reason`. Each API's output schema lists the exact words that API can answer with, +so the schema is that API's contract for a miss, it is typed in both SDKs, and it is never +the source's own wording: | You asked for | You get | | --- | --- | @@ -184,9 +185,10 @@ miss the source refused without billing is free, and `costUsd` / `cost_usd` on t result is always what the call actually cost you. `reason` is present only on a miss. In TypeScript it is the `NotFoundReason` union on the -`found: false` branch of `Output`; in Python it is `OutputNotFound.reason`. New words can -be added to the list; read it from the API's `outputSchema` in `/v1/apis/{slug}` or the -OpenAPI document rather than hardcoding it. +`found: false` branch of `Output`; in Python it is `OutputNotFound.reason`. Every API can +answer `not_found`; an API lists `suspended` only when one of its sources can tell a suspended +account apart. An API can gain a word later, so read the list from its `outputSchema` in +`/v1/apis/{slug}` or the OpenAPI document rather than hardcoding it. ```ts TypeScript