Quick Start Guide

Quick Start #

Getting started with the Blicker API is really easy!

To make a request there are only two requirements. We need, of course, an image of a utility meter that we want to read out, and we need a subscription key to authenticate and identify ourselves.

The Blicker API can be tested against a sample of your own images, or against our provided test image. Execute the following on the command line to download the image.

wget https://portal.blicker.ai/static/docs/test_image.jpg

To identify who is responsible for making the request, a subscription key is always needed when calling the API. This key is unique to (part of) your organization.

Please sign up to the Blicker portal and request your personal subscription key if you haven’t already.

The Blicker reading endpoint #

With both prerequisites met, we can send a request to the Blicker reading endpoint.

curl -X 'POST' \
  'https://api.blicker.ai/blicker/2020-10-01' \
  -H 'subscription-key: <your subscription key>' \
  -F 'image=@test_image.jpg'
from pathlib import Path
from pprint import pprint

import requests

response = requests.post(
    "https://api.blicker.ai/blicker/2020-10-01",
    headers={"subscription-key": "<your subscription key>"},
    files={"image": ("test_image.jpg", Path("test_image.jpg").read_bytes())},
)

pprint(response.json())

The returned result should be a JSON containing all properties of the utility meter that could be read. Since this JSON is quite large, we will explain the output in parts.

Firstly, the response will contain some general information about the request, such as the apiVersion, language, requestId, and possibly tags that were provided in the request.

{
    "apiVersion": "2020-10-01",
    "language": "en",
    "requestId": "9fb7cc31-15f4-4d7a-be83-ab9ad4c87946",
    "tags": [],
}

The response might also contain metaData extracted from the image, such as the GPS location and date at which the image was taken. This information can also be used by Blicker to verify whether an image was taken at the expected location and time.

{
    "metaData": {
        "GPSInfo": {
            "GPSAltitude": null,
            "GPSLatitude": 51.9239681694474,
            "GPSLongitude": 4.492662314260368
        },
        "datetime": "2017-04-01T12:34:56"
    }
}

The most important information in the response though is contained in the objects. These are returned hierarchically. This means that at the top level, a list of meter objects is returned. And in turn, this meter object might contain objects contained in it, such as displays, serial numbers, and barcodes.

A meter object will hold information such as the displayType and meterCategory. For this particular example, Blicker has detected that the image shows an electricity meter with an analog display.

The objects contained in a meter object - namely displays, serial numbers, and barcodes - represent the various detected properties of the respective meter. Their read value is often the most important property of these objects. This represents the read display, serial number, or decoded barcode sequence.

{
    "objects": {
        "meter": [
            {
                "displayType": "analog",
                "index": 0,
                "messages": [],
                "meterCategory": "electricity",
                "objectId": "o-hAppwqC4",
                "objects": {
                    "barcode": [],
                    "display": [
                        {
                            "confidence": "medium",
                            "index": 0,
                            "messages": [],
                            "objectId": "o-7pRpPySM",
                            "value": "51289",
                            "valueBeforeCorrection": "51289"
                        },
                        {
                            "confidence": "high",
                            "index": 1,
                            "messages": [],
                            "objectId": "o-7k5VyGYZ",
                            "value": "57266",
                            "valueBeforeCorrection": "57266"
                        }
                    ],
                    "serialNumber": [
                        {
                            "confidence": "high",
                            "index": 0,
                            "messages": [],
                            "objectId": "o-aPFy8UuX",
                            "value": "40000444",
                            "valueBeforeCorrection": "40000444"
                        }
                    ]
                }
            }
        ]
    }
}

Note that each object has a unique identifier, namely the objectId. Furthermore, objects may have messages associated with them. Within the objects, these are referenced by their unique messageId. In the above example, none of the objects have a message associated with them. At the top level of the returned JSON, a list of all referenced messages is returned, which is empty for this particular example.

{
    "messages": []
}

These messages can, for example, show whether verification was successful or unsuccessful. Let’s see how this works.

Although Blicker correctly identified the meter to be an electricity meter, let’s pretend that we actually expected it to be a gas meter. We can let Blicker know by specifying this in the request body.

