get402 / docs
get402 docs
Fetch a public URL and return its main content as markdown.
Price
0.005 USDC per successful call on base, paid to 0xB3b5Fb04B2Aef15baf18b121F49fF1DfD100Dd2E over x402. You pay only when you get a 200 with markdown. Every error is free.
How a call works
- Call
GET https://get402.ai/v1/fetch?url=…with no payment. The answer is402with aPAYMENT-REQUIREDheader: base64 JSON, x402 version 2. - Sign one of its
acceptsentries with your wallet and repeat the call with the result in aPAYMENT-SIGNATUREheader. Any stock x402 v2 client does this for you. - The proof is verified, the page is fetched, and settlement happens only if the answer is
200. That200carries aPAYMENT-RESPONSEheader, base64 JSON with the settlement transaction, the same record asmeta.payment. Sending the same proof again returns the same response and does not charge you twice.
Paying from code
Tested against this service with the stock x402 client @x402/fetch 2.28.0 and @x402/evm 2.28.0, set up as its README shows:
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
});
const response = await fetchWithPayment("https://get402.ai/v1/fetch?url=https://example.com/page");
const { data, error, meta } = await response.json();Parameters
| Name | Required | Meaning |
|---|---|---|
url | yes | Absolute http or https URL to fetch. |
fresh | no | 1 or true bypasses the cache and fetches now. Same price. |
max_bytes | no | Cap on the downloaded body size. The server enforces its own maximum regardless. |
timeout_ms | no | Hint. The server enforces its own upper bound. |
Example 402 challenge
The decoded PAYMENT-REQUIRED header this host sends for ?url=https://example.com/page.
Show the challenge
{
"x402Version": 2,
"error": "Payment required",
"resource": {
"url": "https://get402.ai/v1/fetch?url=https://example.com/page",
"description": "Fetch a public URL and return its main content as markdown.",
"mimeType": "application/json"
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "5000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0xB3b5Fb04B2Aef15baf18b121F49fF1DfD100Dd2E",
"maxTimeoutSeconds": 300,
"extra": {
"name": "USD Coin",
"version": "2"
}
}
],
"extensions": {
"bazaar": {
"info": {
"input": {
"type": "http",
"method": "GET",
"queryParams": {
"url": "https://example.com/page"
}
},
"output": {
"type": "json",
"example": {
"method": "GET",
"path": "/v1/fetch",
"params": [
{
"name": "url",
"required": true,
"description": "Absolute http or https URL to fetch."
},
{
"name": "fresh",
"required": false,
"description": "1 or true bypasses the cache and fetches now. Same price."
},
{
"name": "max_bytes",
"required": false,
"description": "Cap on the downloaded body size. The server enforces its own maximum regardless."
},
{
"name": "timeout_ms",
"required": false,
"description": "Hint. The server enforces its own upper bound."
}
],
"price": {
"amount": "0.005",
"asset": "USDC",
"network": "base",
"payTo": "0xB3b5Fb04B2Aef15baf18b121F49fF1DfD100Dd2E"
},
"example_success": {
"data": {
"url": "https://example.com/page",
"final_url": "https://example.com/page",
"title": "Page title",
"description": null,
"content_type": "text/html; charset=utf-8",
"markdown": "# Page title\n\nExample body.",
"word_count": 5,
"truncated": false,
"fetched_at": "2026-09-19T12:00:00.000Z"
},
"error": null,
"meta": {
"request_id": "00000000-0000-4000-8000-000000000001",
"cost_usdc": "0.005",
"cached": false,
"cached_at": null,
"upstream_status": 200,
"payment": {
"network": "base",
"asset": "USDC",
"payer": "0x1111111111111111111111111111111111111111",
"settlement_ref": "0x1111111111111111111111111111111111111111111111111111111111111111"
}
}
},
"example_error": {
"data": null,
"error": {
"code": "blocked_target",
"message": "Private, link-local and metadata addresses are not fetchable."
},
"meta": {
"request_id": "00000000-0000-4000-8000-000000000002",
"cost_usdc": "0",
"cached": false,
"cached_at": null,
"upstream_status": null,
"payment": null
}
},
"errors": [
{
"code": "payment_required",
"status": 402,
"when": "No payment proof was sent.",
"message": "Payment is required."
},
{
"code": "payment_invalid",
"status": 402,
"when": "A proof was sent but verification failed.",
"message": "The payment proof could not be verified."
},
{
"code": "invalid_url",
"status": 400,
"when": "Missing, relative, or malformed url.",
"message": "The url parameter must be an absolute http or https URL."
},
{
"code": "blocked_target",
"status": 400,
"when": "Non-http(s) scheme, a private, link-local, or metadata address, or a host on the deny list, at any redirect hop.",
"message": "Private, link-local and metadata addresses are not fetchable."
},
{
"code": "blocked_by_robots",
"status": 403,
"when": "robots.txt disallows the get402 user agent for the path.",
"message": "The target's robots.txt disallows this user agent for that path."
},
{
"code": "rate_limited",
"status": 429,
"when": "Per-caller, per-target-host, or per-payer limit hit. Retry-After gives the seconds to wait. Never charged.",
"message": "Rate limit exceeded."
},
{
"code": "upstream_failed",
"status": 502,
"when": "DNS, connect, TLS, or read failure. A timeout uses the same code with HTTP 504.",
"message": "The upstream request failed before a response."
},
{
"code": "upstream_error",
"status": 502,
"when": "The target answered with 4xx or 5xx. meta.upstream_status carries that status.",
"message": "The target returned an error."
},
{
"code": "too_large",
"status": 422,
"when": "The hard cap was hit before extraction could succeed.",
"message": "The body exceeded the size cap before enough content was read to extract."
},
{
"code": "unsupported_content",
"status": 422,
"when": "The body is not HTML or plain text.",
"message": "Only HTML and plain text can be extracted."
},
{
"code": "extraction_empty",
"status": 422,
"when": "The fetch succeeded but extraction found no article body.",
"message": "The page had no usable main content."
},
{
"code": "not_found",
"status": 404,
"when": "Unknown request_id on the receipts route.",
"message": "No request exists with that id."
},
{
"code": "unavailable",
"status": 503,
"when": "Health check failed. error.message names the dependency.",
"message": "A dependency is unavailable."
},
{
"code": "internal",
"status": 500,
"when": "Unexpected failure in this service.",
"message": "get402 failed while handling the request."
}
]
}
}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"input": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "http"
},
"method": {
"type": "string",
"enum": [
"GET",
"HEAD",
"DELETE"
]
},
"queryParams": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "Absolute http or https URL to fetch."
},
"fresh": {
"type": "string",
"description": "1 or true bypasses the cache and fetches now. Same price."
},
"max_bytes": {
"type": "string",
"description": "Cap on the downloaded body size. The server enforces its own maximum regardless."
},
"timeout_ms": {
"type": "string",
"description": "Hint. The server enforces its own upper bound."
}
},
"required": [
"url"
]
}
},
"required": [
"type",
"method"
],
"additionalProperties": false
},
"output": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"example": {
"type": "object"
}
},
"required": [
"type"
]
}
},
"required": [
"input"
]
}
}
}
}Example 200
{
"data": {
"url": "https://example.com/page",
"final_url": "https://example.com/page",
"title": "Page title",
"description": null,
"content_type": "text/html; charset=utf-8",
"markdown": "# Page title\n\nExample body.",
"word_count": 5,
"truncated": false,
"fetched_at": "2026-09-19T12:00:00.000Z"
},
"error": null,
"meta": {
"request_id": "00000000-0000-4000-8000-000000000001",
"cost_usdc": "0.005",
"cached": false,
"cached_at": null,
"upstream_status": 200,
"payment": {
"network": "base",
"asset": "USDC",
"payer": "0x1111111111111111111111111111111111111111",
"settlement_ref": "0x1111111111111111111111111111111111111111111111111111111111111111"
}
}
}Example error
{
"data": null,
"error": {
"code": "blocked_target",
"message": "Private, link-local and metadata addresses are not fetchable."
},
"meta": {
"request_id": "00000000-0000-4000-8000-000000000002",
"cost_usdc": "0",
"cached": false,
"cached_at": null,
"upstream_status": null,
"payment": null
}
}Error codes
| Code | HTTP | When |
|---|---|---|
payment_required | 402 | No payment proof was sent. |
payment_invalid | 402 | A proof was sent but verification failed. |
invalid_url | 400 | Missing, relative, or malformed url. |
blocked_target | 400 | Non-http(s) scheme, a private, link-local, or metadata address, or a host on the deny list, at any redirect hop. |
blocked_by_robots | 403 | robots.txt disallows the get402 user agent for the path. |
rate_limited | 429 | Per-caller, per-target-host, or per-payer limit hit. Retry-After gives the seconds to wait. Never charged. |
upstream_failed | 502 | DNS, connect, TLS, or read failure. A timeout uses the same code with HTTP 504. |
upstream_error | 502 | The target answered with 4xx or 5xx. meta.upstream_status carries that status. |
too_large | 422 | The hard cap was hit before extraction could succeed. |
unsupported_content | 422 | The body is not HTML or plain text. |
extraction_empty | 422 | The fetch succeeded but extraction found no article body. |
not_found | 404 | Unknown request_id on the receipts route. |
unavailable | 503 | Health check failed. error.message names the dependency. |
internal | 500 | Unexpected failure in this service. |
Machine-readable
GET /v1— capability index, in the same envelope as every other call./openapi.json— OpenAPI 3.1 withx-payment-info./.well-known/x402— the paid resources on this host.GET /v1/receipts/{request_id}— themetaand outcome of a past call, without the page text.GET /v1/health—200when the payment facilitator and the database both answer; otherwise503unavailable, witherror.messagenaming the one that did not. Check it before paying.
Upstream behavior
The fetcher identifies itself, honors robots.txt, and refuses private addresses. What site owners can expect, and how to opt out, is on the policy page.