Documentation Index Fetch the complete documentation index at: https://docs.fieldfunded.com/llms.txt
Use this file to discover all available pages before exploring further.
Build a Line Movement Tracker That Spots Sharp Money
Track how odds change over time to detect sharp money movement and steam moves. This guide uses the FieldFunded SDK to poll odds, store historical snapshots, and alert on significant shifts.
What You’ll Use
SDK Method Endpoint Purpose getEvents()GET /v1/eventsList upcoming events with odds getEventOdds()GET /v1/events/{id}/oddsGet current odds for an event getEvent()GET /v1/events/{id}Get event detail with all markets getLive()GET /v1/liveTrack live events
Architecture
Poll getEventOdds() every N minutes
↓
Store snapshot in database (timestamp, event_id, market, odds)
↓
Compare with previous snapshot
↓
If change > threshold → Alert (Discord webhook, email)
Step 1: Set Up
npm install @fieldfunded/sdk better-sqlite3 node-cron
import { FieldFundedSDK } from '@fieldfunded/sdk' ;
import Database from 'better-sqlite3' ;
import cron from 'node-cron' ;
const client = new FieldFundedSDK ({
apiKey: process . env . FIELDFUNDED_API_KEY ! ,
baseUrl: 'https://api.fieldfunded.com/v1' ,
});
const db = new Database ( 'odds_history.db' );
// Create snapshots table
db . exec ( `
CREATE TABLE IF NOT EXISTS odds_snapshots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
event_id TEXT,
home_team TEXT,
away_team TEXT,
market_name TEXT,
selection_name TEXT,
odds REAL,
UNIQUE(timestamp, event_id, market_name, selection_name)
)
` );
Step 2: Snapshot Odds
async function snapshotOdds ( sport : string = 'soccer' ) {
const events = await client . getEvents ({
sport ,
status: 'prematch' ,
starts_within: '24h' ,
});
for ( const event of events . events . slice ( 0 , 20 )) {
const detail = await client . getEvent ( event . id );
if ( ! detail . markets ) continue ;
// Track main markets (1x2, Over/Under, BTTS)
const mainMarkets = detail . markets . filter (( m : any ) =>
[ 'Match Winner' , '1X2' , 'Over/Under 2.5' , 'Both Teams to Score' ]. includes ( m . name )
);
const insert = db . prepare ( `
INSERT OR IGNORE INTO odds_snapshots
(event_id, home_team, away_team, market_name, selection_name, odds)
VALUES (?, ?, ?, ?, ?, ?)
` );
for ( const market of mainMarkets ) {
for ( const selection of market . selections ) {
insert . run (
event . id ,
event . home_team ,
event . away_team ,
market . name ,
selection . name ,
selection . odds
);
}
}
}
}
Step 3: Detect Movement
function detectMovement ( threshold : number = 0.05 ) {
const movements = db . prepare ( `
SELECT
s1.event_id,
s1.home_team,
s1.away_team,
s1.market_name,
s1.selection_name,
s1.odds as current_odds,
s2.odds as previous_odds,
(s1.odds - s2.odds) as change,
ABS(s1.odds - s2.odds) / s2.odds as pct_change
FROM odds_snapshots s1
JOIN odds_snapshots s2 ON
s1.event_id = s2.event_id AND
s1.market_name = s2.market_name AND
s1.selection_name = s2.selection_name AND
s1.id > s2.id
WHERE ABS(s1.odds - s2.odds) / s2.odds > ?
ORDER BY s1.timestamp DESC
LIMIT 20
` ). all ( threshold );
return movements ;
}
Step 4: Alert via Discord Webhook
async function sendAlert ( movement : any ) {
const direction = movement . change > 0 ? '📈' : '📉' ;
const color = movement . change > 0 ? 0x00FF00 : 0xFF0000 ;
await fetch ( process . env . DISCORD_WEBHOOK_URL ! , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
embeds: [{
title: ` ${ direction } Line Movement Detected` ,
color ,
fields: [
{ name: 'Match' , value: ` ${ movement . home_team } vs ${ movement . away_team } ` , inline: false },
{ name: 'Market' , value: ` ${ movement . market_name } — ${ movement . selection_name } ` , inline: false },
{ name: 'Previous' , value: ` ${ movement . previous_odds } ` , inline: true },
{ name: 'Current' , value: ` ${ movement . current_odds } ` , inline: true },
{ name: 'Change' , value: ` ${ ( movement . pct_change * 100 ). toFixed ( 1 ) } %` , inline: true },
],
}],
}),
});
}
Step 5: Schedule
// Snapshot every 10 minutes (144 req/day for 20 events ≈ 4,320/month)
cron . schedule ( '*/10 * * * *' , async () => {
await snapshotOdds ( 'soccer' );
const movements = detectMovement ( 0.05 ); // 5% threshold
for ( const m of movements ) {
await sendAlert ( m );
}
});
Rate Limit Math
Polling frequency Events tracked Requests/day Monthly (free tier?) Every 10 min 20 events ~288 ~8,640 ✅ Every 5 min 20 events ~576 ~17,280 (Starter) Every 2 min 50 events ~3,600 ~108,000 (Pro)
The free tier (10K/month) supports tracking 20 events every 10 minutes.
Events API Reference See events endpoint filters →
Get Your Free API Key Start tracking lines today — 10,000 free requests/month