Node.js SDK
Server-side event tracking for Node.js applications with @vanta-dev/node.
The Node.js SDK tracks events, identifies users, records metrics, and monitors uptime from any Node.js server. It is strongly typed and batches requests for efficiency.
Installation
npm install @vanta-dev/nodeSetup
Create a client with your API key. Set VANTA_API_KEY in your environment and never hard-code it in source.
import { Vanta } from '@vanta-dev/node'
const vanta = new Vanta({
apiKey: process.env.VANTA_API_KEY
})The SDK works with both ES modules and CommonJS. In a CommonJS project, use require instead:
const { Vanta } = require('@vanta-dev/node')
const vanta = new Vanta({
apiKey: process.env.VANTA_API_KEY
})By default the client sends data to https://vantapi.pancake.wtf. You can override this with the apiUrl option if needed.
Configuration
The constructor accepts an optional configuration object. All fields are optional and have sensible defaults.
const vanta = new Vanta({
apiKey: process.env.VANTA_API_KEY,
apiUrl: 'https://vantapi.pancake.wtf',
flushInterval: 10_000,
flushSize: 20,
maxRetries: 3,
batchSize: 100,
onDeliveryError: (error, events) => {
console.error(`Failed to deliver ${events.length} events: ${error.message}`)
}
})| Option | Default | Description |
|---|---|---|
apiKey | required | Your Vanta API key. Never hard-code this in source. |
apiUrl | https://vantapi.pancake.wtf | Base URL of the Vanta API. Override for self-hosted or staging environments. |
flushInterval | 10000 | Milliseconds between automatic queue flushes. Set to 0 to disable the timer and flush manually. |
flushSize | 20 | Queue threshold that triggers an immediate flush. |
maxRetries | 3 | Maximum number of retry attempts per batch on network errors or rate limits. |
batchSize | 100 | Maximum number of events sent in a single HTTP request. |
onDeliveryError | none | Callback invoked when a batch fails after all retries are exhausted. Receives the error and the events that failed. Failed events are automatically re-queued at the front of the queue. |
Tracking events
Track
The track method records an event. It accepts arbitrary structured data and optional context and tags.
await vanta.track({
event: 'purchase',
userId: 'user_123',
data: {
product: 'Premium',
amount: 19.99,
currency: 'USD'
},
tags: { plan: 'premium', region: 'us-east' }
})Identify
The identify method links future events to a user. Pass the user's profile fields directly, not inside a properties key.
await vanta.identify({
userId: 'user_123',
email: 'sarah@example.com',
name: 'Sarah',
avatar: 'https://example.com/avatar.png',
properties: {
plan: 'premium',
team: 'growth'
}
})Group
Group events by organization or team with the group method.
await vanta.group({
groupId: 'team_growth',
type: 'team',
name: 'Growth',
properties: {
memberCount: 12,
seat: 'enterprise'
}
})Errors
Pass an Error instance or a string to record an error event with stack and message context.
try {
await someOperation()
} catch (err) {
await vanta.error(err)
}Metrics
Vanta exposes two metric primitives: measurements and counters. Both are batched and delivered like events.
Measurements
Use measure to record a numeric observation such as latency or queue depth. The default aggregation is avg.
await vanta.measure({
name: 'api.latency',
value: 82,
unit: 'ms',
tags: { endpoint: '/checkout' }
})Counters
Use increment and decrement for counters. Values must be non-negative finite numbers. A counter's sum over a range is the net change over that period.
await vanta.increment('page_views')
await vanta.increment('items_processed', 25)
await vanta.decrement('active_connections')
await vanta.decrement('active_connections', 2)Querying metrics
The metrics namespace reads metric data back.
const result = await vanta.metrics.query({
name: 'api.latency',
time: { start: '2026-08-01T00:00:00Z', end: '2026-08-07T00:00:00Z' },
aggregation: 'avg'
})
const series = await vanta.metrics.timeseries({
name: 'page_views',
interval: '1d'
})
const current = await vanta.metrics.current({ name: 'queue.depth' })
const { metrics } = await vanta.metrics.list()metrics.submit records a metric, while query, timeseries, current, and list read them back.
Analytics
Run queries and analytics against your event data using the same query model as the API.
import { filter, and, timeRange } from '@vanta-dev/node'
const events = await vanta.queryEvents({
filters: and(
filter('type', 'equals', 'purchase'),
filter('data.amount', 'gte', 50)
),
sort: [{ field: 'timestamp', direction: 'desc' }],
pagination: { limit: 20 }
})
const single = await vanta.getEvent('evt_123')
const { count } = await vanta.count({
filters: filter('type', 'equals', 'purchase')
})
const groups = await vanta.groupBy({
groupBy: ['data.country'],
filters: filter('type', 'equals', 'purchase')
})
const series = await vanta.timeseries({
interval: 'day',
time: timeRange('2026-08-01T00:00:00Z', '2026-08-07T00:00:00Z')
})
const agg = await vanta.aggregate({
aggregations: [
{ name: 'totalRevenue', field: 'data.amount', operation: 'sum' }
]
})
const unique = await vanta.unique({ field: 'data.country' })Uptime
The uptime namespace sends heartbeats, reads monitors, and reports incidents. Monitors themselves are created, edited, and deleted from the dashboard.
await vanta.uptime.heartbeat('discord-bot')
const monitor = await vanta.uptime.getMonitor('mon_123')
const monitors = await vanta.uptime.listMonitors()
const heartbeats = await vanta.uptime.getHeartbeats('mon_123')
const incidents = await vanta.uptime.getIncidents('mon_123')
const stats = await vanta.uptime.getStats('mon_123', '7d')
const running = vanta.uptime.start('discord-bot', { intervalMs: 30_000 })
// ...
running.stop()Query helpers
The SDK exports helpers to build filters and time ranges with full TypeScript inference.
import { filter, and, or, timeRange } from '@vanta-dev/node'
const single = filter('type', 'equals', 'purchase')
const combined = and(
filter('type', 'equals', 'purchase'),
or(
filter('data.country', 'equals', 'US'),
filter('data.country', 'equals', 'CA')
)
)
const range = timeRange('2026-08-01T00:00:00Z', '2026-08-07T00:00:00Z')Errors and retries
The SDK throws typed errors. Catch them to handle failures explicitly.
import { VantaError, RateLimitError, AuthenticationError } from '@vanta-dev/node'
try {
await vanta.track({ event: 'purchase', data: { amount: 10 } })
} catch (err) {
if (err instanceof RateLimitError) {
await sleep(err.retryAfter)
} else if (err instanceof AuthenticationError) {
console.error('Invalid API key')
} else if (err instanceof VantaError) {
console.error('Vanta request failed:', err.message)
}
}Delivery errors
Individual method calls like track and measure queue events internally and return immediately. Delivery happens in the background through the flush cycle. If a batch fails after all retries, the onDeliveryError callback fires and the failed events are re-queued at the front of the queue so they are retried on the next flush.
const vanta = new Vanta({
apiKey: process.env.VANTA_API_KEY,
onDeliveryError: (error, events) => {
console.error(`Failed to deliver ${events.length} events:`)
console.error(`Error: ${error.message}`)
console.error(`Events: ${events.map(e => e.type).join(', ')}`)
if (error instanceof RateLimitError) {
console.error(`Rate limited. Retry after ${error.retryAfter}s`)
}
}
})Shutdown
Call shutdown to flush any queued events and close the client before your process exits.
await vanta.shutdown()This flushes the pending queue and stops the internal flush timer. Call it once at the end of your program or on graceful shutdown.