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/thesestheses:readtokenstring- Token ref:
$RUN,RUN, a contract address,chain:addressortk_…. See token references. handlestring- Author handle, e.g.
@milo. profilestring- Author profile:
fp_…,@handleor Fomo user id. chainstring- Only theses on this chain:
robinhood,solana,base… Sandbox tokens usesandbox. created_aftertimestamp- ISO-8601 lower bound (inclusive).
created_beforetimestamp- ISO-8601 upper bound (inclusive).
sincestring- Relative lower bound:
90s,30m,1h,7d. Shorthand forcreated_after = now - since; combined withcreated_after, the later bound wins. sortenumcreated_at_desc(default),created_at_ascorlikes_desc.limitinteger- 1–100. Default 25.
cursorstring- The
next_cursorfrom 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.
// 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}theses:readconst 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/thesistheses:readhandlestring- Author handle. Provide
handleorprofile_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
| Check | Meaning |
|---|---|
identity | The handle or profile id resolves to a known profile. |
token | The token ref resolves to a known token. |
time_window | A thesis by that profile on that token exists inside the window. |
thesis_exists | A valid thesis matched. |
not_deleted | The matched thesis hasn't been deleted or invalidated upstream, when the upstream exposes it. |
contains | Only 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.
/v1/test/thesestheses:readcurl -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
| Status | Code | When |
|---|---|---|
| 400 | INVALID_CURSOR | The cursor is malformed or expired. |
| 403 | HISTORY_LIMIT | The window reaches past your plan's history. |
| 404 | THESIS_NOT_FOUND | No thesis has that id. |
| 409 | AMBIGUOUS_TOKEN | A symbol matches tokens on several chains. Pass chain or an address. |
| 422 | INVALID_TIME_WINDOW | created_after isn't before created_before. |
| 422 | INVALID_TOKEN | The token ref couldn't be resolved. |