> ## Documentation Index
> Fetch the complete documentation index at: https://docs.walletlink.social/llms.txt
> Use this file to discover all available pages before exploring further.

# Introduction

> Base URL, authentication, plans, and the conventions every endpoint follows.

## Base URL

```
https://walletlink.social/api/v1
```

All endpoints are HTTPS only and return JSON. CORS is open, but see the warning under [Authentication](#authentication) before you call this from a browser.

## Discovery

If you are writing a client that should find this API rather than be told about
it, start at the API catalog:

```
https://walletlink.social/.well-known/api-catalog
```

It is an [RFC 9727](https://www.rfc-editor.org/rfc/rfc9727) catalog served as
`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 the `Authorization` header. Both forms work:

```bash theme={null}
Authorization: Bearer wts_live_xxxxxxxx
Authorization: wts_live_xxxxxxxx
```

<Warning>
  Call the API from your server, never from client-side code. CORS is open so a
  browser request will succeed, which makes this an easy mistake to make. Any
  key shipped to a browser is public, and it carries your entire credit balance.
</Warning>

Keys are prefixed `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:

|                            | **Trial, Campaign, Agent** | **Scale** | **Index**    |
| -------------------------- | -------------------------- | --------- | ------------ |
| Plan name in API responses | `Developer`                | `Startup` | `Enterprise` |
| Requests per minute        | 60                         | 300       | 1,000        |
| Requests per day           | 5,000                      | 50,000    | unlimited    |
| Requests per month         | 50,000                     | 500,000   | unlimited    |
| Max batch size             | 50                         | 200       | 1,000        |

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`](/api-reference/stats),
[`/v1/usage`](/api-reference/usage),
[`/v1/estimate`](/api-reference/estimate) and the
[job status poll](/api-reference/jobs#polling), keep answering at zero balance.

<Note>
  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`](/api-reference/usage) report the preset currently serving you
  under `key.plan`.
</Note>

Higher limits are available. Email [help@walletlink.social](mailto:help@walletlink.social).

## 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.

| Endpoint                               | Match credits                                                                                   |
| -------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `GET /v1/wallet/{address}`             | 1 if the address resolves to an X handle or a Farcaster account, 0 if it does not               |
| `POST /v1/batch`                       | 1 per address that resolves to an X handle or a Farcaster account, after duplicates are removed |
| `POST /v1/jobs`                        | 1 per address that resolves, debited once when the job completes; nothing at submission         |
| `GET /v1/jobs/{id}`                    | 0                                                                                               |
| `POST /v1/estimate`                    | 0, at any balance                                                                               |
| `GET /v1/reverse/twitter/{handle}`     | 1 per wallet returned, up to 100                                                                |
| `GET /v1/reverse/farcaster/{username}` | 1 per wallet returned, up to 100                                                                |
| `GET /v1/stats`                        | 0                                                                                               |
| `GET /v1/usage`                        | 0                                                                                               |

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](/api-reference/jobs) 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.

| Endpoint                               | Rate-limit units        |
| -------------------------------------- | ----------------------- |
| `GET /v1/wallet/{address}`             | 1                       |
| `POST /v1/batch`                       | 1 per address submitted |
| `POST /v1/jobs`                        | 1 per submission        |
| `GET /v1/jobs/{id}`                    | 0                       |
| `POST /v1/estimate`                    | 1 per address submitted |
| `GET /v1/reverse/twitter/{handle}`     | 2                       |
| `GET /v1/reverse/farcaster/{username}` | 2                       |
| `GET /v1/stats`                        | 0                       |
| `GET /v1/usage`                        | 0                       |

Reverse lookups weigh double here because they scan the index by identity rather
than by key. A [job](/api-reference/jobs) 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](/api-reference/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 with `data` and `meta`.

```json theme={null}
{ "data": { ... }, "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.
* `OPTIONS` is supported on every endpoint for CORS preflight.
