Base URL
Discovery
If you are writing a client that should find this API rather than be told about it, start at the API catalog:application/linkset+json, and it lists all three APIs this product exposes:
this REST API, the MCP server, and the x402 credit rail. Each entry carries the
OpenAPI description, the documentation page, and for the MCP server its OAuth
protected resource metadata. Every page on walletlink.social also links to it
with rel="api-catalog", so an origin is enough to find everything else.
Authentication
Pass your key in theAuthorization header. Both forms work:
wts_live_ and are stored only as hashes. A lost key is replaced, not recovered.
Plans
API access is included with every credit pack rather than sold separately, and it draws on the same balance the app does. There is no API-only plan. Which rate-limit preset serves your requests is decided by the largest pack the account holds that has not yet expired, for the twelve months the pack lives, spent down or not:
These are rate limits: they bound how fast you can call, not how much you can
resolve in total. What you can resolve is bounded by your credit balance at
every rung, and a metered call made with no credits left returns
402 with
code NO_CREDITS. The free endpoints, /v1/stats,
/v1/usage,
/v1/estimate and the
job status poll, keep answering at zero balance.
The ladder follows the pack, not the key. Buying Scale raises your existing
key’s limits on the next request; when the pack expires, twelve months after
purchase, the limits return to
Developer with no key change. Responses from
/v1/usage report the preset currently serving you
under key.plan.Credits
Two meters, and they are not the same
The API is measured twice, and the numbers differ. Keeping them apart is the only way the cost of a call makes sense. Match credits are what you bought. One credit is one wallet resolved to an X handle or a Farcaster account. A wallet that resolves to nothing costs nothing, which is the whole pricing position and applies to the API exactly as it does in the app.
A record that carries only an ENS name, a Lens profile or a GitHub account is
returned but not billed: a match means X or Farcaster.
A metered call made with no match credits left returns
402 with code
NO_CREDITS. The zero-credit endpoints answer at any balance, so a drained
key can always read its own meter, and can collect the results of a
job it already paid for.
Rate-limit units are separate, are not bought, and reset on their own
schedule. They bound how fast you may call, never how much you may resolve.
Reverse lookups weigh double here because they scan the index by identity rather
than by key. A job submission weighs one unit however
long its list, because the request only queues the work; the work itself is
billed as matches when the job completes. An
estimate weighs exactly like the batch it previews,
one unit per address, so it costs the same throughput and none of the credits.
Deduplicating your list still helps, but not for the reason it used to: a
duplicate is billed once in match credits, and counts twice against your rate
limit.
Response shape
Every successful response is an object withdata and meta.
data holds the result and is null or [] when there is nothing to return. meta holds everything about the request itself: what you asked for, how much came back, whether it was truncated.
Two conventions are worth internalising:
Absent fields are omitted, not nulled. A wallet with no GitHub account has no github key at all. Check for presence.
Not found is not an error. A wallet we have no identities for returns HTTP 200 with data: null and meta.found: false. Error status codes are reserved for requests that actually failed.
Conventions
- Wallet addresses are accepted in any case and returned lowercased.
- X handles are accepted with or without a leading
@and returned without it. - All timestamps are ISO 8601 in UTC.
OPTIONSis supported on every endpoint for CORS preflight.