Posts API
Superhero posts live on chain; this API is the indexed, queryable copy. Use it to read a feed, load a thread, search post content, or find everything one account has written.
All paths are relative to https://api.superhero.com. Reads need no authentication. Posting is
a contract call, not a REST operation. See Posting.
Endpoints
| Method & path | Query | Returns |
|---|---|---|
GET /api/posts | order_by, order_direction, search, account_address, topics, limit, page | Paginated PostDto |
GET /api/posts/popular | page, limit, eight ranking weights, debug | Paginated PostDto, ranked |
GET /api/posts/{id} | None | One PostDto. {id} accepts a slug too |
GET /api/posts/{id}/comments | order_direction, limit, page | Paginated comment thread |
GET /api/feed | sort, cursor, limit | Cursor-paginated union of posts, token creations and trades |
GET /api/topics | order_by, order_direction, search, limit, page | Paginated topics |
GET /api/topics/name/{name} | None | One topic with its posts |
GET /api/topics/popular/trending | None | Most-used topics by post count |
GET /api/tips/posts/{postId}/summary | None | Total tipped on a post |
GET /api/tips | order_by, order_direction, sender, receiver, type, post_id, limit, page | Paginated tips |
GET /api/tips/accounts/{address}/summary | None | Tips sent and received by an account |
GET /api/posts/analytics | start_date, end_date, sender_address | Daily posting volume series |
GET /api/posts/analytics/top-posters | start_date, end_date, limit | Leaderboard of authors |
GET /api/posts/analytics/top-topics | start_date, end_date, limit | Leaderboard of topics |
PostDto
| Field | Type | Meaning |
|---|---|---|
id | string | Unique post identifier |
tx_hash | string | th_... of the transaction that created the post |
tx_args | array of objects | Raw contract-call arguments as JSON |
sender_address | string | Author ak_... |
sender | PostSenderDto | Inlined author profile: address, public_name, bio, avatarurl |
contract_address | string | ct_... of the posting contract |
type | string | The contract entrypoint that created the post on a single-post read; the literal post on list endpoints |
content | string | The post body |
trend_mentions | array of PostTrendMentionDto | Hashtags in content resolved to tokens |
media | array of strings | Media URLs attached to the post |
total_comments | number | Comment count |
created_at | string | ISO 8601 |
A single-post read also carries slug, updated_at, post_id (the parent, null on a top-level
post), is_hidden, token_mentions and version.
PostTrendMentionDto turns a #HASHTAG in the body into a renderable price chip:
| Field | Type | Meaning |
|---|---|---|
name | string | The trend name, without the # |
sale_address | string or null | Token sale address, when a token exists for that name |
collection_info | CollectionInfoDto or null | Collection metadata for that token |
performance | TrendPerformanceSummaryDto or null | past_24h, past_7d, past_30d, all_time price movement |
type
On a single post, type is the contract entrypoint that created it, or unknown. The posting
contract is one deployment per network (ct_2Hyt9ZxzXra5NAzhePkRsDPDWppoatVD7CtHnUoHVbuehwR8Nb
on mainnet). On the list endpoints type is always the literal post.
Whether a post is a comment, hidden, or generated by a token launch or trade is encoded in the
transaction's second argument, a string array carrying comment:<parentId>, hidden, bcl:
or bcl-tx:. A comment surfaces as a non-null post_id.
GET /api/posts
| Parameter | Notes |
|---|---|
order_by | total_comments or created_at |
order_direction | ASC or DESC |
search | Filters by content or topic |
account_address | Only posts by this ak_... |
topics | Comma-separated topic names, partial matching |
limit, page | Standard pagination, limit capped at 100 |
# Newest 20 posts mentioning a topic
curl -s "https://api.superhero.com/api/posts?topics=superhero&order_by=created_at&order_direction=DESC&limit=20"GET /api/posts/popular
Ranking is all-time with a freshness boost for new posts; window is accepted and ignored.
Eight per-request weights tune it, each low, med (the default) or high:
| Parameter | Signal |
|---|---|
comments | Comment count |
tipsAmountAE | Total AE tipped |
tipsCount | Number of tips |
uniqueTippers | Distinct tipping accounts |
trendingBoost | Whether the post mentions a trending topic |
contentQuality | Server-side content quality signal |
reads | Read count |
interactionsPerHour | Engagement velocity from comments, tips and unique tippers per hour |
Defaults are page=1 and limit=50. debug=1 returns the ranking breakdown alongside the
items. Note the casing: tipsAmountAE has a capital AE; the rest are lowerCamelCase.
# A feed that leans on tips rather than comments
curl -s "https://api.superhero.com/api/posts/popular?limit=20&tipsAmountAE=high&comments=low"GET /api/posts/{id}
One post, by either its id or its slug:
curl -s "https://api.superhero.com/api/posts/hello-world-2f8a91c4"A hidden post is a 404. The path segment must start with a letter or digit and may then contain
letters, digits and . _ ~ : -, up to 128 characters; anything else is a 400.
A slug is the first 60 characters of the content, lowercased, with everything that is not a
letter, digit, ., _, ~ or - collapsed to a single -, then the first
underscore-delimited segment of the post id. Slugs are not guaranteed unique; prefer the id.
GET /api/posts/{id}/comments
Paginated thread under a post. Takes order_direction (ASC or DESC), limit and page;
there is no order_by; comments are ordered by time. Comments are posts, so items follow the
PostDto shape.
GET /api/feed
The combined home feed, and the only cursor-paginated endpoint in the API.
| Parameter | Notes |
|---|---|
sort | latest or hot. Anything else is a 400 |
cursor | Opaque cursor from the previous response's next_cursor |
limit | Defaults to 20, clamped to 1–50 |
sort=latest is a keyset-paginated union of posts, token creations and trades. sort=hot
returns posts only, ranked by the same algorithm as /api/posts/popular. Responses are cached
for 15 seconds.
{
"items": [
{ "type": "post", "created_at": "2026-09-13T09:14:02.000Z", "data": {} }
],
"next_cursor": "..."
}Each item is a discriminated union:
type | data is |
|---|---|
post | A post row, the same shape as a /api/posts item |
token_created | A token row, as on /api/tokens |
trade | A transaction row, as on /api/transactions |
next_cursor is null on the last page; pass it straight back as cursor and do not parse it.
A cursor minted under one sort is ignored by the other, so switching sort restarts from the top.
Topics
A topic is a hashtag with a post count. GET /api/topics/name/{name} returns the topic with its
posts array and post_count. GET /api/topics takes order_by of name, post_count or
created_at, plus order_direction, search, limit and page. GET /api/topics/{id}
accepts either a UUID or a name.
curl -s "https://api.superhero.com/api/topics/popular/trending"See Trending & Topics for the topic row and how a tag maps to a token.
Tips
GET /api/tips/posts/{postId}/summary returns totalTips, an AE total as a decimal string.
Parse it rather than comparing it. It 404s for an unknown post id.
curl -s "https://api.superhero.com/api/tips/posts/123/summary"GET /api/tips lists individual tips, ordered by amount, type or created_at, with filters
for sender, receiver, type and post_id. Combine post_id and receiver to list who
tipped a post:
curl -s "https://api.superhero.com/api/tips?post_id=123&receiver=ak_YOUR_ADDRESS&order_by=created_at&order_direction=DESC&limit=100"| Field | Type | Meaning |
|---|---|---|
tx_hash | string | th_... of the tipping transaction, the primary key |
type | string | profile (the default) or post |
sender_address | string | The tipper. The row also carries a joined sender account object |
receiver_address | string | The recipient, with a joined receiver account object |
post_id | string or null | The tipped post, null for a profile tip |
amount | string | Tip size in AE, so no 1e18 division |
created_at | string | ISO 8601 |
order_by=amount sorts numerically, not lexically.
GET /api/tips/accounts/{address}/summary returns two AE totals as decimal strings, both
excluding self-tips:
{ "totalTipsSent": "12.5", "totalTipsReceived": "3.25" }Posting analytics
Three endpoints under /api/posts/analytics, all taking start_date and end_date as
YYYY-MM-DD. Defaults are the last 10 days. A start after the end, or an unparseable date, is a
400.
GET /api/posts/analytics
A bare array, one element per day, computed live:
| Field | Type | Meaning |
|---|---|---|
date | string | YYYY-MM-DD |
total_posts | number | Top-level posts that day (post_id IS NULL) |
total_comments | number | Comments that day |
total_all | number | Both together |
total_unique_posters | number | Distinct authors that day |
cumulative_total_posts | number | Running total up to and including that day |
avg_comments_per_post | number |
Hidden posts are excluded throughout. sender_address scopes every figure to one author.
GET /api/posts/analytics/past-24-hours returns the same object for a rolling 24-hour window.
GET /api/posts/analytics/top-posters
limit defaults to 10 and is clamped to 1–100. Each item:
sender_address · total_posts · total_comments · total_all · public_nameOrdered by total_all descending. public_name falls back to the address when no profile
exists, so it is never null.
GET /api/posts/analytics/top-topics
Each item is topic_id, topic_name and post_count for the range, ordered by post_count
descending. Same limit handling.
GET /api/posts/analytics/preview is a server-rendered HTML dashboard, not JSON.
Related
- API Conventions: the pagination envelope and the endpoints that differ from it
- Trending & Topics: the tag surface these posts reference
- Profile API: resolving
sender_addressto a display name - Posting: how a post gets on chain
- The feed: what the ranking is trying to achieve