Workflows
Recipes that chain v3 endpoints. Send each path to https://api.elfa.ai
with your x-elfa-api-key, as in the Quickstart.
<now> and <now - 1d> are Unix seconds.
One Asset at a Glance
What is happening to SOL, and how are accounts positioned?
1. GET /v3/entities?q=sol&entityType=token&limit=1
-> entities[0].id. Check its name first: see Choosing a Match
2. GET /v3/events?entityIds=<id>&from=<now - 1d>&alertAction=alert
-> the flagged events of the last day
3. GET /v3/events/storylines?entityIds=<id>&from=<now - 7d>
-> open storylines naming SOL, active in the last week, with summaries
4. GET /v3/calls/positioning?entityId=<id>
-> openLong, openShort right now
5. GET /v3/calls/episodes?entityIds=<id>&status=open&from=<now>&limit=30
-> who holds those positions, newest opened first
To cover Solana the project too, resolve both with
q=solana&entityType=token,project and pass both ids to steps 2 and 3.
Daily Market Brief
What mattered today, where is attention, and which way does the crowd lean?
1. GET /v3/events?alertAction=alert&novelty=new&from=<now - 1d>&limit=30
-> the flagged stories of the last day, each once. Page with
nextCursor while hasMore is true
2. GET /v3/events/arcs
-> the larger movements. Their storylines:
GET /v3/events/storylines?storylineIds=<up to 30 ids>
3. GET /v3/market/crypto/mindshare?period=24h&limit=20
GET /v3/market/equity/mindshare?period=24h&limit=20
-> the loudest assets of the day, and which are hot right now
4. GET /v3/market/crypto/call-book?from=<now - 1h>&limit=1
GET /v3/market/equity/call-book?from=<now - 1h>&limit=1
-> the newest row of each book, usually the live tick for the
current hour: longShare, crowdingPercentile, pulse24
In step 4, report crowdingPercentile, not longShare: the crowd leans long
most days, and the percentile tells an unusual day apart.
Poll for New Events
import time
last_seen = int(time.time()) - 3600 # start one hour back, in seconds
seen_ids = set()
while True:
params = {"from": last_seen - 300, "order": "asc", "limit": 30}
while True:
# The Quickstart helper. It raises on any error: on
# ERR_RATE_LIMIT_EXCEEDED, sleep and retry.
page = elfa("/v3/events", **params)
for event in page["events"]:
if event["id"] in seen_ids:
continue # overlap from the last poll
seen_ids.add(event["id"])
last_seen = max(last_seen, event["analyzedAt"])
process(event) # your code
if not page["hasMore"]:
break
params["cursor"] = page["nextCursor"] # every other param unchanged
time.sleep(60)
# seen_ids only needs ids from the last few minutes. Prune it in a
# long-running process.
fromreaches five minutes behind the newest event seen, so late arrivals are caught.seen_idsdrops the repeats.order=ascsolast_seenonly moves forward.- One alert per story: add
novelty=new. A story read again arrives as a new event withnovelty: "update". - A cursor belongs to its query: keep every parameter while paging, and start each poll without one.
Follow a Storyline
An event mentions a development. How has it moved since?
1. GET /v3/events?entityIds=<id>&from=<now - 1d>
-> an event with storylineId set
2. GET /v3/events/storylines?storylineIds=<storylineId>
-> title, summary, status, lastMaterialAt, and every event id in the
thread with its relation
3. GET /v3/events?eventIds=<up to 30 event ids from step 2>
-> the events themselves. Pick the ids whose relation is update,
contradiction or consequence to skip background and reactions
4. If arcId is not null:
GET /v3/events/arcs?arcIds=<arcId>
-> the arc's summary and the ids of the other threads in it.
Fetch them with GET /v3/events/storylines?storylineIds=<ids>
Read lastMaterialAt and status to judge whether the thread is still moving.
Research a Topic
What is happening around stablecoin regulation?
1. GET /v3/events/storylines?q=stablecoin%20regulation
-> threads about it, open and dormant, best match first, each with a summary.
Add status=open for only the ones still developing. Prices in a summary
are as of its lastMaterialAt, not now
2. GET /v3/events?q=stablecoin%20regulation&alertAction=alert
-> its flagged events, best match first
3. For a storyline whose arcId is set:
GET /v3/events/arcs?arcIds=<arcId>
-> the larger movement it belongs to
URL-encode q. For one asset rather than a topic, use entityIds: see
Search by Topic.
Check How an Account's Calls Ended
An account gets attention. Do its calls work, and what does it hold now?
1. GET /v3/people/accounts?limit=20
-> the 20 accounts others engage with most, each with its
track: alpha score, hit rate and average return over every position
2. For a handle you are considering:
GET /v3/calls/episodes?handle=<handle>&status=closed&from=<now-30d>&limit=30
-> the positions it held in the last 30 days that have since closed,
each with entryPrice, exitPrice, closeReason and the opening setup
3. GET /v3/calls/episodes?handle=<handle>&status=open&from=<now>
-> what it holds now
4. For the posts behind one episode:
GET /v3/calls?callIds=<calls[].callId from step 2>
-> each call's setup and thesis
- A long worked when
exitPriceis aboveentryPrice; a short, below. Skip episodes with a null price. closeReason: "expiry"means the account never closed it. ItsexitPriceis not one the account chose: count expiries apart.handletakes X accounts only. See Filtering by Account.
Spot New Contracts
Which addresses are being pasted right now, and what do we know about them?
1. GET /v3/market/crypto/trending-contracts?from=<now - 1h>&minMentions=5
-> addresses ranked by posts in the last hour, every source together
2. For a row whose asset is not null:
GET /v3/events?entityIds=<asset.id>&from=<now - 1d>
GET /v3/calls/positioning?entityId=<asset.id>
-> what is being said about it, and how accounts are positioned
3. For a row whose asset is null:
-> nobody has catalogued the address yet. Re-read trending contracts
later; once it has an id, step 2 works