Skip to content

Documentation

Quality vocabulary

GET /api/v1/quality/vocabulary

Return 12 kinds, parameter labels, safe formats, and exact form constraints.

Access

Use a Bearer API key for the active workspace and runs.read permission. Writes also require a writable key and sites.write on the selected site. Only a platform operator with settings.manage, not an external key, may change platform-wide definitions. Sites and history are scoped to the current team. An agent rejects POST/PATCH/DELETE; edit definitions in the cloud.

Parameters

Name In Required Description
locale query no Language of vocabulary, suggestions, and explanations. If omitted, akmelo_locale cookie is used, otherwise ru. User-defined field names are not translated.

No body is required. locale=ru|en selects labels and explanations; without an explicit parameter the akmelo_locale cookie is used, then Russian. Kind codes and field names are not translated.

Response 200

This is a shortened example with placeholder numeric IDs; full fields are documented below and in the downloadable OpenAPI schema.

{
  "kinds": [
    {
      "value": "field_fill_rate",
      "label": "Field completeness",
      "hint": "Report when too many products have no price.",
      "skip_hint": "Saved records are required.",
      "params": [
        {
          "key": "field",
          "label": "Field",
          "type": "field",
          "required": true,
          "default": ""
        },
        {
          "key": "max_empty_percent",
          "label": "Allowed empty values, %",
          "type": "number",
          "min": 0,
          "max": 100,
          "default": 30,
          "required": false
        }
      ]
    }
  ],
  "actions": [
    {
      "value": "warn",
      "label": "Notify only"
    }
  ],
  "severities": [
    {
      "value": "warning",
      "label": "Warning"
    }
  ],
  "formats": {
    "url": "Absolute HTTP or HTTPS link",
    "email": "Email address",
    "phone": "Phone number",
    "inn": "Russian tax ID",
    "gtin": "GTIN, EAN, or UPC product code"
  }
}
Field Meaning
kinds[] Stable value, human-readable label, hint, skip_hint, next_step, and params[] schema
actions[] warn and mark_suspicious, with translated labels
severities[] Finding severity levels
formats Labels for the five supported formats

All kinds and parameters

kind Parameters and initial defaults Finding condition
row_count_drop drop_percent=50, min_previous=10 Record count decreased by at least the threshold
row_count_growth growth_percent=100, min_previous=10 Record count increased by at least the threshold
row_count_min min_count=1 Fewer records saved than the minimum
invalid_share max_invalid_percent=50, min_items=1 Rejected-record share exceeds the allowance
field_fill_rate field, max_empty_percent=30 Empty-value share exceeds the allowance
field_numeric_range field, min_value=0, max_value=null, max_bad_percent=5 A filled value is not a number or falls outside inclusive bounds
field_format field, format=url, max_bad_percent=5 A filled value does not match the format
field_allowed_values field, allowed_values, max_bad_percent=5 A value is absent from the allowed list
field_length field, min_length=3, max_length=null, max_bad_percent=5 Filled-string character count falls outside inclusive bounds
field_duplicate_share field, max_duplicate_percent=0 Extra repeats beyond the first exceed the allowance
field_change_rate field, max_changed_percent=50, min_items=10 The share of records with a logged field change exceeds the allowance
field_freshness field, max_age_hours=48, max_bad_percent=5 A date is too old, invalid, or over five minutes in the future

field is the code of a declared field on the selected site, not its display name. allowed_values contains 1 to 100 distinct strings, up to 128 characters each, and is case-sensitive. A, A, B is one repeat among three values. JSON/collection field changes include nested paths such as stock.warehouse.qty; a different field such as stockroom is excluded. A new record is not a changed record.

Share percentages accept 0 to 100, drop_percent 1 to 100, and growth_percent 1 to 100000. Counts are integers from 1 to 1000000000. Numeric bounds accept -1000000000000000 to 1000000000000000; length bounds accept a minimum of 0 or a maximum of 1 through 1000000; max_age_hours accepts 0.25 to 87600. Use the returned kinds[].params as the exact form contract, including required, nullable, integer, options, min, max, and default.

min_value, max_value, min_length, and max_length accept explicit null to remove that bound. At least one bound of each pair is required. Other parameters reject null; omitted optional values receive safe defaults. PATCH replaces supplied params, so omitted optional bounds are removed. Numeric strings retain precision; send precise fractional thresholds as strings, for example "7.0000000000000001".

Formats: url checks an absolute HTTP/HTTPS URL; email checks address syntax; phone accepts 10 to 15 digits with permitted separators; inn checks Russian taxpayer-number checksums; gtin checks GTIN/EAN/UPC check digits. Checks do not open links or look up phone numbers and addresses with external services. Custom regular expressions are not accepted.

Behavior and limits

The kinds example is shortened; the service returns all 12 kinds. Select by stable value, not a translated label. Respect parameter type, required, min, max, integer, options, and default.

Rules apply to future successful complete collections. They do not alter old data or fix scenario code. Field checks use up to 5,000 records from the current collection and explicitly report a limited sample; except for completeness checks, empty values are excluded. passed, breached, and skipped are distinct, and no finding does not prove success.

