Scroll down to learn more

Documentation Center

Welcome to Verity Documentation Center. You find here all the content you need to enjoy your data.

Search Results for

    Show / Hide Table of Contents

    Querying the API

    The Swagger definition lists every endpoint and every parameter, but it cannot show how they fit together. This page covers what an integration actually needs: how the resources relate, how filters combine, and how to read data points without pulling more than necessary.

    Everything below assumes you already have a token — see Connect to the API.

    The entity chain

    The whole API is organised around one chain. Understanding it is what turns a list of endpoints into a mental model.

    The entity chain and the identifiers that address each level

    Level Endpoint What it represents
    Account /accounts The tenant. Everything else lives inside one account.
    Site /sites A physical place — a building, a plant, a delivery point.
    Source /sources Something that produces data at that site: a meter, a gateway, a file feed.
    Variable /variables One measured quantity of that source: an index, a power, a temperature.
    Data points /data The timestamped values held by a variable.

    Two consequences shape every integration:

    • A data point is always reached through its variable. There is no endpoint that returns values without going through /variables or /data filters. To read a meter you first need its variable id — or a filter that identifies it.
    • The chain must exist before the data does. Sites, sources and variables are created first, through the API or through the interface. Data arriving for a structure that does not exist is discarded.

    Business identifiers

    Beyond the internal Id, sources and variables carry identifiers you control. They exist precisely so that an external system can address Insights entities using its own vocabulary, without storing our internal ids.

    Identifier On Typical use
    SerialNumber Source The manufacturer or asset serial number.
    Ean Source The EAN / delivery-point code.
    MeterNumber Source The meter number used by the operator.
    Name Source, Variable Human-readable name.
    MappingConfig Variable The key used by the push service to pick a variable inside a source.
    Tags Source, Variable Free labels, filterable directly.
    Tip

    Populate at least one business identifier when you create a source. An integration that resolves entities by SerialNumber or Ean survives an environment change; one that hard-codes internal ids does not.

    Conventions shared by every endpoint

    The collection endpoints — /sites, /sources, /variables, /data — follow the same rules.

    Filters combine with AND

    Every filter you add narrows the result further. GET /sources?SiteId=42&State=Enabled returns the enabled sources of site 42, never the union of both conditions.

    Singular and plural parameters

    Most filters exist in two forms: SourceId for one value, SourceIds for several. The plural form is repeated in the query string, once per value:

    GET /data?VariableIds=101&VariableIds=102&VariableIds=103
    
    Important

    A URL has a practical length limit. Past a few dozen ids the request starts failing at the infrastructure level, before it ever reaches the API. Batch the ids — around 80 per call is a safe working figure — or switch to POST /data.

    DisplayLevel: how much of each object you get back

    DisplayLevel decides how detailed each returned object is. Asking for more than you need is the easiest way to make a fast endpoint slow.

    Endpoint Values
    /sites SiteLight, Site, VerboseSite
    /sources Light, Normal, Verbose, Site
    /variables Normal, Verbose
    /data Value, ValueVariable, ValueVariableDate, ValueVariableDateSource

    On /data the meaning is different and worth stating plainly: it does not control verbosity, it chooses which discriminant columns accompany the values. Each level adds one:

    DisplayLevel Columns next to the values
    Value The value fields only.
    ValueVariable + variableId
    ValueVariableDate + variableId, date
    ValueVariableDateSource + variableId, date, sourceId
    Important

    Querying several variables with DisplayLevel=Value returns values you cannot attribute to anything: no variable, no timestamp. Unless you are reading a single aggregate for a single variable, use ValueVariableDateSource.

    Paging

    /sites, /sources and /data accept Paging.PageNumber (0-based) and Paging.ItemsPerPage. On /data, PagingOrder also sets the direction, ASC or DESC.

    Selecting by view, by tag or by custom filter

    Three parameters go beyond the fixed list of filters, and they are worth knowing before writing a filter of your own.

    Parameter On What it does
    ViewId /sites, /sources Reuses a view already defined in the interface. The selection logic stays in Insights, and your code only carries an id.
    Tags /sources, /variables Matches the free labels set on the entity. The simplest way to mark a subset for an integration.
    CustomFilter /sites, /sources An expression reaching into fields no dedicated parameter covers, including your own form fields.
    OdmSqlFilter /sources Selection expressed against the Open Data Model.
    Tip

    Prefer ViewId or Tags when they can express the need. Both are visible and editable in the interface, so the selection can be adjusted without a deployment — a CustomFilter is buried in your source code.

    CustomFilter expressions

    CustomFilter has a full grammar of its own, documented — with its operators, its type rules and a BNF definition — in Custom filter query syntax. It is the same syntax as the source search box in the Insights portal.

    Two common forms:

    Name like acme
    

    matches on a standard field of the entity, and:

    'Informations'.'General'.'SalesForceId' = '0011t00000ABCDE'
    

    walks into a form field, quoting each level of the path — here the field SalesForceId, inside the group General, of the form Informations.

    To build the path of one of your own fields, open the form in the interface and read the levels from the outside in: form, then group, then field.

    Beyond equality, the grammar supports <>, >, >=, <, <=, like, startswith, endswith and their negations, criteria combined with boolean operators, searches on empty properties, and filtering on a foreign entity. All of it is in Custom filter query syntax.

    Tip

    Whatever the expression, assert on the result. A CustomFilter that returns zero rows where you expected one is the failure mode to guard against, and it looks exactly like "no data" to the caller.

    Reading data points

    GET /data is the endpoint most integrations spend their time in. Every query answers the same four questions, and it is worth composing them in that order.

    The four groups of parameters of a data query

    1 — Selection: which variables

    Parameter Notes
    VariableId / VariableIds The most direct selection.
    SourceId / SourceIds Every variable of those sources.
    VariableTypeId / VariableTypeIds Every variable of a given type, across sources.
    AccountId Administrators only. For everyone else the account comes from the token.

    2 — Period: over what window

    Parameter Notes
    From Start of the range, included.
    To End of the range — included only if IncludeToBoundary is set.
    IncludeToBoundary Off by default. This is the classic off-by-one: without it, midnight of the last day is excluded.
    TimeZoneId Interpret and return dates in this time zone (BCL or IANA name).
    UseReportingTimezone Interpret and return dates in the time zone of the variable's parent site.

    Golden rule

    Decide the time zone explicitly, on every data query.

    From and To are read in the zone set by TimeZoneId or UseReportingTimezone. Leave both unset and a daily total silently shifts by the site's UTC offset — a bug that surfaces months later, on a summer/winter boundary. The two parameters are mutually exclusive: set one, never both.

    3 — Shape: at what resolution

    Granularity groups the raw points into buckets; Aggregation reduces each bucket to a single number.

    How granularity and aggregation combine

    Parameter Values
    Granularity Raw, Minute, Hour, Day, Week, Month, Year, All
    Aggregation None, SUM, MIN, MAX, AVG, COUNT, VAR, STDEV
    Aggregations Several at once, in one call.
    TargetUnits, UnitTransformation Convert values into the first compatible target unit.
    Important

    Granularity=Raw and an aggregation are mutually exclusive — raw means "no aggregation". Granularity=All collapses the whole window into one row per variable, which is what you want for a total or a count.

    4 — Delivery: how the result comes back

    The response is a JSON array. Each row carries the discriminant columns chosen by DisplayLevel, plus the value fields:

    Field Present when
    rawValue No aggregation.
    sumValue, minValue, maxValue, avgValue, countValue, stdevValue, varianceValue The matching aggregation was requested.
    unitId Always.

    The rule to remember: the aggregation you ask for names the field you read. Aggregation=COUNT puts the result in countValue, AVG in avgValue.

    Note

    Variables with no data in the window are simply absent from the response — they do not come back with a zero. Treat "missing" and "zero" as different answers, and reconcile against the ids you requested.

    Important

    The Swagger definition under-declares this response. The published schema for GET /data lists the value fields and unitId only: the variableId, date and sourceId columns that DisplayLevel adds are absent from it, even though the API returns them.

    The consequence is for generated clients: a model class generated from swagger.json silently drops those columns, and the rows become impossible to attribute to a variable or a timestamp. Either extend the generated model by hand, or deserialise loosely and read the fields you asked for. Inspecting one real response is the fastest way to see the actual shape.

    Only the latest point

    A frequent need — "what is the last reading of these meters?" — is a paging trick rather than a dedicated endpoint.

    Keeping a data query small

    GET /data?VariableIds=101&VariableIds=102
            &DisplayLevel=ValueVariableDateSource
            &Paging.PageNumber=0&Paging.ItemsPerPage=1&PagingOrder=DESC
    

    When the filter is too long for a URL

    POST /data performs the same query as GET /data, with the filter in a JSON body instead of the query string. Same parameters, same response, no length limit.

    Use it as soon as the id list grows — it is simpler than batching, and it is the documented way to run wide queries.

    Deleting data points

    DELETE /data removes points from a range. It takes a WhatIf flag:

    Field Notes
    VariableIds Required.
    FromDateUtc, ToDateUtc The range to clear.
    WhatIf When true, the response reports what would be deleted, and nothing is removed.

    Golden rule

    Run every deletion once with WhatIf = true, and read the result, before running it for real.

    A deletion cannot be undone, and a filter that selects one variable too many is indistinguishable from a correct one until after the fact.

    Choosing between the API and the push service

    Reading is always api.opinum.com. Writing depends on what you are writing.

    Choosing an ingestion route

    You are... Use Why
    Sending measured data points, in our format push.opinum.com/api/data Built for volume, asynchronous, with webhook feedback. See Standard format.
    Sending a file in your own format push.opinum.com/api/generic/{guid} Verity maps it for you. See Custom format.
    Creating or updating sites, sources, variables api.opinum.com The push service never creates structure.
    Correcting or deleting existing points api.opinum.com DELETE /data, then push the corrected values.
    Reading anything at all api.opinum.com The push service is write-only and returns no data.
    Note

    A push is asynchronous: the HTTP call returns immediately, and parsing, insertion and aggregation happen afterwards. A 200 means "accepted", not "stored". The webhook described in Standard format is how you learn the real outcome.

    Patterns that scale

    Aggregate server-side

    Counting a year of points? Granularity=All with Aggregation=COUNT returns one row per variable, instead of millions of rows you would count yourself.

    Resolve ids once

    Fetch the variable ids you need at start-up and cache them. Re-resolving them on every cycle multiplies the calls for a mapping that rarely changes.

    Ask for the narrowest DisplayLevel

    …that still identifies your rows. Verbose on a thousand sources is a large payload built for one field.

    Reuse the token

    One token, cached until expires_in, for the whole run.

    Retry transient failures

    Network timeouts and 5xx deserve a bounded retry with a delay. Retrying a 400 never helps.

    Pin Api-Version

    On every call, to the version you tested against.

    Worked examples

    All examples assume TOKEN holds a bearer token.

    Find a source by its EAN, then list its variables:

    curl -s -H "Authorization: Bearer $TOKEN" -H "Api-Version: 1.7" \
      "https://api.opinum.com/sources?Ean=541448000000000000&DisplayLevel=Normal"
    
    curl -s -H "Authorization: Bearer $TOKEN" -H "Api-Version: 1.7" \
      "https://api.opinum.com/variables?SourceId=12345&DisplayLevel=Verbose"
    

    Read a raw series over a day, in the site's own time zone:

    curl -s -H "Authorization: Bearer $TOKEN" -H "Api-Version: 1.7" \
      "https://api.opinum.com/data?VariableId=67890\
    &DisplayLevel=ValueVariableDateSource\
    &Granularity=Raw\
    &From=2026-08-01T00:00:00&To=2026-08-02T00:00:00\
    &UseReportingTimezone=true"
    

    Daily consumption over a month:

    curl -s -H "Authorization: Bearer $TOKEN" -H "Api-Version: 1.7" \
      "https://api.opinum.com/data?VariableId=67890\
    &DisplayLevel=ValueVariableDateSource\
    &Granularity=Day&Aggregation=SUM\
    &From=2026-08-01T00:00:00Z&To=2026-08-31T23:59:59Z&IncludeToBoundary=true"
    

    Count the points of several variables without downloading them:

    curl -s -H "Authorization: Bearer $TOKEN" -H "Api-Version: 1.7" \
      "https://api.opinum.com/data?VariableIds=101&VariableIds=102&VariableIds=103\
    &DisplayLevel=ValueVariableDateSource\
    &Granularity=All&Aggregation=COUNT\
    &From=2026-01-01T00:00:00Z&To=2026-12-31T23:59:59Z&IncludeToBoundary=true"
    

    Each row of the answer carries a variableId and a countValue; a variable absent from the answer has no data at all in the window.

    Key takeaways

    • Everything hangs off the Account → Site → Source → Variable → Data point chain, and the structure comes before the data.
    • Address entities by business identifiers, not by internal ids you cannot reproduce elsewhere.
    • On /data, DisplayLevel chooses the discriminant columns, not the verbosity.
    • Set the time zone explicitly, and remember IncludeToBoundary.
    • The aggregation you request names the field you read; absent variables mean no data, not zero.
    • Let the server aggregate, batch or POST long filters, and always dry-run a deletion with WhatIf.
    Tip

    Working in .NET? The .NET client applies all of the above for you — token, version header, host resolution and repeated array parameters.

    DOCS 2026.08 REVIEWED 2026-08-28 API 1.7 Documentation changelog →

    Developer Center

    User manual API Swagger Github
    © 2025  -   www.verity.global

    Follow us

    Linkedin