API Reference
Trending & Topics

Trending & Topics API

Trending is the entry point for most agent strategies: find what is moving, then find whether a token exists for it. Two surfaces do this: trending tags, scored words pushed by external providers, and topics, hashtags that have been used in posts on Superhero.

All paths are relative to https://api.superhero.com. Reads need no authentication.

Endpoints

Method & pathQueryReturns
GET /api/trending-tagsorder_by, order_direction, search, limit, pageScored tags, each with its matching token joined in
GET /api/trending-tags/{tag}NoneOne tag, without the token join
POST /api/trending-tagsNoneIngest tags from a provider. Requires an API key
GET /api/topicsorder_by, order_direction, search, limit, pagePaginated topics
GET /api/topics/{id}NoneOne topic, by UUID or name
GET /api/topics/name/{name}NoneOne topic, by name
GET /api/topics/popular/trendingNoneMost-used topics by post count
GET /api/tokens with order_by=trending_scoreNoneTokens ranked by trending score
GET /api/tokens/{address}/scoreNoneA token's full trending-score breakdown
GET /api/tokens/{address}/trending-eligibilityNoneWhether a token qualifies for the trending list
GET /api/searchq, limitTokens, accounts and posts in one call

GET /api/trending-tags

ParameterNotes
order_byscore (default), source or created_at. Anything else is a 400
order_directionASC or DESC, default DESC. Anything else is a 400
searchCase-insensitive substring match on the tag. Rejected above 100 characters
limit, pagelimit defaults to 100 and must be 1–100; outside that range it is a 400
curl -s "https://api.superhero.com/api/trending-tags?order_by=score&order_direction=DESC&limit=20"
FieldTypeMeaning
idnumberRow id
tagstringThe normalised tag, unique
descriptionstring or nullCurrently always null
scorenumberThe provider's score, as sent
sourcestringThe provider string from the ingest call
created_atstringISO 8601
tokenTokenDto or nullList endpoint only. The token whose name equals tag; absent or null when there is none

Take token.sale_address off the nested object to reach the token.

GET /api/trending-tags/{tag} returns the bare row for one tag, with no token join. It normalises the path parameter the same way ingest does, so /superHero finds SUPER-HERO, and 404s with Trending tag not found when there is no row.

score and source

Tag scores are supplied by the provider that posts them, with no normalisation, cap or rescaling and no documented range. Each ingest replaces the whole table, so every score in a response comes from one provider on one scale: treat score as a ranking key within a response, not a quantity to compare across time. source is the provider string as sent.

Normalisation

Tags are normalised on ingest and on lookup:

camelCase -> kebab-case   (\p{Ll})(\p{Lu})  ->  $1-$2
uppercase
drop everything that is not a letter, a number or "-"   (any script)
collapse runs of "-" and trim leading/trailing "-"

So CamelCase becomes CAMEL-CASE, dog plus an emoji becomes DOG, привет becomes ПРИВЕТ, and !!! becomes the empty string, which ingest skips with an error entry. Non-Latin scripts survive so a tag matches token names in the CHINESE, ARABIC and RUSSIAN collections (see Collections and naming).

POST /api/trending-tags

Requires the API key, sent as x-api-key or Authorization: Bearer. A missing or wrong key is a 401.

curl -s -X POST "https://api.superhero.com/api/trending-tags" \
  -H "x-api-key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"provider":"x","items":[{"tag":"SuperHero","score":"95.5"}]}'

provider is any non-empty string. A call with a non-empty items array replaces the entire table. The response is a summary, not the stored rows:

{
  "message": "Trending tags processing completed",
  "results": { "created": 18, "updated": 0, "total_processed": 20, "errors": ["Tag \"!!!\" resulted in empty normalized tag"] }
}

From a tag to a token

  1. Read the nested token off the tag row. The list endpoint joins it on token.name = trending_tag.tag.
  2. Look the name up directly. GET /api/tokens/{name} resolves a token by name and 404s when none exists.
  3. Search. GET /api/tokens?search={tag} matches on name, for partial cases.

Posts go the other way for free: every PostDto carries trend_mentions, resolving each #hashtag to a sale_address, collection_info and performance summary. See Posts.

The trending-score formula

The tag score and a token's trending_score are different numbers. TokenDto.trending_score is computed from Superhero's own data with the formula below, and is the default sort for the trending token list:

curl -s "https://api.superhero.com/api/tokens?order_by=trending_score&order_direction=DESC&limit=10"

Shape and range

