Skip to main content
GuidesAPI ReferenceExamples

Poll Events

|

Retrieve monitoring events efficiently for real-time alerting and analytics systems.

Overview

Event polling enables continuous monitoring of asset activities without missing events. The API supports both historical queries and incremental polling for real-time systems.

Use Case

A fleet management system needs to:

  • Retrieve new events since last poll
  • Process events in order for accurate tracking
  • Build real-time dashboards showing asset activities
  • Trigger alerts based on event patterns

Polling Strategy

Example 1: Basic Event Polling

Poll for new events since last check.

curl -X GET https://api.magiclane.net/api/v1/poll_monitor_events \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"p_last_poll_timestamp": 1754810032,
"p_limit": 100,
"p_sort_order": "asc"
}'

Response Structure

{
"data": {
"monitor_events": [
{
"asset_id": "vehicle_101",
"event_type": "enter",
"monitor_id": "delivery_zone_monitor",
"geofence_id": "downtown_delivery_zone",
"event_location": {
"lat": 45.647265,
"lon": 25.612887
},
"event_timestamp": 1754810100,
"previous_location": {
"lat": 45.646980,
"lon": 25.611922
}
},
{
"asset_id": "truck_205",
"event_type": "speeding",
"monitor_id": "fleet_speed_monitor",
"event_location": {
"lat": 45.648579,
"lon": 25.614076
},
"event_timestamp": 1754810150
}
]
}
}

Example 2: Continuous Polling Loop

Implement a continuous event monitoring system.

class EventPoller {
constructor(apiKey, pollInterval = 30000) {
this.apiKey = apiKey;
this.pollInterval = pollInterval; // 30 seconds
this.lastTimestamp = Math.floor(Date.now() / 1000);
this.isRunning = false;
}

async poll() {
try {
const response = await fetch(
'https://api.magiclane.net/api/v1/poll_monitor_events',
{
method: 'GET',
headers: {
'Authorization': this.apiKey,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
p_last_poll_timestamp: this.lastTimestamp,
p_limit: 5000,
p_sort_order: 'asc'
})
}
);

const data = await response.json();
const events = data.data.monitor_events;

if (events.length > 0) {
// Process events
await this.processEvents(events);

// Update last timestamp to newest event
this.lastTimestamp = Math.max(
...events.map(e => e.event_timestamp)
);

console.log(`Processed ${events.length} events. Last timestamp: ${this.lastTimestamp}`);
} else {
console.log('No new events');
}

return events;
} catch (error) {
console.error('Polling error:', error);
throw error;
}
}

async processEvents(events) {
for (const event of events) {
// Handle different event types
switch (event.event_type) {
case 'enter':
await this.handleEntry(event);
break;
case 'exit':
await this.handleExit(event);
break;
case 'speeding':
await this.handleSpeeding(event);
break;
case 'idle':
await this.handleIdle(event);
break;
}
}
}

async handleEntry(event) {
console.log(`Asset ${event.asset_id} entered ${event.geofence_id}`);
// Send notification, update database, etc.
}

async handleExit(event) {
console.log(`Asset ${event.asset_id} exited ${event.geofence_id}`);
}

async handleSpeeding(event) {
console.log(`Asset ${event.asset_id} speeding detected`);
// Send alert
}

async handleIdle(event) {
console.log(`Asset ${event.asset_id} idle detected`);
}

start() {
if (this.isRunning) return;

this.isRunning = true;
console.log(`Event poller started (interval: ${this.pollInterval}ms)`);

this.intervalId = setInterval(async () => {
if (this.isRunning) {
await this.poll();
}
}, this.pollInterval);
}

stop() {
this.isRunning = false;
if (this.intervalId) {
clearInterval(this.intervalId);
console.log('Event poller stopped');
}
}
}

// Usage
const poller = new EventPoller('YOUR_API_KEY', 30000);
poller.start();

// Stop after 1 hour
setTimeout(() => {
poller.stop();
}, 3600000);

Sort Order Strategy

ASC (Ascending) - For Polling

{
p_sort_order: 'asc' // Oldest events first
}

Best for: Sequential processing, avoiding duplicates

  • Events processed in chronological order
  • Easier to track last processed timestamp
  • Recommended for continuous polling

DESC (Descending) - For Display

{
p_sort_order: 'desc' // Newest events first
}

Best for: Dashboards, recent activity views

  • Shows most recent events first
  • Good for UI displays
  • Not recommended for polling loops

Limit Guidelines

LimitUse CasePolling Interval
100Low-traffic systems60 seconds
1,000Medium traffic30 seconds
5,000High traffic15-30 seconds
10,000Maximum (batch processing)As needed

Example 3: Event Analytics

Analyze polled events for insights.

