API Reference
Profile

Profile API

The write endpoints on api.superhero.com take no password, API key or session cookie. Every write is authorised by a wallet signature over a server-issued challenge: request a challenge, sign it with the key that owns the address, post the signature back.

All paths are relative to https://api.superhero.com.

Reading a profile

Method & pathQueryReturns
GET /api/profile/{address}NoneOne profile aggregate
GET /api/profileaddressesA bare array of profile aggregates
GET /api/profile/feedlimit, offsetA page of cached profiles with its own envelope

GET /api/accounts/{address} is the primary read for a linked X handle, bio, website and preferred .chain name, and its links block is authoritative. GET /api/profile/{address} fills in the remaining display fields. See Accounts.

curl -s "https://api.superhero.com/api/profile/ak_2ruXgsLy9jMwEqsgyQgEsxw8chYDfv2QyBfCsR6qtpQYkektWB"
FieldTypeMeaning
addressstringThe ak_...
public_namestringResolved display name. Never null: it falls back to the address
profile.fullnamestringDisplay name from the legacy profile cache. "" when unset
profile.biostringThe verified links.bio if present, else the cached bio, else ""
profile.sitestring or nullThe verified links.site
profile.avatarurlstring"" when unset
profile.usernamestring or nullLegacy custom username
profile.prefered_aens_namestring or nullThe verified links.prefaens. Note the one-r spelling
profile.x_usernamestring or nullThe verified links.x
profile.chain_namestring or nullThe account's chain_name first, then the cached copy
profile.chain_expires_atstring or nullWhen the .chain name expires
profile.followers_count / following_countnumberSingle-profile read only, and only when the social-graph feature is enabled

The display name resolves as:

public_name = prefered_aens_name || chain_name || username || address

An X handle never becomes the display name. fullname, avatarurl and username come from a legacy profile cache; the account's chain_name is refreshed hourly and is the live one.

includeOnChain is accepted on both read endpoints and ignored; profiles are served from indexed data.

The batch form

GET /api/profile?addresses=ak_a,ak_b returns a plain JSON array with no items wrapper: one element per unique address, in the order supplied, after trimming and de-duplication. Addresses with no cached profile still get an element with the empty-string defaults above.

RuleBehaviour
More than 100 addresses400 addresses must contain at most 100 entries
Any malformed address400 addresses contains invalid account address ak_..., and the whole call fails
Empty or missing addresses[]
curl -s "https://api.superhero.com/api/profile?addresses=ak_ONE,ak_TWO"

GET /api/profile/feed returns { items, pagination }, where pagination carries limit, offset and count. limit defaults to 20 and offset to 0.

The write pattern

Every write endpoint is one of two shapes:

claim   ->  server returns { message, nonce, value, verification_token }
sign    ->  wallet signs `message`
submit  ->  server verifies, returns { txHash }

or, for the reward and sponsorship flows:

challenge ->  server returns { nonce, expires_at, message }
sign      ->  wallet signs `message`
submit    ->  server verifies, performs the action

The nonce is single-use and the challenge expires.

Address links

An address link is a verified statement that an ak_... owns something off-chain: an X handle, a bio, a website, a preferred .chain name, or a Nostr key. Five families, each with the same four endpoints.

LinkBase path
X handle/api/address-links/x
Bio/api/address-links/bio
Website/api/address-links/site
Preferred .chain name/api/address-links/prefered-aens-name
Nostr key/api/address-links/nostr

The path is spelled prefered-aens-name, with one r. Each base path has POST {base}/claim, POST {base}/submit, POST {base}/unclaim and POST {base}/unclaim/submit.

Claim

For bio, site, preferred name and Nostr, the body is the address plus the value you are claiming:

curl -s -X POST "https://api.superhero.com/api/address-links/bio/claim" \
  -H "Content-Type: application/json" \
  -d '{"address":"ak_YOUR_ADDRESS","value":"Building things on aeternity."}'
FieldTypeMeaning
messagestringThe exact text the wallet must sign
noncenumberThe address's current nonce on the AddressLink contract, echoed back on submit
valuestringThe normalised value the server will record
verification_tokenstringServer-side proof that this claim was issued. X only

nonce is the AddressLink contract's get_nonce counter for the address; it advances after a successful submit, and sending it as a string ("42") is a 400. The chain-name and X-reward flows use a different, server-issued string nonce.

The message the wallet signs is deterministic:

link:<address>:<provider>:<value>:<nonce>
unlink:<address>:<provider>:<nonce>

<provider> is x, bio, site, prefered-aens-name or nostr. A signature over anything else fails contract-side verification.

Submit

Sign message with the address's key, then post everything back:

curl -s -X POST "https://api.superhero.com/api/address-links/bio/submit" \
  -H "Content-Type: application/json" \
  -d '{"address":"ak_YOUR_ADDRESS","value":"Building things on aeternity.",
       "nonce":42,"signature":"sg_...","verification_token":"..."}'

