{"openapi":"3.1.0","jsonSchemaDialect":"https://spec.openapis.org/oas/3.1/dialect/base","info":{"title":"v2ex.com API — compiled by Unbrowse","version":"2026.09.28","summary":"Call v2ex.com's own capabilities over HTTP, without a browser.","description":"The first-party capabilities of v2ex.com, compiled from the site's own requests. Each tool is one POST with its inputs as a JSON body; the answer is the run, with the site's result when it is verified. Only verified results bill. The same tools are an MCP server at https://unbrowse.ai/api/v1/sites/v2ex.com/mcp. Unofficial: not made or endorsed by v2ex.com.","contact":{"name":"Unbrowse","url":"https://unbrowse.ai"}},"externalDocs":{"description":"Calling an indexed website","url":"https://unbrowse.ai/docs/site-apis"},"servers":[{"url":"https://unbrowse.ai","description":"Unbrowse"}],"security":[{"bearer":[]}],"tags":[{"name":"v2ex.com","description":"Tools compiled from v2ex.com"}],"paths":{"/api/v1/sites/v2ex.com/call/v2ex_com__hot":{"post":{"operationId":"v2ex_com__hot","summary":"v2ex.hot","description":"Read public V2EX hot; returns one bounded API page. No inputs. Read-only on v2ex.com.","tags":["v2ex.com"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Deadline"},{"$ref":"#/components/parameters/EndUser"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results to return, within the source's supported limit."},"page":{"type":"integer","minimum":1,"maximum":100,"default":1,"description":"One-based result page to request."},"select":{"type":"array","items":{"type":"string"},"description":"Keep only these parts of the result: dots walk keys, [] maps over a list, a final {a,b} keeps several keys (e.g. \"results[].{title,url}\"). Unmatched paths come back in selectMissing."},"deadlineMs":{"type":"integer","minimum":1000,"maximum":120000,"description":"Maximum execution time in milliseconds, between 1000 and 120000."},"idempotencyKey":{"type":"string","minLength":1,"maxLength":256,"description":"Stable retry identity for this action"}},"required":[],"additionalProperties":false},"example":{"limit":20,"page":1}}}},"responses":{"200":{"description":"The run finished: `status` succeeded carries the site's verified `result`; failed carries `error` (never billed).","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","description":"The run, for unbrowse_inspect / resume / cancel"},"status":{"type":"string","enum":["succeeded","input_required","failed","outcome_unknown","cancelled","working","accepted","expired"],"description":"succeeded carries the site's result; input_required lists what to answer (unbrowse_resume)"},"capabilityId":{"type":["string","null"]},"result":{"type":"object"},"error":{"type":["object","null"]},"requirements":{"type":"array"},"via":{"type":["string","null"],"enum":["http","rendered",null]}},"required":["runId","status"],"additionalProperties":true}}}},"202":{"description":"The run needs input (`status` input_required): answer `requirements` with POST /api/v1/runs/{runId}/responses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"400":{"description":"The body is not JSON, or a value is malformed (invalid_input, bad_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No API key, or an invalid one: send Authorization: Bearer <key>.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Out of verified calls this month (quota_exceeded) or out of paid credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such tool on this site (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The tool is quarantined (tool_quarantined): it failed its checks and is rechecked automatically; the message says what to use now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"An input the tool does not take (unknown_argument): the message lists the ones it does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (by the site or by Unbrowse): retry after the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"run_timeout: the run did not finish within its deadline. It keeps going: poll the given runId. A verified success is billed once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunTimeout"}}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -s 'https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__hot' \\\n  -H \"authorization: Bearer $UNBROWSE_API_KEY\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\"limit\":20,\"page\":1}'"},{"lang":"TypeScript","label":"TypeScript","source":"import { Unbrowse } from \"@unbrowse/sdk\";\n\nconst ub = new Unbrowse(); // reads UNBROWSE_API_KEY\nconst run = await ub.callTool(\"v2ex.com\", \"v2ex_com__hot\", {\n  limit: 20,\n  page: 1,\n});\nif (run.status === \"succeeded\") console.log(run.result);"},{"lang":"Python","label":"Python","source":"import os, requests\n\nr = requests.post(\n    \"https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__hot\",\n    headers={\"Authorization\": f\"Bearer {os.environ['UNBROWSE_API_KEY']}\"},\n    json={\n        \"limit\": 20,\n        \"page\": 1,\n    },\n    timeout=120,\n)\nr.raise_for_status()\nrun = r.json()\nif run[\"status\"] == \"succeeded\":\n    print(run[\"result\"])"}],"x-unbrowse-capability":"v2ex.hot","x-unbrowse-read-only":true,"x-unbrowse-version":"2026.09.28"}},"/api/v1/sites/v2ex.com/call/v2ex_com__node":{"post":{"operationId":"v2ex_com__node","summary":"v2ex.node","description":"Read public V2EX node; returns one bounded API page. Inputs: node. Read-only on v2ex.com.","tags":["v2ex.com"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Deadline"},{"$ref":"#/components/parameters/EndUser"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"node":{"type":"string","minLength":1,"description":"V2EX node slug, such as python; not its display label or URL."},"limit":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results to return, within the source's supported limit."},"page":{"type":"integer","minimum":1,"maximum":100,"default":1,"description":"One-based result page to request."},"select":{"type":"array","items":{"type":"string"},"description":"Keep only these parts of the result: dots walk keys, [] maps over a list, a final {a,b} keeps several keys (e.g. \"results[].{title,url}\"). Unmatched paths come back in selectMissing."},"deadlineMs":{"type":"integer","minimum":1000,"maximum":120000,"description":"Maximum execution time in milliseconds, between 1000 and 120000."},"idempotencyKey":{"type":"string","minLength":1,"maxLength":256,"description":"Stable retry identity for this action"}},"required":["node"],"additionalProperties":false}}}},"responses":{"200":{"description":"The run finished: `status` succeeded carries the site's verified `result`; failed carries `error` (never billed).","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","description":"The run, for unbrowse_inspect / resume / cancel"},"status":{"type":"string","enum":["succeeded","input_required","failed","outcome_unknown","cancelled","working","accepted","expired"],"description":"succeeded carries the site's result; input_required lists what to answer (unbrowse_resume)"},"capabilityId":{"type":["string","null"]},"result":{"type":"object"},"error":{"type":["object","null"]},"requirements":{"type":"array"},"via":{"type":["string","null"],"enum":["http","rendered",null]}},"required":["runId","status"],"additionalProperties":true}}}},"202":{"description":"The run needs input (`status` input_required): answer `requirements` with POST /api/v1/runs/{runId}/responses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"400":{"description":"The body is not JSON, or a value is malformed (invalid_input, bad_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No API key, or an invalid one: send Authorization: Bearer <key>.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Out of verified calls this month (quota_exceeded) or out of paid credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such tool on this site (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The tool is quarantined (tool_quarantined): it failed its checks and is rechecked automatically; the message says what to use now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"An input the tool does not take (unknown_argument): the message lists the ones it does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (by the site or by Unbrowse): retry after the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"run_timeout: the run did not finish within its deadline. It keeps going: poll the given runId. A verified success is billed once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunTimeout"}}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -s 'https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__node' \\\n  -H \"authorization: Bearer $UNBROWSE_API_KEY\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\"node\":\"<node>\"}'"},{"lang":"TypeScript","label":"TypeScript","source":"import { Unbrowse } from \"@unbrowse/sdk\";\n\nconst ub = new Unbrowse(); // reads UNBROWSE_API_KEY\nconst run = await ub.callTool(\"v2ex.com\", \"v2ex_com__node\", {\n  node: \"<node>\",\n});\nif (run.status === \"succeeded\") console.log(run.result);"},{"lang":"Python","label":"Python","source":"import os, requests\n\nr = requests.post(\n    \"https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__node\",\n    headers={\"Authorization\": f\"Bearer {os.environ['UNBROWSE_API_KEY']}\"},\n    json={\n        \"node\": \"<node>\",\n    },\n    timeout=120,\n)\nr.raise_for_status()\nrun = r.json()\nif run[\"status\"] == \"succeeded\":\n    print(run[\"result\"])"}],"x-unbrowse-capability":"v2ex.node","x-unbrowse-read-only":true,"x-unbrowse-version":"2026.09.28"}},"/api/v1/sites/v2ex.com/call/v2ex_com__replies":{"post":{"operationId":"v2ex_com__replies","summary":"v2ex.replies","description":"Read public V2EX replies; returns one bounded API page. Inputs: topicId. Read-only on v2ex.com.","tags":["v2ex.com"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Deadline"},{"$ref":"#/components/parameters/EndUser"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"topicId":{"type":"integer","minimum":1,"description":"Positive numeric V2EX topic ID."},"limit":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results to return, within the source's supported limit."},"page":{"type":"integer","minimum":1,"maximum":100,"default":1,"description":"One-based result page to request."},"select":{"type":"array","items":{"type":"string"},"description":"Keep only these parts of the result: dots walk keys, [] maps over a list, a final {a,b} keeps several keys (e.g. \"results[].{title,url}\"). Unmatched paths come back in selectMissing."},"deadlineMs":{"type":"integer","minimum":1000,"maximum":120000,"description":"Maximum execution time in milliseconds, between 1000 and 120000."},"idempotencyKey":{"type":"string","minLength":1,"maxLength":256,"description":"Stable retry identity for this action"}},"required":["topicId"],"additionalProperties":false}}}},"responses":{"200":{"description":"The run finished: `status` succeeded carries the site's verified `result`; failed carries `error` (never billed).","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","description":"The run, for unbrowse_inspect / resume / cancel"},"status":{"type":"string","enum":["succeeded","input_required","failed","outcome_unknown","cancelled","working","accepted","expired"],"description":"succeeded carries the site's result; input_required lists what to answer (unbrowse_resume)"},"capabilityId":{"type":["string","null"]},"result":{"type":"object"},"error":{"type":["object","null"]},"requirements":{"type":"array"},"via":{"type":["string","null"],"enum":["http","rendered",null]}},"required":["runId","status"],"additionalProperties":true}}}},"202":{"description":"The run needs input (`status` input_required): answer `requirements` with POST /api/v1/runs/{runId}/responses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"400":{"description":"The body is not JSON, or a value is malformed (invalid_input, bad_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No API key, or an invalid one: send Authorization: Bearer <key>.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Out of verified calls this month (quota_exceeded) or out of paid credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such tool on this site (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The tool is quarantined (tool_quarantined): it failed its checks and is rechecked automatically; the message says what to use now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"An input the tool does not take (unknown_argument): the message lists the ones it does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (by the site or by Unbrowse): retry after the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"run_timeout: the run did not finish within its deadline. It keeps going: poll the given runId. A verified success is billed once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunTimeout"}}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -s 'https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__replies' \\\n  -H \"authorization: Bearer $UNBROWSE_API_KEY\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\"topicId\":1}'"},{"lang":"TypeScript","label":"TypeScript","source":"import { Unbrowse } from \"@unbrowse/sdk\";\n\nconst ub = new Unbrowse(); // reads UNBROWSE_API_KEY\nconst run = await ub.callTool(\"v2ex.com\", \"v2ex_com__replies\", {\n  topicId: 1,\n});\nif (run.status === \"succeeded\") console.log(run.result);"},{"lang":"Python","label":"Python","source":"import os, requests\n\nr = requests.post(\n    \"https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__replies\",\n    headers={\"Authorization\": f\"Bearer {os.environ['UNBROWSE_API_KEY']}\"},\n    json={\n        \"topicId\": 1,\n    },\n    timeout=120,\n)\nr.raise_for_status()\nrun = r.json()\nif run[\"status\"] == \"succeeded\":\n    print(run[\"result\"])"}],"x-unbrowse-capability":"v2ex.replies","x-unbrowse-read-only":true,"x-unbrowse-version":"2026.09.28"}},"/api/v1/sites/v2ex.com/call/v2ex_com__topic":{"post":{"operationId":"v2ex_com__topic","summary":"v2ex.topic","description":"Read a public V2EX topic. Fetch v2ex.replies separately for comments. Inputs: topicId. Read-only on v2ex.com.","tags":["v2ex.com"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Deadline"},{"$ref":"#/components/parameters/EndUser"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"topicId":{"type":"integer","minimum":1,"description":"Positive numeric V2EX topic ID."},"select":{"type":"array","items":{"type":"string"},"description":"Keep only these parts of the result: dots walk keys, [] maps over a list, a final {a,b} keeps several keys (e.g. \"results[].{title,url}\"). Unmatched paths come back in selectMissing."},"deadlineMs":{"type":"integer","minimum":1000,"maximum":120000,"description":"Maximum execution time in milliseconds, between 1000 and 120000."},"idempotencyKey":{"type":"string","minLength":1,"maxLength":256,"description":"Stable retry identity for this action"}},"required":["topicId"],"additionalProperties":false}}}},"responses":{"200":{"description":"The run finished: `status` succeeded carries the site's verified `result`; failed carries `error` (never billed).","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","description":"The run, for unbrowse_inspect / resume / cancel"},"status":{"type":"string","enum":["succeeded","input_required","failed","outcome_unknown","cancelled","working","accepted","expired"],"description":"succeeded carries the site's result; input_required lists what to answer (unbrowse_resume)"},"capabilityId":{"type":["string","null"]},"result":{"type":"object"},"error":{"type":["object","null"]},"requirements":{"type":"array"},"via":{"type":["string","null"],"enum":["http","rendered",null]}},"required":["runId","status"],"additionalProperties":true}}}},"202":{"description":"The run needs input (`status` input_required): answer `requirements` with POST /api/v1/runs/{runId}/responses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"400":{"description":"The body is not JSON, or a value is malformed (invalid_input, bad_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No API key, or an invalid one: send Authorization: Bearer <key>.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Out of verified calls this month (quota_exceeded) or out of paid credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such tool on this site (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The tool is quarantined (tool_quarantined): it failed its checks and is rechecked automatically; the message says what to use now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"An input the tool does not take (unknown_argument): the message lists the ones it does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (by the site or by Unbrowse): retry after the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"run_timeout: the run did not finish within its deadline. It keeps going: poll the given runId. A verified success is billed once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunTimeout"}}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -s 'https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__topic' \\\n  -H \"authorization: Bearer $UNBROWSE_API_KEY\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\"topicId\":1}'"},{"lang":"TypeScript","label":"TypeScript","source":"import { Unbrowse } from \"@unbrowse/sdk\";\n\nconst ub = new Unbrowse(); // reads UNBROWSE_API_KEY\nconst run = await ub.callTool(\"v2ex.com\", \"v2ex_com__topic\", {\n  topicId: 1,\n});\nif (run.status === \"succeeded\") console.log(run.result);"},{"lang":"Python","label":"Python","source":"import os, requests\n\nr = requests.post(\n    \"https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__topic\",\n    headers={\"Authorization\": f\"Bearer {os.environ['UNBROWSE_API_KEY']}\"},\n    json={\n        \"topicId\": 1,\n    },\n    timeout=120,\n)\nr.raise_for_status()\nrun = r.json()\nif run[\"status\"] == \"succeeded\":\n    print(run[\"result\"])"}],"x-unbrowse-capability":"v2ex.topic","x-unbrowse-read-only":true,"x-unbrowse-version":"2026.09.28"}},"/api/v1/sites/v2ex.com/call/v2ex_com__user":{"post":{"operationId":"v2ex_com__user","summary":"v2ex.user","description":"Read a public V2EX user profile. Inputs: username. Read-only on v2ex.com.","tags":["v2ex.com"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Deadline"},{"$ref":"#/components/parameters/EndUser"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"username":{"type":"string","minLength":1,"description":"Exact username to look up on this site."},"select":{"type":"array","items":{"type":"string"},"description":"Keep only these parts of the result: dots walk keys, [] maps over a list, a final {a,b} keeps several keys (e.g. \"results[].{title,url}\"). Unmatched paths come back in selectMissing."},"deadlineMs":{"type":"integer","minimum":1000,"maximum":120000,"description":"Maximum execution time in milliseconds, between 1000 and 120000."},"idempotencyKey":{"type":"string","minLength":1,"maxLength":256,"description":"Stable retry identity for this action"}},"required":["username"],"additionalProperties":false}}}},"responses":{"200":{"description":"The run finished: `status` succeeded carries the site's verified `result`; failed carries `error` (never billed).","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","description":"The run, for unbrowse_inspect / resume / cancel"},"status":{"type":"string","enum":["succeeded","input_required","failed","outcome_unknown","cancelled","working","accepted","expired"],"description":"succeeded carries the site's result; input_required lists what to answer (unbrowse_resume)"},"capabilityId":{"type":["string","null"]},"result":{"type":"object"},"error":{"type":["object","null"]},"requirements":{"type":"array"},"via":{"type":["string","null"],"enum":["http","rendered",null]}},"required":["runId","status"],"additionalProperties":true}}}},"202":{"description":"The run needs input (`status` input_required): answer `requirements` with POST /api/v1/runs/{runId}/responses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"400":{"description":"The body is not JSON, or a value is malformed (invalid_input, bad_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No API key, or an invalid one: send Authorization: Bearer <key>.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Out of verified calls this month (quota_exceeded) or out of paid credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such tool on this site (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The tool is quarantined (tool_quarantined): it failed its checks and is rechecked automatically; the message says what to use now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"An input the tool does not take (unknown_argument): the message lists the ones it does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (by the site or by Unbrowse): retry after the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"run_timeout: the run did not finish within its deadline. It keeps going: poll the given runId. A verified success is billed once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunTimeout"}}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -s 'https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__user' \\\n  -H \"authorization: Bearer $UNBROWSE_API_KEY\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\"username\":\"<username>\"}'"},{"lang":"TypeScript","label":"TypeScript","source":"import { Unbrowse } from \"@unbrowse/sdk\";\n\nconst ub = new Unbrowse(); // reads UNBROWSE_API_KEY\nconst run = await ub.callTool(\"v2ex.com\", \"v2ex_com__user\", {\n  username: \"<username>\",\n});\nif (run.status === \"succeeded\") console.log(run.result);"},{"lang":"Python","label":"Python","source":"import os, requests\n\nr = requests.post(\n    \"https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__user\",\n    headers={\"Authorization\": f\"Bearer {os.environ['UNBROWSE_API_KEY']}\"},\n    json={\n        \"username\": \"<username>\",\n    },\n    timeout=120,\n)\nr.raise_for_status()\nrun = r.json()\nif run[\"status\"] == \"succeeded\":\n    print(run[\"result\"])"}],"x-unbrowse-capability":"v2ex.user","x-unbrowse-read-only":true,"x-unbrowse-version":"2026.09.28"}},"/api/v1/sites/v2ex.com/call/v2ex_com__render_page":{"post":{"operationId":"v2ex_com__render_page","summary":"Search v2ex.com","description":"Search v2ex.com and read the results page (/go/apple). The site builds its results in the browser, so each run renders the page (Unbrowse's own browser, then a hosted renderer when the site blocks it) instead of calling an API: seconds, not milliseconds. Use for requests like “search v2ex.com for apple — Search v2ex.com for apple”. Inputs: query e.g. \"apple\". Returns the page's title, readable text and links. Read-only on v2ex.com.","tags":["v2ex.com"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/Deadline"},{"$ref":"#/components/parameters/EndUser"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"query — what to search for on v2ex.com","examples":["apple"]},"select":{"type":"array","items":{"type":"string"},"description":"Keep only these parts of the result: dots walk keys, [] maps over a list, a final {a,b} keeps several keys (e.g. \"results[].{title,url}\"). Unmatched paths come back in selectMissing."},"deadlineMs":{"type":"integer","minimum":5000,"maximum":300000,"default":60000,"description":"Answer within this many ms (5,000–300,000). Past it: 504 run_timeout with a runId to poll; the run keeps going. The x-unbrowse-deadline-ms header wins."}},"required":["query"],"additionalProperties":false},"example":{"query":"muse"}}}},"responses":{"200":{"description":"The run finished: `status` succeeded carries the site's verified `result`; failed carries `error` (never billed).","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","description":"The run, for unbrowse_inspect / resume / cancel"},"status":{"type":"string","enum":["succeeded","input_required","failed","outcome_unknown","cancelled","working","accepted","expired"],"description":"succeeded carries the site's result; input_required lists what to answer (unbrowse_resume)"},"capabilityId":{"type":["string","null"]},"result":{"type":"object","properties":{"title":{"type":"string"},"text":{"type":"string"},"links":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"href":{"type":"string"}},"required":["text","href"]}}},"required":["title","text","links"]},"error":{"type":["object","null"]},"requirements":{"type":"array"},"via":{"type":["string","null"],"enum":["http","rendered",null]}},"required":["runId","status"],"additionalProperties":true}}}},"202":{"description":"The run needs input (`status` input_required): answer `requirements` with POST /api/v1/runs/{runId}/responses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"400":{"description":"The body is not JSON, or a value is malformed (invalid_input, bad_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No API key, or an invalid one: send Authorization: Bearer <key>.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Out of verified calls this month (quota_exceeded) or out of paid credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such tool on this site (not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The tool is quarantined (tool_quarantined): it failed its checks and is rechecked automatically; the message says what to use now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"An input the tool does not take (unknown_argument): the message lists the ones it does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (by the site or by Unbrowse): retry after the Retry-After header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"run_timeout: the run did not finish within its deadline. It keeps going: poll the given runId. A verified success is billed once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunTimeout"}}}}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -s 'https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__render_page' \\\n  -H \"authorization: Bearer $UNBROWSE_API_KEY\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\"query\":\"muse\"}'"},{"lang":"TypeScript","label":"TypeScript","source":"import { Unbrowse } from \"@unbrowse/sdk\";\n\nconst ub = new Unbrowse(); // reads UNBROWSE_API_KEY\nconst run = await ub.callTool(\"v2ex.com\", \"v2ex_com__render_page\", {\n  query: \"muse\",\n});\nif (run.status === \"succeeded\") console.log(run.result);"},{"lang":"Python","label":"Python","source":"import os, requests\n\nr = requests.post(\n    \"https://unbrowse.ai/api/v1/sites/v2ex.com/call/v2ex_com__render_page\",\n    headers={\"Authorization\": f\"Bearer {os.environ['UNBROWSE_API_KEY']}\"},\n    json={\n        \"query\": \"muse\",\n    },\n    timeout=120,\n)\nr.raise_for_status()\nrun = r.json()\nif run[\"status\"] == \"succeeded\":\n    print(run[\"result\"])"}],"x-unbrowse-capability":"public.v2ex_com.render_page","x-unbrowse-read-only":true,"x-unbrowse-version":"2026.09.22.1"}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"An Unbrowse API key (ub_live_…, from https://unbrowse.ai/app/keys) or an OAuth access token."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":200},"description":"Makes a retry safe: a repeat with the same key returns the same run instead of starting another."},"Deadline":{"name":"x-unbrowse-deadline-ms","in":"header","required":false,"schema":{"type":"integer","minimum":5000,"maximum":300000,"default":60000},"description":"Answer within this many ms; past it 504 run_timeout (the run keeps going). Same as deadlineMs in the body."},"EndUser":{"name":"X-Unbrowse-End-User","in":"header","required":false,"schema":{"type":"string","maxLength":200},"description":"With an org key: run as this end user, in their own workspace with their own logins."}},"schemas":{"Run":{"type":"object","properties":{"runId":{"type":"string","description":"The run, for unbrowse_inspect / resume / cancel"},"status":{"type":"string","enum":["succeeded","input_required","failed","outcome_unknown","cancelled","working","accepted","expired"],"description":"succeeded carries the site's result; input_required lists what to answer (unbrowse_resume)"},"capabilityId":{"type":["string","null"]},"result":{},"error":{"type":["object","null"]},"requirements":{"type":"array"},"via":{"type":["string","null"],"enum":["http","rendered",null]}},"required":["runId","status"],"additionalProperties":true},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, e.g. not_found, unknown_argument, quota_exceeded, run_timeout"},"message":{"type":"string","description":"What happened and what to do, in plain words"}},"additionalProperties":true}},"additionalProperties":true},"RunTimeout":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","runId","poll"],"properties":{"code":{"type":"string","const":"run_timeout"},"message":{"type":"string"},"runId":{"type":"string"},"deadlineMs":{"type":"integer"},"poll":{"type":"string","description":"GET this to read the run's answer when it lands"}},"additionalProperties":true}},"additionalProperties":true}}}}