Skip to main content

Metric queries

Metric queries read raw and aggregated metric data from the observability storage domain. For exporter and storage setup, see the Metrics overview. For automatic metric names and labels, see the Automatic metrics reference.

Access
Direct link to Access

Observability store
Direct link to Observability store

Use the storage domain for in-process queries:

const observability = await mastra.getStorage()?.getStore('observability')

if (!observability) {
throw new Error('Observability storage is not configured')
}

const result = await observability.getMetricAggregate({
name: ['mastra_agent_duration_ms'],
aggregation: 'avg',
filters: {
timestamp: { start: new Date(Date.now() - 60 * 60 * 1000) },
},
})

getStore('observability') returns undefined when the storage configuration doesn't provide the observability domain.

Client SDK
Direct link to Client SDK

@mastra/client-js exposes the analytics and metric discovery methods on MastraClient:

import { MastraClient } from '@mastra/client-js'

const client = new MastraClient({
baseUrl: 'http://localhost:4111',
})

const result = await client.getMetricAggregate({
name: ['mastra_agent_duration_ms'],
aggregation: 'avg',
})

The client doesn't expose a raw listMetrics() method. Use the observability store or the GET /api/observability/metrics route to list raw metric records.

Shared values
Direct link to Shared values

Aggregations
Direct link to Aggregations

The aggregate, breakdown, and time-series methods accept these aggregation values:

ValueResult
sumSum of metric values
avgAverage metric value
minMinimum metric value
maxMaximum metric value
countNumber of matching metric records
count_distinctApproximate or exact number of distinct values in distinctColumn, depending on the storage backend
lastMost recent matching metric value

When aggregation is count_distinct, distinctColumn is required. Supported columns are:

entityType
entityName
parentEntityType
parentEntityName
rootEntityType
rootEntityName
name
provider
model
environment
executionSource
serviceName
threadId
resourceId

Intervals
Direct link to Intervals

Time-series and percentile queries accept 1m, 5m, 15m, 1h, or 1d.

Filters
Direct link to Filters

All metric operations accept the same optional filters object. Raw list requests pass these fields as query parameters. Analytics methods pass them in the JSON request body.

FieldTypeDescription
timestamp{ start?: Date; end?: Date; startExclusive?: boolean; endExclusive?: boolean }Timestamp range. Boundaries are inclusive unless their corresponding exclusive flag is true. HTTP requests use ISO date strings.
traceIdstringExact trace ID
traceIdsstring[]One to 1,000 trace IDs
spanIdstringExact span ID
entityTypeEntityTypeEntity type
entityNamestringEntity name
entityVersionIdstringEntity version ID
parentEntityTypeEntityTypeParent entity type
parentEntityNamestringParent entity name
parentEntityVersionIdstringParent entity version ID
rootEntityTypeEntityTypeRoot entity type
rootEntityNamestringRoot entity name
rootEntityVersionIdstringRoot entity version ID
userIdstringUser ID
organizationIdstringOrganization ID
experimentIdstringExperiment or evaluation run ID
serviceNamestringService name
environmentstringEnvironment name
resourceIdstringResource ID
runIdstringRun ID
sessionIdstringSession ID
threadIdstringThread ID
requestIdstringRequest ID
executionSourcestringExecution source
tagsstring[]Records must contain all specified tags
namestring[]One or more metric names
providerstringModel provider
modelstringModel ID
costUnitstringCost unit
labelsRecord<string, string>Exact matches for all specified metric label key-value pairs
sourcestringDeprecated. Use executionSource.

Analytics methods
Direct link to Analytics methods

getMetricAggregate(args)
Direct link to getmetricaggregateargs

Returns one value across all matching records. The observability store and MastraClient expose this method.

Arguments
Direct link to Arguments

FieldTypeRequiredDescription
namestring[]YesOne or more metric names
aggregationAggregationTypeYesAggregation to apply
distinctColumnMetricDistinctColumnFor count_distinctColumn whose distinct values are counted
filtersMetricsFilterNoShared metric filters
comparePeriod'previous_period' | 'previous_day' | 'previous_week'NoAdds comparison-period values. previous_period uses the duration of filters.timestamp.

Returns
Direct link to Returns

{
value: number | null
previousValue?: number | null
changePercent?: number | null
estimatedCost?: number | null
costUnit?: string | null
previousEstimatedCost?: number | null
costChangePercent?: number | null
}

costUnit is null when the matching records don't have one shared unit. Cost fields are optional and may be null when the records don't include cost context.

