Skip to content
Data

Theses

Query Trade Theses and verify that one was posted.

A Trade Thesis is a public post by a Fomo profile about a token. FomoData normalizes theses with stable ids (th_…), embedded profile and token references, and lets you query, page and verify them.

The thesis object

object"thesis"
Always thesis.
idstring
Stable FomoData id (th_…).
profileProfileRef
{ id, handle, display_name, avatar_url } of the author.
tokenTokenRef
{ id, symbol, name, chain, address } of the token.
textstring
The thesis text.
public_urlstring | null
Fomo exposes no public per-thesis permalink today, so this is null unless one becomes available.
likesinteger | null
Public like count, when the upstream provides it.
repliesinteger | null
Public reply count, when the upstream provides it.
created_attimestamp
When the thesis was posted.
updated_attimestamp
When FomoData last wrote this record.
source"fomo" | "sandbox"
Data origin.
livemodeboolean
true for live data.

List theses

GET/v1/theses
Query parameters
tokenstring
Token ref: $RUN, RUN, a contract address, chain:address or tk_…. See token references.
handlestring
Author handle, e.g. @milo.
profilestring
Author profile: fp_…, @handle or Fomo user id.
chainstring
Only theses on this chain: robinhood, solana, base… Sandbox tokens use sandbox.
created_aftertimestamp
ISO-8601 lower bound (inclusive).
created_beforetimestamp
ISO-8601 upper bound (inclusive).
sincestring
Relative lower bound: 90s, 30m, 1h, 7d. Shorthand for created_after = now - since; combined with created_after, the later bound wins.
sortenum
created_at_desc (default), created_at_asc or likes_desc.
limitinteger
1–100. Default 25.
cursorstring
The next_cursor from the previous page.
const theses =
  await fomo.theses.list({
    token: "$RUN",
    since: "1h"
  });

History window

Queries can reach back as far as your plan's history window (history_days). Without an explicit lower bound, results are limited to that window; asking for older data returns 403 HISTORY_LIMIT.

Pagination

Lists are cursor-paginated and stable under concurrent inserts. Pass next_cursor back as cursor until has_more is false. Cursors are opaque; don't parse them. An invalid or expired cursor returns 400 INVALID_CURSOR.

paginate.ts
// The SDK follows next_cursor for you:
for await (const thesis of fomo.theses.iterate({ token: "$RUN", since: "1h" })) {
  handle(thesis);
}

// Or page manually:
let cursor: string | undefined;
do {
  const page = await fomo.theses.list({ token: "$RUN", limit: 100, cursor });
  for (const thesis of page.data) handle(thesis);
  cursor = page.next_cursor ?? undefined;
} while (cursor);

Retrieve a thesis

GET/v1/theses/{id}
const thesis = await fomo.theses.get("th_7Hq2LmZx9RkT4bVn0sYc3D");

Verify a thesis

The endpoint games and contests are built on: did this profile post a thesis on this token inside this time window? One call, a yes or no, and the evidence.

POST/v1/verify/thesis
Request body
handlestring
Author handle. Provide handle or profile_id.
profile_idstring
Author profile id (fp_…) or Fomo user id.
tokenstringrequired
Token ref, as in the list filters.
chainstring
Narrows a symbol that exists on several chains.
created_aftertimestamprequired
Start of the window (inclusive).
created_beforetimestamp
End of the window (inclusive). Defaults to now. Must be after created_after (422 INVALID_TIME_WINDOW).
containsstring
Optional text the thesis must contain (case-insensitive).
const result =
  await fomo.theses.verify({
    handle: "@sbx_milo",
    token: "$RUN",
    after: start,
    before: end
  });

if (result.verified) admitToRace(result.profile_id);

The response is 200 whether or not the thesis is verified; read verified and reason. Errors are reserved for bad input, auth and outages.

What verification checks

CheckMeaning
identityThe handle or profile id resolves to a known profile.
tokenThe token ref resolves to a known token.
time_windowA thesis by that profile on that token exists inside the window.
thesis_existsA valid thesis matched.
not_deletedThe matched thesis hasn't been deleted or invalidated upstream, when the upstream exposes it.
containsOnly when you passed contains; otherwise null.

reason is one of VERIFIED, PROFILE_NOT_FOUND, TOKEN_NOT_FOUND, NO_THESIS_IN_WINDOW, TEXT_NOT_FOUND or THESIS_DELETED. The freshness block says when the check ran and whether it used a live upstream sync or the cache.

Verification never judges content

It checks identity, token, time window and existence. It never scores thesis quality, sentiment, intelligence or accuracy.

Create test theses

With a test key you can create sandbox theses to exercise verification, streams and webhooks end to end. They're visible only to your project and emit a labelled thesis.created event (livemode: false, source: "sandbox", test: true). Live keys get 400 TEST_MODE_ONLY.

POST/v1/test/theses
curl -X POST 'https://fomodata.dev/v1/test/theses' \
  -H "Authorization: Bearer $FOMODATA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "handle": "@sbx_milo", "token": "$RUN", "text": "Sandbox thesis: testing my race entry flow." }'

The response is 201 with the new thesis and the event that was emitted. Use a sandbox fixture handle and token, or any new @handle / $SYMBOL: it becomes a test profile or token visible only to your project. Test theses are never deduplicated by text, so you can post the same text twice.

Errors

StatusCodeWhen
400INVALID_CURSORThe cursor is malformed or expired.
403HISTORY_LIMITThe window reaches past your plan's history.
404THESIS_NOT_FOUNDNo thesis has that id.
409AMBIGUOUS_TOKENA symbol matches tokens on several chains. Pass chain or an address.
422INVALID_TIME_WINDOWcreated_after isn't before created_before.
422INVALID_TOKENThe token ref couldn't be resolved.