satohub-ai Scan API

Sato Scan: a reading of a payee address, and the marks Sato Scan holds, before money moves. A reading describes what was found and what was not checked; no record on an address is not a statement that it is fine. Never a safety rating.

Operations 6

POST /api/scan/recipient Read a payee address before paying it #
GET /api/scan/address What Sato Scan holds on one address #
GET /api/scan/feed Marks a wallet policy can act on #
GET /api/export/scan-tokens.json Fake-token contracts on record #
GET /api/export/scan-tokens.csv Fake-token contracts as CSV #
GET /api/export/scan-tokens.ndjson Fake-token contracts as NDJSON #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/satohub-ai:satohub-ai-scan-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

satohub-ai-scan-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Sato Hub public Scan API
  version: 1.1.0
  summary: Read access to the onchain agent library, its change feed, and the Agent Passport registry.
  description: 'Public reads need no key and no account; the projects and listings routes take a bearer key. A Sato API key is optional on every metered endpoint and only raises limits. What is free, what is paid (the plans are Sato Check Team and Platform & Data), every limit and the fee schedule are listed on the pricing page, and the same sheet is machine-readable at /api/pricing.json.


    Pricing: https://satohub.ai/pricing


    License: Catalog data CC-BY-4.0 — attribution: data by satohub.ai. Scores/liveness are informational signals, not safety or quality guarantees.


    Scores and liveness are informational signals computed from public evidence. They are not safety ratings, audits, or claims about returns. `verification_status` distinguishes self-reported from independently checked; self-reported is never presented as verified.


    Every shape this API returns is also published as a versioned JSON Schema (draft 2020-12) at https://satohub.ai/schemas — catalogue at https://satohub.ai/schemas/index.json. They are referenced from components.schemas below, and v1 is additive-only: a breaking change becomes v2 at a new URL. Validate with additional properties allowed.


    The same data is available to agents over MCP (Streamable HTTP, JSON-RPC) at https://satohub.ai/api/mcp — documented at https://satohub.ai/mcp. That endpoint is not described here because it is not REST.


    INTERPRETED ANSWERS. A route template sent literally (`{slug}`), an empty query or body, a parameter under another name (`?package=` for `?target=`) or another spelling of a chain (`8453`, `eth`, `bsc`) is answered with the closest honest thing instead of a bare 4xx, and always LABELLED: the body carries an `interpretation` object (`interpreted_as`, `requested`, `rule`, `note`; the same four keys are repeated at the top level when they cannot collide with the record''s own fields), and the response carries the header `X-Sato-Interpreted: ` (plus `Content-Location` when there is a canonical URL). An interpreted answer is never an exact match. Nothing that moves money is ever interpreted beyond a chain spelling: no amount, token, taker, recipient, deployer or slippage is defaulted, a missing chain is never assumed, a chain is never guessed from an address, and a generic network word (`mainnet`, `testnet`) is never read as a chain (the answer lists the candidates). Writes are never interpreted, and a receipt is never served for a template.'
  contact:
    name: Sato Hub
    url: https://satohub.ai
  x-guidance: 'Free: search and read the onchain-agent catalog (/api/export/index.json, /api/changes, /api/resolve) and use the MCP server at /api/mcp. Paid over x402 (USDC on Base, no account or key): GET /api/x402/history/{slug} for one listing''s daily Sato Score record, GET /api/x402/agent-economy for the weekly agent-economy series. Each paid read is priced per unit delivered (daily points or rows returned): the 402 states the exact amount, the unit, the quantity and the formula, which is also on /pricing. The Sato Score measures how open, active and verifiable a project is; it is not a safety or returns grade.'
  x-status:
    page: https://satohub.ai/status/sato-hub
    json: https://satohub.ai/api/status.json
    feed: https://satohub.ai/status/feed.xml
    note: Success ratios and latencies of Sato Hub's own hourly checks. Not a service-level agreement.
  license:
    name: CC-BY-4.0
    url: https://creativecommons.org/licenses/by/4.0/
servers:
- url: https://satohub.ai
security: []
tags:
- name: Scan
  description: 'Sato Scan: a reading of a payee address, and the marks Sato Scan holds, before money moves. A reading describes what was found and what was not checked; no record on an address is not a statement that it is fine. Never a safety rating.'