const result = await observability.getMetricAggregate({
name: ['mastra_model_total_input_tokens', 'mastra_model_total_output_tokens'],
aggregation: 'sum',
filters: {
timestamp: {
start: new Date('2026-08-24T00:00:00Z'),
end: new Date('2026-08-25T00:00:00Z'),
},
},
comparePeriod: 'previous_period',
})

HTTP: POST /api/observability/metrics/aggregate

getMetricBreakdown(args)
Direct link to getmetricbreakdownargs

Groups matching records by one or more dimensions and aggregates each group. The observability store and MastraClient expose this method.

Arguments
Direct link to Arguments

FieldTypeRequiredDescription
namestring[]YesOne or more metric names
groupBystring[]YesOne or more fields to group by
aggregationAggregationTypeYesAggregation for each group
distinctColumnMetricDistinctColumnFor count_distinctColumn whose distinct values are counted
filtersMetricsFilterNoShared metric filters
limitnumberNoPositive integer up to 1,000. Required for high-cardinality groupings.
orderDirection'ASC' | 'DESC'NoSort direction for the aggregated value. Storage implementations default to DESC.

Returns
Direct link to Returns

{
groups: Array<{
dimensions: Record<string, string | null>
value: number
estimatedCost?: number | null
costUnit?: string | null
}>
}
const result = await client.getMetricBreakdown({
name: ['mastra_model_total_input_tokens'],
groupBy: ['entityName'],
aggregation: 'sum',
limit: 10,
orderDirection: 'DESC',
})

HTTP: POST /api/observability/metrics/breakdown

getMetricTimeSeries(args)
Direct link to getmetrictimeseriesargs

Buckets matching values by time interval, with optional grouping. The observability store and MastraClient expose this method.

Arguments
Direct link to Arguments

FieldTypeRequiredDescription
namestring[]YesOne or more metric names
intervalAggregationIntervalYesTime bucket interval
aggregationAggregationTypeYesAggregation for each bucket
distinctColumnMetricDistinctColumnFor count_distinctColumn whose distinct values are counted
filtersMetricsFilterNoShared metric filters
groupBystring[]NoFields used to split the result into series

Returns
Direct link to Returns

{
series: Array<{
name: string
costUnit?: string | null
points: Array<{
timestamp: Date
value: number
estimatedCost?: number | null
}>
}>
}
const result = await client.getMetricTimeSeries({
name: ['mastra_model_total_input_tokens'],
interval: '1h',
aggregation: 'sum',
filters: {
timestamp: { start: new Date(Date.now() - 24 * 60 * 60 * 1000) },
},
})

HTTP: POST /api/observability/metrics/timeseries

getMetricPercentiles(args)
Direct link to getmetricpercentilesargs

Calculates percentile values in time buckets. The observability store and MastraClient expose this method.

Arguments
Direct link to Arguments

FieldTypeRequiredDescription
namestringYesOne metric name
percentilesnumber[]YesOne or more values from 0 through 1
intervalAggregationIntervalYesTime bucket interval
filtersMetricsFilterNoShared metric filters

Returns
Direct link to Returns

{
series: Array<{
percentile: number
points: Array<{
timestamp: Date
value: number
}>
}>
}
const result = await client.getMetricPercentiles({
name: 'mastra_agent_duration_ms',
percentiles: [0.5, 0.95, 0.99],
interval: '1h',
})

HTTP: POST /api/observability/metrics/percentiles

Raw metric records
Direct link to Raw metric records

listMetrics(args)
Direct link to listmetricsargs

Returns stored metric observations without aggregating them. This method is available on the observability store. The HTTP route accepts the same fields as query parameters.

Arguments
Direct link to Arguments

Page mode is the default:

{
mode?: 'page'
filters?: MetricsFilter
pagination?: {
page?: number // Default: 0
perPage?: number // Default: 10; maximum: 100
}
orderBy?: {
field?: 'timestamp' // Default: 'timestamp'
direction?: 'ASC' | 'DESC' // Default: 'DESC'
}
}

Delta mode supports incremental polling:

{
mode: 'delta'
filters?: MetricsFilter
after?: string
limit?: number // Default: 10; maximum: 100
}

pagination and orderBy aren't allowed in delta mode. after and limit aren't allowed in page mode. A backend that doesn't support delta polling returns an unsupported-operation error.

Returns
Direct link to Returns

