Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.anomalyarmor.ai/llms.txt

Use this file to discover all available pages before exploring further.

The Metrics API enables programmatic management of data quality metrics. Use it to track trends in your data over time, detect anomalies, and integrate quality monitoring into your pipelines.

Endpoints

MethodEndpointDescription
GET/api/v1/sdk/metrics/{asset_id}/summaryGet metrics summary for an asset
GET/api/v1/sdk/metrics/{asset_id}List metrics for an asset
GET/api/v1/sdk/metrics/{asset_id}/{metric_id}Get metric details
POST/api/v1/sdk/metrics/{asset_id}Create a new metric
PATCH/api/v1/sdk/metrics/{asset_id}/{metric_id}Update a metric
DELETE/api/v1/sdk/metrics/{asset_id}/{metric_id}Delete a metric
POST/api/v1/sdk/metrics/{asset_id}/{metric_id}/captureTrigger metric capture
GET/api/v1/sdk/metrics/{asset_id}/{metric_id}/snapshotsList metric snapshots

Metric Types

TypeDescriptionRequires Column
row_countTotal row count of the tableNo
null_percentPercentage of null valuesYes
distinct_countCount of distinct valuesYes
duplicate_countCount of duplicate valuesYes
min_valueMinimum numeric valueYes
max_valueMaximum numeric valueYes
meanAverage numeric valueYes
percentilePercentile value (requires percentile_value)Yes

Get Metrics Summary

GET /api/v1/sdk/metrics/{asset_id}/summary
Returns aggregate metrics statistics for an asset.
curl -H "Authorization: Bearer aa_live_xxx" \
  "https://api.anomalyarmor.ai/api/v1/sdk/metrics/550e8400-e29b-41d4-a716-446655440000/summary"

Response

{
  "data": {
    "total_metrics": 15,
    "active_metrics": 12,
    "total_checks": 8,
    "passing": 6,
    "failing": 1,
    "warning": 1,
    "error": 0,
    "health_percentage": 87.5
  }
}

List Metrics

GET /api/v1/sdk/metrics/{asset_id}

Query Parameters

ParameterTypeDefaultDescription
metric_typestring-Filter by type (e.g., row_count, null_percent)
is_activeboolean-Filter by active status
limitinteger50Max results (max: 100)
offsetinteger0Results to skip
curl -H "Authorization: Bearer aa_live_xxx" \
  "https://api.anomalyarmor.ai/api/v1/sdk/metrics/550e8400-e29b-41d4-a716-446655440000?metric_type=null_percent&limit=10"

Response

{
  "data": {
    "items": [
      {
        "id": "m_550e8400-e29b-41d4-a716-446655440001",
        "internal_id": 123,
        "asset_id": 456,
        "table_path": "snowflake.prod.warehouse.orders",
        "column_name": "customer_email",
        "metric_type": "null_percent",
        "capture_interval": "daily",
        "sensitivity": 3,
        "is_active": true,
        "created_at": "2026-01-01T10:00:00Z"
      }
    ]
  },
  "pagination": {
    "total": 15,
    "limit": 50,
    "offset": 0,
    "has_more": false
  }
}

Get Metric Details

GET /api/v1/sdk/metrics/{asset_id}/{metric_id}

Query Parameters

ParameterTypeDefaultDescription
include_snapshotsbooleantrueInclude recent snapshots
snapshot_limitinteger30Max snapshots to include
curl -H "Authorization: Bearer aa_live_xxx" \
  "https://api.anomalyarmor.ai/api/v1/sdk/metrics/550e8400-e29b-41d4-a716-446655440000/m_550e8400-e29b-41d4-a716-446655440001?include_snapshots=true&snapshot_limit=10"

Response

{
  "data": {
    "id": "m_550e8400-e29b-41d4-a716-446655440001",
    "internal_id": 123,
    "asset_id": 456,
    "table_path": "snowflake.prod.warehouse.orders",
    "column_name": "customer_email",
    "metric_type": "null_percent",
    "capture_interval": "daily",
    "sensitivity": 3,
    "is_active": true,
    "created_at": "2026-01-01T10:00:00Z"
  }
}

Create Metric

POST /api/v1/sdk/metrics/{asset_id}
Requires read-write or admin scope.

Request Body

FieldTypeRequiredDescription
metric_typestringYesMetric type (see table above)
table_pathstringYesFull table path (catalog.schema.table)
column_namestringFor column metricsColumn name
capture_intervalstringNohourly, daily, weekly (default: daily)
sensitivityfloatNoAnomaly detection sensitivity (default: 1.0)
group_by_columnsarrayNoColumns to group by
percentile_valuefloatNoPercentile value (for percentile type)
curl -X POST -H "Authorization: Bearer aa_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "metric_type": "null_percent",
    "table_path": "snowflake.prod.warehouse.orders",
    "column_name": "customer_email",
    "capture_interval": "daily"
  }' \
  "https://api.anomalyarmor.ai/api/v1/sdk/metrics/550e8400-e29b-41d4-a716-446655440000"

Response

{
  "data": {
    "id": "m_550e8400-e29b-41d4-a716-446655440001",
    "internal_id": 123,
    "asset_id": 456,
    "table_path": "snowflake.prod.warehouse.orders",
    "column_name": "customer_email",
    "metric_type": "null_percent",
    "capture_interval": "daily",
    "sensitivity": 1.0,
    "is_active": true,
    "created_at": "2026-01-04T10:30:00Z"
  }
}

Update Metric

PATCH /api/v1/sdk/metrics/{asset_id}/{metric_id}
Requires read-write or admin scope.

