Skip to main content

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.
  • from reaches five minutes behind the newest event seen, so late arrivals are caught. seen_ids drops the repeats.
  • order=asc so last_seen only moves forward.
  • One alert per story: add novelty=new. A story read again arrives as a new event with novelty: "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 exitPrice is above entryPrice; a short, below. Skip episodes with a null price.
  • closeReason: "expiry" means the account never closed it. Its exitPrice is not one the account chose: count expiries apart.
  • handle takes 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