Datasets

What to send the QC Analytical Results API: precision (duplicate pairs) and accuracy (CRMs and blanks) datasets, their columns, the formats, and the limits.

A dataset is your QC results, sent once and asked about many times. There are two kinds, matching the app’s two sheets:

Kind What it holds Charts
precision Duplicate pairs: Sample 1 and Sample 2 of each pair, one row per analyte per pair The eleven precision charts
accuracy CRM and blank results, one row per analyte per result The seven CRM and blank charts

A chart asked of the wrong kind answers 422 wrong_dataset_kind.

Columns

Column names are matched ignoring case and spaces around them. Extra columns are ignored. These are the ones the charts use, as the app’s export template names them.

Precision (duplicate pairs)

Column What Required
LABCODE Laboratory code, e.g. ALS yes
DESPATCHNO Despatch (batch) number yes
LABJOBNO Laboratory job number yes
ASSAYNAME Assay name: element, method and unit, e.g. Au_AA25_ppm (or ELEMENT, METHOD and UNIT columns, which the API joins the same way) yes
CHECKSTAGE_CK The check sample’s stage, e.g. Pulverising Stage, Crushing Stage, Field Prep Stage yes
SENDDATE Despatch send date yes
RETURNDATE Laboratory return date yes
ASSAYVALUE_OR Sample 1’s value (the first of the pair) yes
ASSAYVALUE_CK Sample 2’s value (the second of the pair) yes
LOWERDETECTION The method’s lower detection limit for this analyte yes
ORIG_SAMPLE Sample 1’s id yes
CHECK_SAMPLE Sample 2’s id yes
UPPERDETECTION Upper detection limit (for apply_upper_detection) no
ANALYSISSUITE_CK Analysis suite (for the suite parameter) no

Accuracy (CRMs and blanks)

Column What Required
LABCODE Laboratory code yes
DESPATCHNO Despatch (batch) number yes
LABJOBNO Laboratory job number yes
ASSAYNAME Assay name, as above yes
STANDARDID The CRM or blank’s id, e.g. ALS-26 yes
STANDARDTYPE Its type, e.g. COMPANY_STD, LAB_STD, COMPANY_BLANK; the blanks chart takes a blank type yes
CHECKID The result’s own id (one per CRM result): the default x axis yes
SENDDATE Despatch send date yes
RETURNDATE Laboratory return date yes
ASSAYVALUE The result yes
LOWERDETECTION Lower detection limit yes
STANDARDVALUE The certified value (for use_certified_values); may be blank yes (the column)
STANDARDDEVIATION The certified standard deviation; may be blank yes (the column)
UPPERDETECTION Upper detection limit no
ANALYSISSUITE Analysis suite no

Values

  • Send every value as text, as it is in your database. <0.01, -0.01 and the like are read as below detection, and below-detection results are never plotted or counted, except on the blanks chart, where they are kept at half the detection limit (see How this is calculated).
  • Dates in any usual form. Day-first and month-first are decided once per dataset from the dates themselves, so keep to one form.
  • NULL (as some database exports write it) is read as blank.

Formats

JSON: {"kind": "precision", "rows": [{...}, {...}]}, one object per row, Content-Type: application/json.

CSV: the file as it is, Content-Type: text/csv, with ?kind=precision or ?kind=accuracy on the address. The first line is the column names.

Either may be gzipped: add Content-Encoding: gzip. A 50 MB CSV is a few MB gzipped and goes in a second or two.

What comes back

{
  "dataset_id": "ds_bbd34394c3dd10abfeebe84d",
  "kind": "accuracy",
  "rows": 39919,
  "created_at": "2026-10-22T06:10:00Z",
  "expires_at": "2026-10-22T07:10:00Z",
  "facets": {
    "labs": ["ALS"],
    "analytes": ["Ag_MEICP41s_ppm", "..."],
    "suites": ["Expl-Core", "..."],
    "crm_types": ["COMPANY_BLANK", "COMPANY_STD", "LAB_STD"],
    "crms": ["A0029", "ALS-26", "G398-4c", "..."],
    "despatches": 174,
    "dates": {"return": {"from": "2022-11-16", "to": "2025-11-12"}, "send": {"from": "2022-10-29", "to": "2025-10-19"}}
  }
}

rows is the rows kept after tidying. The facets are the exact values the charts take: labs and analytes for every chart; stages (precision) for stage; crm_types and crms (accuracy) for crm_type and crms; suites for suite. dates is the span of each date column, for date_from and date_to.

GET /v1/datasets/{id} gives the same again, and DELETE /v1/datasets/{id} drops it now.

Limits

Rows in one dataset 500,000
Rows across all your datasets at once 1,000,000 (the least recently used dataset is dropped to make room)
One request 50 MB
A dataset is kept one hour after its last use (each call with it starts the hour again)
The same file again the same dataset: the id you already have
Calls 300 a minute per key

A dataset that has gone answers 410 dataset_expired: send it again. A client that sends its data at the start of each run, and again on a 410, needs nothing else.

Privacy

A dataset is held in memory on the API server that received it (in London or Frankfurt), only for the hour, and is never written to disk. If that server restarts, the dataset is gone and your next call about it gets 410 dataset_expired: send it again. The log of each call holds its request id, your key’s name, what was asked (chart and parameters), its status and how long it took: never the data, never the key. See the Privacy notice.