Numeric examples retain the legacy v1 contract. Use X-Identifier-Format: uuid or the same method under /api/v2 for public UUIDs, including nested site, rule, and collection references. preset_id, category_id, and field remain string codes. Identifier formats.

With budget_truncated=true, the check is skipped and measurements are partial. Reaching a shared budget is not reported as a successful check.

Coverage and skipped checks

Before json_decode, JSON complexity and depth are checked: 200000 nodes and 16 MiB estimated decoded structure overall, 4 MiB per row, and 24 levels. Brackets inside strings are not nodes. Remaining PHP memory is checked before each PDO batch and decode, with an 8 MiB reserve. Additional skip reasons are sample_structure_exceeded, sample_depth_exceeded, sample_decode_error, and sample_memory_exceeded. A first row that is too complex is a budget skip, not no_records. Measurements before stopping are partial.

Only successful complete normal collections are checked. Trials, partial, failed, and cancelled collections are excluded. Continuations or retries after an interruption receive skipped / continued_run. Volume comparisons require an earlier successful complete collection from the same site and scenario that is not flagged for review; otherwise the reason is no_baseline, or baseline_too_small when its volume is too low.

Volume counters cover the full collection. Field checks use the first 5000 active records from this collection, ordered by internal ID. This is a bounded sample, not a random estimate of the whole catalog. Shared limits are 32 distinct fields, 100000 cells, and 8 MiB of raw data; an individual value over 8 KiB is not loaded into memory. sample_limited, sample_size, sample_limit, budget_truncated, budget_bytes, byte_limit, cell_limit, field_limit, and oversized_skipped describe coverage. Reaching a shared budget produces skipped / sample_budget_exceeded; partial observed_status is not a final pass or finding. Fields beyond the field limit receive field_budget_exceeded.

Empty means null, an empty string, Unicode-whitespace-only text, or an empty array. 0 and false are filled. Only field_fill_rate treats emptiness as a violation. Other kinds exclude empty values from their denominator and return no_filled_values when no filled values remain. Other skip reasons are no_records, field_missing, values_too_large, too_few_records, unsupported_kind, and check_error. A check failure does not turn an already saved successful collection into a failed one.

Shares breach strictly above the allowance: exactly 7 of 100 at 7% passes. Volume drops and growth include the threshold: 100 → 90 at a 10% threshold creates a finding. Comparisons are exact; only the displayed percentage is rounded. Freshness is measured against run.finished_at; dates with no time zone are UTC.

See Data quality for all 24 template examples and the interface workflow. For parameter definitions, see the vocabulary.

Send the request

Set BASE to your installation URL without a trailing slash and API_KEY to a secret stored on your server. Client setup and browser JavaScript.

curl

curl --fail-with-body -X GET "$BASE/api/v1/quality/vocabulary?locale=en" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Accept: application/json"

Node.js · server

// Node.js server example. Save as request.mjs.
const response = await fetch(process.env.BASE + "/api/v1/quality/vocabulary?locale=en", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.API_KEY}`,
    Accept: "application/json"
  },
  signal: AbortSignal.timeout(120000)
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
console.log(await response.json());

Python

import json
import os
import urllib.request
import urllib.error

request = urllib.request.Request(
    os.environ["BASE"] + "/api/v1/quality/vocabulary?locale=en",
    method="GET",
    headers={"Authorization": "Bearer " + os.environ["API_KEY"],
             "Accept": "application/json"},
)
try:
    with urllib.request.urlopen(request, timeout=120) as response:
        print(json.load(response))
except urllib.error.HTTPError as error:
    print(error.code, error.read().decode("utf-8", errors="replace"))
    raise

PHP · external client

<?php
$handle = curl_init(getenv("BASE") . "/api/v1/quality/vocabulary?locale=en");
curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 120,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("API_KEY"),
        "Accept: application/json",
    ],
]);
$content = curl_exec($handle);
$status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
if ($content === false) {
    throw new RuntimeException(curl_error($handle));
}
curl_close($handle);
if ($status < 200 || $status >= 300) {
    throw new RuntimeException("HTTP $status: $content");
}
print_r(json_decode($content, true, 512, JSON_THROW_ON_ERROR));

Go

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

func main() {
    request, err := http.NewRequest("GET", os.Getenv("BASE") + "/api/v1/quality/vocabulary?locale=en", nil)
    if err != nil { panic(err) }
    request.Header.Set("Authorization", "Bearer " + os.Getenv("API_KEY"))
    request.Header.Set("Accept", "application/json")
    client := &http.Client{Timeout: 120 * time.Second}
    response, err := client.Do(request)
    if err != nil { panic(err) }
    defer response.Body.Close()
    content, err := io.ReadAll(response.Body)
    if err != nil { panic(err) }
    if response.StatusCode < 200 || response.StatusCode >= 300 {
        panic(fmt.Sprintf("HTTP %d: %s", response.StatusCode, content))
    }
    fmt.Println(string(content))
}

If the request fails

401: check the key and its expiry. 403: check permissions, read/write scope, and 2FA. 404: check the ID and workspace. 422: check fields and resource state. 429: respect Retry-After. Errors and retries.