normalized(x, cap) = ln(1 + min(x, cap)) / ln(1 + cap)

trading_score = 0.40*normalized(active_wallets, 25)
              + 0.20*normalized(buy_count, 40)
              + 0.20*normalized(sell_count, 40)
              + 0.20*normalized(volume_ae, 5000)

social_score  = 0.32*normalized(mention_posts, 15)
              + 0.32*normalized(mention_comments, 50)
              + 0.22*normalized(unique_authors, 20)
              + 0.05*normalized(tips_count, 15)
              + 0.03*normalized(tips_amount_ae, 250)
              + 0.06*normalized(reads, 200)

pre_decay     = 0.35*trading_score + 0.65*social_score

freshness     = 1 / (1 + age_hours / 2) ^ 1.15        when pre_decay > 0, else 0

trending_score = round(pre_decay * freshness, 6)

Every weight set sums to 1 and every normalized(...) term lies in [0, 1], so pre_decay lies in [0, 1]; freshness is at most 1 at zero age and falls monotonically. trending_score is a decimal in [0, 1], rounded to six places. 0 means no activity in the window.

The caps, weights and decay constants are shared by every token and nothing depends on other tokens, so the score is comparable across tokens. It is not comparable across time: the same activity scores lower an hour later, because age_hours runs from the most recent qualifying event.

Inputs

The window is the last 24 hours.

SignalCapWeightSource
active_wallets250.40 of tradingDistinct trader addresses over the token's transactions
buy_count400.20Transactions with tx_type = 'buy'
sell_count400.20Transactions with tx_type = 'sell'
volume_ae50000.20Sum of amount.ae over buy, sell and create_community
mention_posts150.32 of socialTop-level posts whose body mentions the symbol
mention_comments500.32Comments anywhere in a thread rooted on such a post
unique_authors200.22Distinct sender_address across those posts and comments
tips_count150.05Tips on those posts, excluding self-tips
tips_amount_ae2500.03Sum of those tips, in AE
reads2000.06Daily read counts for those posts

Trading is worth 0.35 of the total and social 0.65. Attribution is by direct #SYMBOL mention: a top-level post mentioning the symbol roots a token thread, and every non-hidden comment, tip and read in that thread counts for the token whether or not it repeats the hashtag.

When it is recomputed

TriggerCadence
A trade, post, comment or tip involving the tokenImmediately, via a queue deduped per symbol with a 5-second delay
Active refresh: tokens with activity in the last 120 minutes0 */2 * * *, every two hours, up to 250 tokens
Stale backfill: tokens whose score is older than 30 minutes30 */6 * * *, every six hours, up to 150 tokens
GET /api/tokens/{address}/scoreOn demand, and it persists the result

Decay is applied at write time, so a token with no new activity keeps its last score until a backfill reaches it. trending_score_update_at tells you how stale.

Which tokens are eligible

Sorting by trending_score applies a gate before the sort: at least 5 holders, 2 all-time posts mentioning the symbol and 3 all-time trades. GET /api/tokens/{address}/trending-eligibility shows one token's standing against those thresholds. If no token clears the gate the list is re-queried without it.

Topics

A topic is a hashtag that exists because someone posted it, with a post count attached.

Parameter on GET /api/topicsNotes
order_byname, post_count, created_at
order_directionASC or DESC
searchSubstring match on the name
limit, pageStandard pagination
curl -s "https://api.superhero.com/api/topics/name/superhero"
curl -s "https://api.superhero.com/api/topics?order_by=post_count&order_direction=DESC&limit=20"

GET /api/topics/name/{name} returns one topic with a posts array and a post_count number. GET /api/topics/{id} accepts a UUID or a name, and GET /api/topics/popular/trending is a ready-made "most used" list with no parameters.

FieldTypeMeaning
idstringUUID
namestringThe hashtag, unique, at most 100 characters
descriptionstring or nullAt most 500 characters
post_countnumberPosts carrying this topic
tokenTokenDto or nullThe token whose symbol matches this topic name, when one exists
created_at / updated_atstringISO 8601
versionnumberIndexer bookkeeping

GET /api/search

One request across all three entity types:

ParameterNotes
qSearch term, max 100 characters. A q shorter than 2 characters returns empty arrays
limitMax results per category, clamped to 1–20, default 5
curl -s "https://api.superhero.com/api/search?q=super&limit=5"

Returns tokens (full TokenDto objects), accounts (address plus chain_name) and posts (full PostDto objects).

Related