paths:
  /api/scan/recipient:
    post:
      tags:
      - Scan
      operationId: scanRecipient
      summary: Read a payee address before paying it
      description: 'Takes a chain and a payee address (`to`), optionally the token, an amount in base units, the sender and the https origin the payment terms came from, and returns one reading: `go`, `caution`, `no` or `unknown`, the rule that produced it (S1 to S7), every evidence line with its date, and the limits of what was read. The rules look for a token contract that claims a well-known symbol without being the canonical one, an address that copies the start and end of one the sender has really paid, a payee that differs from the one the origin declared, and marks Sato Scan holds. `unknown` means Sato Scan holds no record for this payee — it is not a statement that the payee is fine, and `go` is never a safety rating: it says that nothing Sato Scan looks for was found and that a canonical token and a declared payee agreed. Nothing is signed, held or moved. Body cap 4 KB; 60 requests per 10 minutes per caller. Only the chain is recorded in telemetry.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - chain
              - to
              properties:
                chain:
                  type: string
                  description: 'A chain Sato Scan reads, case-insensitive: Base, Solana, Tempo, Polygon, BNB Chain, Arbitrum, Avalanche, Optimism, Ethereum, SKALE Base, Sei, X Layer, Monad, Robinhood Chain, World Chain, Abstract. Lightning has no public ledger and is refused.'
                to:
                  type: string
                  pattern: ^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{32,44})$
                  description: 'The payee: 0x + 40 hex on EVM chains, base58 of 32 to 44 characters on Solana.'
                token:
                  type: string
                  pattern: ^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{32,44})$
                  description: The token contract being paid, in the same address form.
                amount:
                  type: string
                  pattern: ^[0-9]{1,78}$
                  maxLength: 78
                  description: Non-negative integer in base units, at most 78 digits.
                from:
                  type: string
                  pattern: ^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{32,44})$
                  description: The sender, in the same address form. Its own payment history is used to find look-alikes, and the reading may say how many poison transfers were aimed at it in the last 30 days (a count and reviewed campaign ids only, never a transaction or an address).
                origin:
                  type: string
                  format: uri
                  maxLength: 2048
                  description: An https URL, at most 2048 characters, with no credentials and no host on a private network, whose declared payment terms name the payee.
      responses:
        '200':
          description: A reading. Signed; the Link header names the scan-recipient schema.
          headers:
            Link:
              schema:
                type: string
              description: <https://satohub.ai/schemas/scan-recipient/v1.json>; rel="describedby"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/scan-recipient'
        '400':
          description: The body was not JSON, or a field failed validation. The error names the field.
        '413':
          description: The body is larger than 4 KB.
        '429':
          description: Too many requests from this address.
  /api/scan/address:
    get:
      tags:
      - Scan
      operationId: scanAddress
      summary: What Sato Scan holds on one address
      description: 'What the address itself did, as Sato Scan holds it: the marks on it (a fake-token mark is public on observation; sender and dispatcher marks once a person has reviewed them), the reviewed campaigns it appears in as a dispatcher, sender or fake token, and the identities it declared (an Agent Passport), with the first and last sighting of those marks. It never reports what was done TO an address: transfers aimed at a wallet, campaigns that targeted it and whether it is watched are not part of this reading, and `watched` is always null. An empty reading means no public record: it does not say the address is fine. A declared identity is a claim the address''s owner made, not a verification. Signed; 120 requests per 10 minutes per caller.'
      parameters:
      - name: chain
        in: query
        required: false
        schema:
          type: string
        description: 'A chain Sato Scan reads, case-insensitive: Base, Solana, Tempo, Polygon, BNB Chain, Arbitrum, Avalanche, Optimism, Ethereum, SKALE Base, Sei, X Layer, Monad, Robinhood Chain, World Chain, Abstract. Lightning has no public ledger and is refused. Also read: the chain id, CAIP-2 (eip155:8453) and per-chain short names (eth, bsc, matic, arb), echoed with `interpretation`. A generic network word (mainnet, testnet) is refused with candidates. Never guessed from the address.'
      - name: address
        in: query
        required: false
        schema:
          type: string
          pattern: ^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{32,44})$
        description: 0x + 40 hex on EVM chains, base58 of 32 to 44 characters on Solana. With neither chain nor address the answer is a 200 needs_input.
      responses:
        '200':
          description: An address reading (signed; the Link header names the scan-address schema), OR a `needs_input` help body when nothing was sent (no Link, no reading).
          headers:
            Link:
              schema:
                type: string
              description: <https://satohub.ai/schemas/scan-address/v1.json>; rel="describedby"
          content:
            application/json:
              schema:
                oneOf:
                - type: object
                - $ref: '#/components/schemas/needs-input'
        '400':
          description: 'The address is malformed, the chain is not one Sato Scan reads ("chain ''X'' not recognised; accepted: …"), a generic network word was sent (candidates listed), or an address came with no chain (per-chain candidate calls; never guessed).'
        '429':
          description: Too many requests from this address.
  /api/scan/feed:
    get:
      tags:
      - Scan
      operationId: scanFeed
      summary: Marks a wallet policy can act on
      description: Reviewed marks plus every fake-token contract Sato Scan holds, as `json`, or shaped for a wallet provider's policy (`privy` condition-set items, `cdp` evmAddress `not in` criteria; EVM only). The privy and cdp shapes follow those vendors' documentation as of 2026-09 and have not been exercised against either service — check them against the vendor's current docs before relying on them. An entry is an observation to consider blocking; an address absent from the feed is not thereby known to be fine. Watched wallets are never included.
      parameters:
      - name: chain
        in: query
        required: false
        schema:
          type: string
        description: 'A chain Sato Scan reads, case-insensitive: Base, Solana, Tempo, Polygon, BNB Chain, Arbitrum, Avalanche, Optimism, Ethereum, SKALE Base, Sei, X Layer, Monad, Robinhood Chain, World Chain, Abstract. Lightning has no public ledger and is refused. Omit for every chain.'
      - name: since
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Only marks last seen at or after this time. An ISO 8601 date or timestamp.
      - name: format
        in: query
        required: false
        schema:
          type: string
          enum:
          - json
          - privy
          - cdp
          default: json
      responses:
        '200':
          description: The feed.
          content:
            application/json:
              schema:
                type: object
        '400':
          description: format, chain or since is not valid.
        '429':
          description: 'Too many requests from this address: 120 requests per 10 minutes per caller, with a `Retry-After` header.'
        '503':
          description: 'The feed could not be read within its 20 second budget, or a part of it (the fake-token marks or the reviewed marks of a chain) could not be read. It is never answered as an empty or short list, and it is not cached: keep the list you hold and retry after the `Retry-After` header.'
  /api/export/scan-tokens.json:
    get:
      tags:
      - Scan
      operationId: getScanTokensJson
      summary: Fake-token contracts on record
      description: 'Contracts whose token label reads like a well-known token while the address is not that token''s canonical one: chain, contract, the label spelled out in code points, first and last sighting, one evidence transaction, and whether a person has reviewed the mark. It describes an observation; it is not a list of every such contract and not a statement about who deployed one.'
      parameters:
      - name: chain
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: Marks.
          content:
            application/json:
              schema:
                type: object
                properties:
                  schema_version:
                    type: string
                  license:
                    type: string
                  count:
                    type: integer
                  tokens:
                    type: array
                    items:
                      type: object
  /api/export/scan-tokens.csv:
    get:
      tags:
      - Scan
      operationId: getScanTokensCsv
      summary: Fake-token contracts as CSV
      parameters:
      - name: chain
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: CSV.
          content:
            text/csv:
              schema:
                type: string
  /api/export/scan-tokens.ndjson:
    get:
      tags:
      - Scan
      operationId: getScanTokensNdjson
      summary: Fake-token contracts as NDJSON
      parameters:
      - name: chain
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: NDJSON.
          content:
            application/x-ndjson:
              schema:
                type: string