Request Body

FieldTypeDescription
is_activebooleanWhether metric is active
capture_intervalstringCapture interval
sensitivityfloatAnomaly detection sensitivity
curl -X PATCH -H "Authorization: Bearer aa_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"is_active": false}' \
  "https://api.anomalyarmor.ai/api/v1/sdk/metrics/550e8400-e29b-41d4-a716-446655440000/m_550e8400-e29b-41d4-a716-446655440001"

Delete Metric

DELETE /api/v1/sdk/metrics/{asset_id}/{metric_id}
Requires read-write or admin scope.
curl -X DELETE -H "Authorization: Bearer aa_live_xxx" \
  "https://api.anomalyarmor.ai/api/v1/sdk/metrics/550e8400-e29b-41d4-a716-446655440000/m_550e8400-e29b-41d4-a716-446655440001"

Response

{
  "data": {
    "success": true,
    "message": "Metric deleted"
  }
}

Trigger Metric Capture

POST /api/v1/sdk/metrics/{asset_id}/{metric_id}/capture
Requires read-write or admin scope.
Triggers an immediate capture of the metric value.
curl -X POST -H "Authorization: Bearer aa_live_xxx" \
  "https://api.anomalyarmor.ai/api/v1/sdk/metrics/550e8400-e29b-41d4-a716-446655440000/m_550e8400-e29b-41d4-a716-446655440001/capture"

Response

{
  "data": {
    "snapshot_count": 1,
    "snapshots": [
      {
        "id": 789,
        "value": 2.5,
        "captured_at": "2026-01-04T10:35:00Z",
        "is_anomaly": false,
        "status": "PASS"
      }
    ]
  }
}

List Metric Snapshots

GET /api/v1/sdk/metrics/{asset_id}/{metric_id}/snapshots

Query Parameters

ParameterTypeDefaultDescription
limitinteger100Max results
offsetinteger0Results to skip
curl -H "Authorization: Bearer aa_live_xxx" \
  "https://api.anomalyarmor.ai/api/v1/sdk/metrics/550e8400-e29b-41d4-a716-446655440000/m_550e8400-e29b-41d4-a716-446655440001/snapshots?limit=30"

Response

{
  "data": {
    "items": [
      {
        "id": 789,
        "metric_definition_id": 123,
        "value": 2.5,
        "captured_at": "2026-01-04T10:35:00Z",
        "is_anomaly": false,
        "z_score": 0.3,
        "status": "PASS"
      },
      {
        "id": 788,
        "metric_definition_id": 123,
        "value": 15.2,
        "captured_at": "2026-01-03T10:35:00Z",
        "is_anomaly": true,
        "z_score": 4.2,
        "status": "FAIL"
      }
    ]
  },
  "pagination": {
    "total": 90,
    "limit": 30,
    "offset": 0,
    "has_more": true
  }
}
Track daily row counts to detect unexpected data volume changes:
from anomalyarmor import Client

client = Client()
asset_id = "550e8400-e29b-41d4-a716-446655440000"

# Create row count metric
metric = client.metrics.create(
    asset_id,
    metric_type="row_count",
    table_path="snowflake.prod.warehouse.orders",
    capture_interval="daily",
    sensitivity=2.0,  # Alert on 2+ standard deviations
)

# Trigger initial capture
result = client.metrics.capture(asset_id, metric.id)
print(f"Initial row count: {result['snapshots'][0]['value']}")

# Later: check for anomalies
snapshots = client.metrics.snapshots(asset_id, metric.id, limit=7)
anomalies = [s for s in snapshots if s.is_anomaly]
if anomalies:
    print(f"Found {len(anomalies)} anomalies in the last 7 captures")

Error Responses

Metric Not Found (404)

{
  "error": {
    "code": "METRIC_NOT_FOUND",
    "message": "Metric not found",
    "details": {"metric_id": "m_invalid-uuid"}
  }
}

Validation Error (400)

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "column_name is required for null_percent metrics",
    "details": {"field": "column_name", "metric_type": "null_percent"}
  }
}

Forbidden (403)

{
  "error": {
    "code": "FORBIDDEN",
    "message": "Insufficient permissions. Required scope: read-write",
    "details": {"current_scope": "read-only", "required_scope": "read-write"}
  }
}

Common Questions

What’s the difference between a metric and a validity rule?

Metrics track numeric values over time (row counts, null percentages, mean, percentile) and alert on statistical anomalies via the sensitivity parameter. Validity rules (Validity API) enforce deterministic pass/fail constraints like NOT NULL, REGEX, or RANGE. Use metrics to catch drift, validity to catch explicit contract violations.

How does the sensitivity parameter affect anomaly detection?

Sensitivity is the z-score threshold for flagging a snapshot as anomalous, defaulting to 1.0. Raise it (e.g. 2.0 or 3.0) to reduce false positives on noisy data, lower it to catch subtler shifts. Each captured snapshot returns z_score and is_anomaly so you can tune in production.

Can I capture a metric on demand outside its scheduled interval?

Yes. POST /api/v1/sdk/metrics/{asset_id}/{metric_id}/capture (or client.metrics.capture(...)) triggers an immediate capture and returns the new snapshot with anomaly status. This is useful for backfilling after creating a metric and for post-deploy validation without waiting for the next scheduled run.

Which metric types require a column_name?

Column-level types (null_percent, distinct_count, duplicate_count, min_value, max_value, mean, percentile) require column_name. row_count operates on the whole table and ignores column_name. percentile additionally needs percentile_value in the request body.