Response is { "txHash": "th_..." }. The server broadcasts the on-chain write on your behalf. The API sponsors the transaction; your signature authorises it. value is capped at 200 characters, signature is a hex-encoded wallet signature, and address is validated as an ak_ account address.

Unclaim

POST {base}/unclaim with { "address": "ak_..." } returns { message, nonce }. Sign message, then POST {base}/unclaim/submit with { address, nonce, signature } and receive a txHash.

Linking X

The X value comes from an OAuth round trip rather than being typed. Two accepted claim bodies:

{ "address": "ak_...", "x_access_token": "..." }
{ "address": "ak_...", "x_code": "...", "x_code_verifier": "...", "x_redirect_uri": "https://superhero.com/profile/x/callback" }

The second is the PKCE form: redirect the user to X, receive the code at your redirect URI, and post the code, verifier and redirect URI; the backend exchanges the code itself. From there the flow is identical: claim returns message plus verification_token, the wallet signs it, submit returns a txHash.

POST /api/address-links/x/claim            { address, x_access_token }  or  { address, x_code, x_code_verifier, x_redirect_uri }
POST /api/address-links/x/submit           { address, value, nonce, signature, verification_token }
POST /api/address-links/x/unclaim          { address }
POST /api/address-links/x/unclaim/submit   { address, nonce, signature }

Read the result back from GET /api/accounts/{address} as links.x, falling back to profile.x_username and then the legacy top-level x_username.

Sponsored .chain name registration

Registering an AENS name normally costs AE. Superhero funds it from a sponsor account when the name qualifies.

Method & pathPurpose
GET /api/profile/chain-name/sponsorship/{name}Can the sponsor fund this name?
POST /api/profile/chain-name/challengeGet a challenge for address
POST /api/profile/chain-name/claimSubmit the signed request
GET /api/profile/{address}/chain-name-claimPoll the claim's status

{name} is the label without the .chain suffix. AENS rules apply, and a sponsored claim requires a minimum of 13 characters.

curl -s "https://api.superhero.com/api/profile/chain-name/sponsorship/mylongenoughname"
FieldTypeMeaning
namestringThe name checked
sponsorablebooleanTrue when the sponsor can fund it, making the claim free for the user
sponsor_configuredbooleanWhether a sponsor account is configured at all
sponsor_balance_aettosstring or nullSponsor balance, in aettos
required_balance_aettosstringTotal estimated on-chain cost (preclaim, claim, update and transfer), in aettos
reasonstring or nullWhy it is not sponsorable, when it is not

The claim body is address, name, challenge_nonce, challenge_expires_at and signature_hex (hex or an sg_ string). It returns { status, message }.

Registration is asynchronous and takes four on-chain transactions. Poll GET /api/profile/{address}/chain-name-claim, which returns status plus, as they happen, preclaim_tx_hash, claim_tx_hash, update_tx_hash, transfer_tx_hash, an error, and the name's expiry under expires_at (older responses use approximate_expire_time, approximateExpireTime, expire_time or expireTime).

X posting reward

All four routes carry the per-IP rate limit: 10 requests per minute per route, returning 429 with a retryAfter field. See Conventions.

Method & pathPurpose
GET /api/profile/{address}/x-posting-rewardRead status. No signature
POST /api/profile/x-posting-reward/recheck-challengeGet a challenge for address
POST /api/profile/{address}/x-posting-reward/recheckRun the capped scan
POST /api/profile/{address}/x-reward/referral-linkGet a referral link
curl -s "https://api.superhero.com/api/profile/ak_YOUR_ADDRESS/x-posting-reward"

The status payload carries onboarding_status (not_started, pending, paid or failed), qualified_posts_count, tx_hash, next_check_allowed_at, x_username, per_post_total_paid_count, current_streak_days, tier_amount_ae, follower_count, referral_link, and an error string explaining why a reward was not sent on a 200 (below the follower minimum, the X identity already rewarded, or the payout failed).

The recheck and referral-link endpoints take the same signed proof body: challenge_nonce, challenge_expires_at, signature_hex. The recheck is capped at once per 24 hours; exceeding it returns 429 with nextAllowedAt in the body, and the scan is skipped, not queued.

Deprecated: the legacy profile update

Two endpoints implement an older generic profile edit. Use the address-link endpoints instead.

Method & pathBodyReturns
POST /api/profile/{address}/challengeThe editable payloadchallenge, payload_hash, expires_at, ttl_seconds
PATCH /api/profile/{address}The payload plus challenge and signatureThe updated profile row

The editable payload is fullname, bio, nostrkey, avatarurl, username, x_username, chain_name, sol_name; the response adds address, created_at and updated_at.

Related

  • API Conventions: the full list of challenge/submit pairs across the API
  • Accounts API: GET /api/accounts/{address} and the links precedence rule
  • Posts API: PostDto.sender, the inlined profile on every post
  • Profiles: what a Superhero profile is, beyond its wire format
  • Chain names: what a .chain name buys you
  • X verification: the OAuth half of the X link flow