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

bash
npm install @vanta-dev/node

Setup

Create a client with your API key. Set VANTA_API_KEY in your environment and never hard-code it in source.

typescript
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:

javascript
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.

typescript
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}`)
  }
})
OptionDefaultDescription
apiKeyrequiredYour Vanta API key. Never hard-code this in source.
apiUrlhttps://vantapi.pancake.wtfBase URL of the Vanta API. Override for self-hosted or staging environments.
flushInterval10000Milliseconds between automatic queue flushes. Set to 0 to disable the timer and flush manually.
flushSize20Queue threshold that triggers an immediate flush.
maxRetries3Maximum number of retry attempts per batch on network errors or rate limits.
batchSize100Maximum number of events sent in a single HTTP request.
onDeliveryErrornoneCallback 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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.