components:
  schemas:
    scan-recipient:
      type: object
      description: 'JSON Schema: https://satohub.ai/schemas/scan-recipient/v1.json'
      externalDocs:
        url: https://satohub.ai/schemas/scan-recipient/v1.json
    needs-input:
      type: object
      description: 'The 200 answer to a call that sent nothing usable: the accepted forms and exact example calls. It carries NO verdict, NO profile and NO pick. Header `X-Sato-Interpreted: needs-input`.'
      required:
      - status
      - needs_input
      - interpretation
      - accepted
      - examples
      properties:
        status:
          type: string
          enum:
          - needs_input
        needs_input:
          type: boolean
          enum:
          - true
        interpretation:
          $ref: '#/components/schemas/interpretation'
        endpoint:
          type: string
          format: uri
        accepted:
          type:
          - object
          - array
        examples:
          type: array
        docs:
          type: string
          format: uri
      additionalProperties: true
    interpretation:
      type: object
      description: 'How the request was read. Present only on an interpreted answer; the response also carries `X-Sato-Interpreted: <rule>`.'
      required:
      - interpreted_as
      - requested
      - rule
      - note
      properties:
        interpreted_as:
          type: string
          description: 'What was served: a URL, a canonical value, `needs_input`, `supported` or `candidates`.'
        requested:
          type: string
          description: What was sent, decoded and cut to 200 characters.
        rule:
          type: string
          enum:
          - template-placeholder
          - near-miss
          - alias
          - needs-input
          - example-goal
          - param-alias
          - body-alias
          - bare-body
          - chain-spelling
          - candidates
        note:
          type: string
      additionalProperties: true
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'Two different credentials ride this header, and they are told apart by their shape.


        • A SATO API KEY, `sk_sato_…` — optional on every metered endpoint. It is NEVER required: each of those endpoints answers an anonymous caller at the free limits, and a key only raises the caps and attributes the calls to an account. A key may also be sent as `X-Sato-Key`. An unknown or revoked key is served as if no key were present, never refused. Limits and plans: /pricing.


        • A PRIVY ACCESS TOKEN for a signed-in account — required by the claim endpoints and the key-management endpoints. A token that is not a Sato key is treated as one of these.'
    satoKey:
      type: apiKey
      in: header
      name: X-Sato-Key
      description: 'The same optional Sato API key, on its own header. Equivalent to `Authorization: Bearer sk_sato_…`.'
externalDocs:
  description: The published Sato Hub formats, as JSON Schema.
  url: https://satohub.ai/schemas