curl -X 'POST' \
  'https://api.blicker.ai/blicker/2020-10-01' \
  -H 'subscription-key: <your subscription key>' \
  -F 'image=@test_image.jpg' \
  -F 'meterCategory=gas'
from pathlib import Path
from pprint import pprint

import requests

response = requests.post(
    "https://api.blicker.ai/blicker/2020-10-01",
    headers={"subscription-key": "<your subscription key>"},
    files={"image": ("test_image.jpg", Path("test_image.jpg").read_bytes())},
    data={"meterCategory": "gas"},
)

pprint(response.json())

We now get a similar response as before, of which we will only show specific parts. We now see that an additional expectedMeterCategory property is returned. Additionally, within the meter object, the list of messages is now filled with one messageId.

{
    "objectId": "o-h6pnpNGU", 
    "meterCategory": "electricity",
    "expectedMeterCategory": "gas",
    "messages": [
        "m-4UmsFxrf"
    ]
}

Info
Since this response is the results of a new request, the meter will have a different objectId.
This messageId is merely a reference to the message. The actual message will be contained within the messages property at the top level of the returned JSON.

 {
    "messages": [
        {
            "code": "no_match_meter_category",
            "message": "The detected meter category does not match the expected meter category.",
            "messageId": "m-4UmsFxrf",
            "objectId": "o-h6pnpNGU"
        }
    ]
}

The message itself also holds a reference to the meter object by its objectId. These references allow to easily determine to which object the message refers.

The Blicker feedback endpoint #

In most cases Blicker can read out meters more reliably than humans, but there are cases in which the reading could be wrong. The Blicker feedback endpoint may be used in such cases to communicate this to us, such that the feedback can be integrated into the Blicker portal. From within the Blicker portal, such corrections can then later be manually verified.

In the above example, the serial number reading is 40000444, which is correct. However, let’s pretend that it is not correct, and should have been 80000888. This can be notified to Blicker by copying the entire response from the Blicker reading endpoint, modifying the incorrect values, and sending this back to the Blicker feedback endpoint.

Technically, however, not the entire response needs to be sent back to the Blicker feedback endpoint. In the example below, only the required fields are sent back. All other values, such as the index or confidence, etc., would be ignored.

curl -X 'PUT' \
  'https://api.blicker.ai/blicker/2020-10-01/feedback' \
  -H 'subscription-key: <your subscription key>' \
  -d '{"requestId":"<requestId>","objects":{"meter":[{"objectId":"<objectId>","displayType":"analog","meterCategory":"electricity","objects":{"barcode":[],"display":[{"objectId":"<objectId>","value":"51289"},{"objectId":"<objectId>","value":"57266"}],"serialNumber":[{"objectId":"<objectId>","value":"80000888"}]}}]}}'
from pprint import pprint

import requests

updated_response = {
    "requestId": "<requestId>",
    "objects": {
        "meter": [
            {
                "objectId": "<objectId>",
                "displayType": "analog",
                "meterCategory": "electricity",
                "objects": {
                    "barcode": [],
                    "display": [
                        {
                            "objectId": "<objectId>",
                            "value": "51289",
                        },
                        {
                            "objectId": "<objectId>",
                            "value": "57266",
                        },
                    ],
                    "serialNumber": [
                        {
                            "objectId": "<objectId>",
                            "value": "80000888",
                        }
                    ],
                },
            }
        ]
    },
}

response = requests.put(
    "https://api.blicker.ai/blicker/2020-10-01/feedback",
    headers={"subscription-key": "<your subscription key>"},
    json=updated_response,
)

pprint(response.json())
Warning
In the above example, the requestId and objectId must be filled in according to the response of the Blicker reading endpoint. When the objectId’s do not match, the Blicker feedback endpoint will assume that all objectId’s not present should have been deleted.

In the Blicker portal, you will now see the original and updated display reading for this request.

The Blicker portal #

All requests to the Blicker reading and feedback endpoints can be viewed afterwards in the Blicker portal. The Blicker portal features extensive search functionality to retrieve a request based on its requestId, tags, read values, etc.

Furthermore, the Blicker portal is used to create subscriptions keys, create organizations, add users to said organizations, and manage other settings such as rate limits and custom styling.