Appearance
Tracker SDK Guide
The Urban Sky SDK provides a complete tracker management interface for creating trackers, uploading GPS data, and querying positions and sessions.
Table of Contents
- Quick Start
- Required Scopes
- Creating Trackers
- Ingesting Points
- Querying Trackers
- Sessions & Points
- Updating Trackers
- API Reference
Quick Start
JavaScript
javascript
const { UrbanSkySDK } = require('@urbansky/sdk')
const sdk = await UrbanSkySDK.init({
apiToken: 'your-api-token',
})
// Create a tracker
const tracker = await sdk.createTracker({ name: 'Field Unit', type: 'GPS' })
// Upload points
await sdk.ingestPoints(tracker.id, [
{ latitude: 39.74, longitude: -104.99, timestamp: Date.now(), altitude: 1609 },
])
// Query sessions
const { sessions } = await sdk.getTrackerSessions(tracker.id)
const { messages } = await sdk.getSessionPoints(sessions[0].id)Python
python
from urbansky_sdk import UrbanSkySDK
sdk = await UrbanSkySDK.init({
'apiToken': 'your-api-token',
})
# Create a tracker
tracker = sdk.create_tracker(name='Field Unit', type='GPS')
# Upload points
import time
sdk.ingest_points(tracker.id, [
{'latitude': 39.74, 'longitude': -104.99, 'timestamp': int(time.time() * 1000), 'altitude': 1609},
])
# Query sessions
sessions = sdk.get_tracker_sessions(tracker.id)
points = sdk.get_session_points(sessions.sessions[0].id)Required Scopes
Every tracker endpoint is scope-gated. Your API token needs the right allowed_resources entry to call each operation. A 403 Forbidden on a call that used to work means your token's scopes need to be updated.
| Operation | Method | Required scope |
|---|---|---|
listTrackers, getTrackerSessions, getSessionPoints, getLatestPositions | GET | trackers:read |
createTracker | POST | trackers:write |
updateTracker | PATCH | trackers:write |
shareTracker, revokeTrackerShare | POST | trackers:write |
ingestPoints | POST | trackers:ingest |
| Any of the above | — | trackers:* |
API tokens that existed before this SDK release were auto-granted trackers:read only. Write / ingest must be granted explicitly. See the release notes for the inventory SQL and grant playbook.
Creating Trackers
Create a tracker before uploading data. You can provide your own ID or let the system generate one.
JavaScript
javascript
// Auto-generated ID
const tracker = await sdk.createTracker({
name: 'Vehicle GPS',
type: 'GPS',
})
console.log(tracker.id) // e.g. "a1b2c3d4-..."
// Custom ID
const tracker2 = await sdk.createTracker({
id: 'truck-42',
name: 'Truck 42',
type: 'FLEET',
})Python
python
# Auto-generated ID
tracker = sdk.create_tracker(name='Vehicle GPS', type='GPS')
# Custom ID
tracker2 = sdk.create_tracker(id='truck-42', name='Truck 42', type='FLEET')Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | No | Custom tracker ID. Auto-generated if omitted |
name | string | Yes | Display name for the tracker |
type | string | No | Tracker type (e.g. gps, fleet). Defaults to custom. Case-insensitive on input; always returned lowercase. |
Ingesting Points
Upload GPS points for a tracker. Points are stored immediately and become queryable via sessions.
Create before ingesting
You must call createTracker before sending points. The ingest endpoint returns 404 Tracker not found for non-existent ids — there is no auto-create. createTracker requires the trackers:write scope; ingestPoints requires trackers:ingest (see Required Scopes).
JavaScript
javascript
await sdk.ingestPoints('truck-42', [
{
latitude: 39.7392,
longitude: -104.9903,
timestamp: 1705753800000, // Unix epoch milliseconds
altitude: 1609, // Optional, meters
metadata: { speed: 45.2, heading: 270 }, // Optional
},
{
latitude: 39.74,
longitude: -104.989,
timestamp: 1705753860000,
altitude: 1615,
},
])Python
python
sdk.ingest_points('truck-42', [
{
'latitude': 39.7392,
'longitude': -104.9903,
'timestamp': 1705753800000,
'altitude': 1609,
'metadata': {'speed': 45.2, 'heading': 270},
},
{
'latitude': 39.7400,
'longitude': -104.9890,
'timestamp': 1705753860000,
'altitude': 1615,
},
])Point fields:
| Field | Type | Required | Description |
|---|---|---|---|
latitude | number | Yes | Latitude (-90 to 90) |
longitude | number | Yes | Longitude (-180 to 180) |
timestamp | number | Yes | Unix epoch in milliseconds |
altitude | number | No | Altitude in meters |
metadata | object | No | Arbitrary key-value pairs |
Limits: Up to 10,000 points per request. For larger datasets, batch your calls.
Querying Trackers
List All Trackers
javascript
// JavaScript
const { trackers } = await sdk.listTrackers()
trackers.forEach(t => {
console.log(`${t.name} (${t.type}) - ${t.isActive ? 'active' : 'inactive'}`)
})
// Filter by name or type
const { trackers: gps } = await sdk.listTrackers({ type: 'GPS' })
const { trackers: search } = await sdk.listTrackers({ name: 'truck' })python
# Python
result = sdk.list_trackers()
for t in result.trackers:
print(f'{t.name} ({t.type}) - {"active" if t.is_active else "inactive"}')
# Filter
gps = sdk.list_trackers(type='GPS')
search = sdk.list_trackers(name='truck')Latest Positions
Get the most recent position for every tracker in your organization.
javascript
// JavaScript
const { positions } = await sdk.getLatestPositions()
positions.forEach(p => {
console.log(`${p.trackerName}: ${p.latitude}, ${p.longitude} (${p.lastTime})`)
})python
# Python
latest = sdk.get_latest_positions()
for p in latest.positions:
print(f'{p.tracker_name}: {p.latitude}, {p.longitude} ({p.last_time})')Sessions & Points
Tracker data is automatically organized into sessions based on time gaps. If a tracker goes silent for longer than its session timeout (default: 6 hours), a new session starts when data resumes.
Get Sessions
javascript
// JavaScript
const { sessions } = await sdk.getTrackerSessions('truck-42', { limit: 10 })
sessions.forEach(s => {
console.log(`${s.startTime} → ${s.endTime} (${s.messageCount} points)`)
})python
# Python
result = sdk.get_tracker_sessions('truck-42', limit=10)
for s in result.sessions:
print(f'{s.start_time} → {s.end_time} ({s.message_count} points)')Get Session Points
Retrieve the actual GPS points for a specific session.
javascript
// JavaScript
const { messages } = await sdk.getSessionPoints(sessions[0].id, { limit: 300 })
messages.forEach(m => {
console.log(`[${m.time}] ${m.latitude}, ${m.longitude}`)
})python
# Python
result = sdk.get_session_points(sessions[0].id, limit=300)
for m in result.messages:
print(f'[{m.time}] {m.latitude}, {m.longitude}')Session IDs
Session IDs use the format trackerId:startTimeMs (e.g. truck-42:1705234800000). You don't need to construct these yourself — they're returned by getTrackerSessions.
Scan window
The session endpoints read up to the last 730 days (~2 years) of tracker history. Sessions older than that aren't returned by getTrackerSessions; sessions longer than 2 years are truncated to their first 730 days of messages. This covers the common balloon flight use case (typical sessions ≤ 1 year).
Updating Trackers
javascript
// JavaScript
const updated = await sdk.updateTracker('truck-42', {
name: 'Truck 42 (Retired)',
sessionTimeoutHours: 24, // 1-336 hours
alias: 'Old Faithful',
groundPinned: true,
})python
# Python
updated = sdk.update_tracker(
'truck-42',
name='Truck 42 (Retired)',
session_timeout_hours=24,
alias='Old Faithful',
ground_pinned=True,
)Updatable fields:
| Field | Type | Description |
|---|---|---|
name | string | Display name |
type | string | Tracker type |
sessionTimeoutHours | number | Hours of silence before new session (1-336) |
alias | string | null | User-friendly name override |
icon | string | null | Icon type for map display |
groundPinned | boolean | Pin tracker to ground level on map |
API Reference
JavaScript Methods
| Method | Returns | Description |
|---|---|---|
createTracker(options) | CreateTrackerResponse | Create a new tracker |
listTrackers(options?) | TrackerListResponse | List organization trackers |
updateTracker(id, options) | UpdateTrackerResponse | Update tracker properties |
ingestPoints(trackerId, points) | IngestPointsResponse | Upload GPS points |
getTrackerSessions(trackerId, options?) | TrackerSessionsResponse | Get sessions for a tracker |
getSessionPoints(sessionId, options?) | SessionPointsResponse | Get points for a session |
getLatestPositions() | LatestPositionsResponse | Latest position per tracker |
Python Methods
| Method | Returns | Description |
|---|---|---|
create_tracker(name, type?, id?) | CreateTrackerResponse | Create a new tracker |
list_trackers(name?, type?) | TrackerListResponse | List organization trackers |
update_tracker(tracker_id, **kwargs) | UpdateTrackerResponse | Update tracker properties |
ingest_points(tracker_id, points) | IngestPointsResponse | Upload GPS points |
get_tracker_sessions(tracker_id, limit?) | TrackerSessionsResponse | Get sessions for a tracker |
get_session_points(session_id, limit?) | SessionPointsResponse | Get points for a session |
get_latest_positions() | LatestPositionsResponse | Latest position per tracker |
Next Steps
- Imagery SDK Guide - Access mission imagery
- Basic Telemetry Guide - Real-time balloon tracking
- Error Handling Guide - Handling SDK errors