class EventAnalytics {
constructor(apiKey) {
this.apiKey = apiKey;
this.events = [];
}

async pollAndAnalyze(startTimestamp, endTimestamp) {
let currentTimestamp = startTimestamp;

while (currentTimestamp < endTimestamp) {
const response = await fetch(
'https://api.magiclane.net/api/v1/poll_monitor_events',
{
method: 'GET',
headers: {
'Authorization': this.apiKey,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
p_last_poll_timestamp: currentTimestamp,
p_limit: 10000,
p_sort_order: 'asc'
})
}
);

const data = await response.json();
const events = data.data.monitor_events;

if (events.length === 0) break;

this.events.push(...events);
currentTimestamp = Math.max(...events.map(e => e.event_timestamp));
}

return this.analyze();
}

analyze() {
const stats = {
total_events: this.events.length,
by_type: {},
by_asset: {},
by_monitor: {},
time_range: {
start: Math.min(...this.events.map(e => e.event_timestamp)),
end: Math.max(...this.events.map(e => e.event_timestamp))
}
};

// Count by event type
this.events.forEach(event => {
stats.by_type[event.event_type] =
(stats.by_type[event.event_type] || 0) + 1;

stats.by_asset[event.asset_id] =
(stats.by_asset[event.asset_id] || 0) + 1;

stats.by_monitor[event.monitor_id] =
(stats.by_monitor[event.monitor_id] || 0) + 1;
});

// Find most active asset
stats.most_active_asset = Object.entries(stats.by_asset)
.sort((a, b) => b[1] - a[1])[0];

return stats;
}
}

// Usage
const analytics = new EventAnalytics('YOUR_API_KEY');
const stats = await analytics.pollAndAnalyze(
1754810000, // Start time
1754820000 // End time
);

console.log(stats);
/* Output:
{
total_events: 245,
by_type: {
enter: 98,
exit: 97,
speeding: 35,
idle: 15
},
by_asset: {
vehicle_101: 82,
truck_205: 76,
bike_301: 87
},
most_active_asset: ['bike_301', 87]
}
*/

Handling Event Bursts

When many events occur simultaneously:

async function handleEventBurst(lastTimestamp) {
const batchSize = 5000;
let allEvents = [];
let currentTimestamp = lastTimestamp;
let hasMore = true;

while (hasMore) {
const response = await fetch(
'https://api.magiclane.net/api/v1/poll_monitor_events',
{
method: 'GET',
headers: {
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
p_last_poll_timestamp: currentTimestamp,
p_limit: batchSize,
p_sort_order: 'asc'
})
}
);

const data = await response.json();
const events = data.data.monitor_events;

if (events.length === 0) {
hasMore = false;
} else {
allEvents.push(...events);

if (events.length < batchSize) {
hasMore = false;
} else {
// More events might exist, update timestamp and continue
currentTimestamp = Math.max(...events.map(e => e.event_timestamp));
}
}
}

return allEvents;
}

Error Handling Example

async function pollWithRetry(lastTimestamp, maxRetries = 3) {
let retries = 0;

while (retries < maxRetries) {
try {
const response = await fetch(
'https://api.magiclane.net/api/v1/poll_monitor_events',
{
method: 'GET',
headers: {
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
p_last_poll_timestamp: lastTimestamp,
p_limit: 1000,
p_sort_order: 'asc'
})
}
);

if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}

return await response.json();

} catch (error) {
retries++;
console.error(`Poll attempt ${retries} failed:`, error);

if (retries < maxRetries) {
// Exponential backoff
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, retries)));
} else {
throw error;
}
}
}
}

Real-Time Dashboard Example

class RealtimeDashboard {
constructor(apiKey) {
this.apiKey = apiKey;
this.lastTimestamp = Math.floor(Date.now() / 1000) - 3600; // Last hour
this.stats = {
entries: 0,
exits: 0,
speeding: 0,
idle: 0
};
}

async update() {
const events = await this.pollEvents();
this.updateStats(events);
this.render();
}

async pollEvents() {
const response = await fetch(
'https://api.magiclane.net/api/v1/poll_monitor_events',
{
method: 'GET',
headers: {
'Authorization': this.apiKey,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
p_last_poll_timestamp: this.lastTimestamp,
p_limit: 100,
p_sort_order: 'asc'
})
}
);

const data = await response.json();
return data.data.monitor_events;
}

updateStats(events) {
events.forEach(event => {
if (event.event_type === 'enter') this.stats.entries++;
if (event.event_type === 'exit') this.stats.exits++;
if (event.event_type === 'speeding') this.stats.speeding++;
if (event.event_type === 'idle') this.stats.idle++;
});

if (events.length > 0) {
this.lastTimestamp = Math.max(...events.map(e => e.event_timestamp));
}
}

render() {
console.log('Dashboard Stats:');
console.log(`Entries: ${this.stats.entries}`);
console.log(`Exits: ${this.stats.exits}`);
console.log(`Speeding: ${this.stats.speeding}`);
console.log(`Idle: ${this.stats.idle}`);
}

start(interval = 30000) {
setInterval(() => this.update(), interval);
}
}

// Usage
const dashboard = new RealtimeDashboard('YOUR_API_KEY');
dashboard.start(30000); // Update every 30 seconds

Notes

  • Use asc sort order for polling
  • Store last timestamp persistently
  • Process events in batches
  • Use appropriate poll intervals (30-60s)
  • Implement retry logic
  • Don't use desc for continuous polling