{
metrics: MetricRecord[]
pagination?: {
total: number
page: number
perPage: number | false
hasMore: boolean
}
delta?: {
limit: number
hasMore: boolean
}
deltaCursor?: string
}

A MetricRecord has this shape:

{
metricId?: string | null
timestamp: Date
name: string
value: number
traceId?: string | null
spanId?: string | null
entityType?: EntityType | null
entityId?: string | null
entityName?: string | null
parentEntityType?: EntityType | null
parentEntityId?: string | null
parentEntityName?: string | null
rootEntityType?: EntityType | null
rootEntityId?: string | null
rootEntityName?: string | null
userId?: string | null
organizationId?: string | null
resourceId?: string | null
runId?: string | null
sessionId?: string | null
threadId?: string | null
requestId?: string | null
environment?: string | null
serviceName?: string | null
scope?: Record<string, unknown> | null
entityVersionId?: string | null
parentEntityVersionId?: string | null
rootEntityVersionId?: string | null
experimentId?: string | null
executionSource?: string | null
tags?: string[] | null
source?: string | null // Deprecated
provider?: string | null
model?: string | null
estimatedCost?: number | null
costUnit?: string | null
costMetadata?: Record<string, unknown> | null
labels: Record<string, string>
metadata?: Record<string, unknown> | null
}

HTTP: GET /api/observability/metrics

Metric discovery
Direct link to Metric discovery

The observability store and MastraClient expose the metric discovery methods. HTTP requests pass arguments as query parameters.

MethodArgumentsReturnsHTTP route
getMetricNames(args?){ prefix?: string; limit?: number }{ names: string[] }GET /api/observability/discovery/metric-names
getMetricLabelKeys(args){ metricName: string }{ keys: string[] }GET /api/observability/discovery/metric-label-keys
getMetricLabelValues(args){ metricName: string; labelKey: string; prefix?: string; limit?: number }{ values: string[] }GET /api/observability/discovery/metric-label-values

limit must be a positive integer when provided.

const { names } = await client.getMetricNames({ prefix: 'mastra_model_' })
const { keys } = await client.getMetricLabelKeys({ metricName: names[0] })
const { values } = await client.getMetricLabelValues({
metricName: names[0],
labelKey: keys[0],
limit: 20,
})

The observability discovery API also exposes shared dimensions used by traces, logs, and metrics:

Store or client methodArgumentsHTTP route
getEntityTypes()None in MastraClient; {} in the storeGET /api/observability/discovery/entity-types
getEntityNames(args?){ entityType?: EntityType }GET /api/observability/discovery/entity-names
getServiceNames()None in MastraClient; {} in the storeGET /api/observability/discovery/service-names
getEnvironments()None in MastraClient; {} in the storeGET /api/observability/discovery/environments
getTags(args?){ entityType?: EntityType }GET /api/observability/discovery/tags

HTTP routes
Direct link to HTTP routes

MethodRouteInput
GET/api/observability/metricsQuery parameters
POST/api/observability/metrics/aggregateJSON body
POST/api/observability/metrics/breakdownJSON body
POST/api/observability/metrics/timeseriesJSON body
POST/api/observability/metrics/percentilesJSON body
GET/api/observability/discovery/metric-namesQuery parameters
GET/api/observability/discovery/metric-label-keysQuery parameters
GET/api/observability/discovery/metric-label-valuesQuery parameters
GET/api/observability/discovery/entity-typesNone
GET/api/observability/discovery/entity-namesQuery parameters
GET/api/observability/discovery/service-namesNone
GET/api/observability/discovery/environmentsNone
GET/api/observability/discovery/tagsQuery parameters

All routes require a configured observability domain. The aggregate, breakdown, time-series, and percentile routes require the observability:read permission when runtime authorization is enabled.

Unsupported backends
Direct link to Unsupported backends

The base observability storage implementation throws *_NOT_IMPLEMENTED errors for raw listing, analytics, and discovery methods. A configured observability domain can therefore exist while its backend doesn't implement metric queries.

DuckDB, ClickHouse, Postgres v-next observability storage, and in-memory observability storage implement metric queries. Google Cloud Spanner implements them only when disableMetrics is false. Metrics are disabled by default. Other storage adapters may support tracing without supporting metrics.

CLI
Direct link to CLI

The mastra api metric commands cover aggregate, breakdown, time-series, percentile, metric-name, label-key, and label-value queries. See the mastra api metric CLI reference for commands, targeting, authentication, and schema inspection.