This page describes the end to end flow to read and parse lab results from a file
What is OCR?
OCR (Optical Character Recognition) is a technology that converts different types of documents, such as scanned paper documents, PDFs, or images containing text, into machine-readable and editable text.
We have a Labs OCR service available over the SD API, that is customised to read lab test results from lab test reports, and return the response as JSON.
To extract results from a lab report file (PDF or image file), you first need to upload the file and then generate a Lab OCR Job against that file for a particular profile to whom the file belongs to. The steps to do so are given below.
Step 1: Upload Your Lab Results File
Step 1.1: Generate a Presigned POST URL
To upload lab results file to SD's Amazon S3 bucket, you will need a URL to send a POST request to containing the file, and the authorisation fields that you are supposed to send along with the file for verification purpose.
To get the URL and the relevant fields, make a POST request to /lab-ocr-job/upload-file/ endpoint.
Example Request
POST https://selfdecode.com/service/b2b-integrations/lab-ocr-job/upload-file/
As shown below, the endpoint will return the URL, auth fields and a file_id.
Example Response
{
"url": "https://sd-platform-prod-userdatas3bucket-1qwn1x3iixoi2.s3.amazonaws.com/",
"fields": {
"key": "tmp/ocr/requests/3763cae3-e8fe-44cc-911c-6278ff3e1292/b825f4d7-c771-4640-b223-b7f39dcc1205",
"AWSAccessKeyId": "ASIA24LREHUC4HG434D3",
"x-amz-security-token": "IQoJb3JpZ2luX2VjEOf//////////wEaCXVzLWVhc3QtMSJHMEUCIQDoNxCXcNgaGCmERsc3Ei4pQ00sO2oatmAUibYKf9BWPAIgKxJjIZa0keCPeP1W4jVfMWrZyoMvAEW2AVBTQSCh2JIqowMIEBAEGgw3NDgwOTg0MzYzNTciDBEmM5Iav+AQdOC86CqAAz8L8Wz6AYCMbfiTB13fnwx+JxUl1ifJNsQe+qRSYBeYSiYtn36FGiTmEkVw79qMCkNG4RFRNvuigYAQcgxDegakJ21N+HzstpXmEscLf5PE4uvC21kYwguwUnj7JKNwBumFOkPq/EsWfFJo1/8oXhrtWY2OsbNXNZMw7icEoBAu09H4+8968V2yD9CWsSHPl4g4b4Re1salgpsDKOFlhvYnp6+8OrfFSlKZRPl3p/x78TFY2Y2y7QhWaG3YCk01eMCZbfcFemoOy/0quhfwgdXMmTf4/ty+dqst5QzATQjs/LgpfrBwr6WrjahP6+McK6D38HFa6+ZCVhjWRg6ZthDqJKdal3GnqSfDh7AYy+1mSMxVSg3h37HmSNcp0tdEQzo+z98OVDGb/SCPwuL0m1ZaDUTbW6LPU3Wx7wZBV7YIkIbNfNlEQ8CD8OsofmCtExb1JQa2nbDsS5QGlQx+5o62Hg1CxInQBx9hL2xXVVxPuGe/WfoRS8l3SZmLT4mHrjD3rLa9BjqdAd5rndOAdLD4vtPEVzC03SdMPwNd3e0Mu55Z5frw9obz+9tJhmfZGzpsMlH7IpRGJM9vFFoSb3Bs0KtHlShJRwWYFmftk0fSIfFbEwH4dnnp2uEvyPQ5dKrcaN3Fxd7zvPScxHqKNlNCyLJqDrdNqcX0856fkC8OR3DfgDSV9MnYhxyLUzPRZoc/iK0yhTUk866gAgxoIu/Yxw7k5rU=",
"policy": "eyJleHBpcmF0aW9uIjogIjIwMjUtMDItMTNUMDc6Mjk6NDBaIiwgImNvbmRpdGlvbnMiOiBbeyJidWNrZXQiOiAic2QtcGxhdGZvcm0tcHJvZC11c2VyZGF0YXMzYnVja2V0LTFxd24xeDNpaXhvaTIifSwgeyJrZXkiOiAidG1wL29jci9yZXF1ZXN0cy9iODI1ZjRkNy1jNzcxLTQ2NDAtYjIyMy1iN2YzOWRjYzEyMDUifSwgeyJ4LWFtei1zZWN1cml0eS10b2tlbiI6ICJJUW9KYjNKcFoybHVYMlZqRU9mLy8vLy8vLy8vL3dFYUNYVnpMV1ZoYzNRdE1TSkhNRVVDSVFEb054Q1hjTmdhR0NtRVJzYzNFaTRwUTAwc08yb2F0bUFVaWJZS2Y5QldQQUlnS3hKaklaYTBrZUNQZVAxVzRqVmZNV3JaeW9NdkFFVzJBVkJUUVNDaDJKSXFvd01JRUJBRUdndzNORGd3T1RnME16WXpOVGNpREJFbU01SWF2K0FRZE9DODZDcUFBejhMOFd6NkFZQ01iZmlUQjEzZm53eCtKeFVsMWlmSk5zUWUrcVJTWUJlWVNpWXRuMzZGR2lUbUVrVnc3OXFNQ2tORzRSRlJOdnVpZ1lBUWNneERlZ2FrSjIxTitIenN0cFhtRXNjTGY1UEU0dXZDMjFrWXdndXdVbmo3SktOd0J1bUZPa1BxL0VzV2ZGSm8xLzhvWGhydFdZMk9zYk5YTlpNdzdpY0VvQkF1MDlINCs4OTY4VjJ5RDlDV3NTSFBsNGc0YjRSZTFzYWxncHNES09GbGh2WW5wNis4T3JmRlNsS1pSUGwzcC94NzhURlkyWTJ5N1FoV2FHM1lDazAxZU1DWmJmY0ZlbW9PeS8wcXVoZndnZFhNbVRmNC90eStkcXN0NVF6QVRRanMvTGdwZnJCd3I2V3JqYWhQNitNY0s2RDM4SEZhNitaQ1ZoaldSZzZadGhEcUpLZGFsM0ducVNmRGg3QVl5KzFtU014VlNnM2gzN0htU05jcDB0ZEVRem8rejk4T1ZER2IvU0NQd3VMMG0xWmFEVVRiVzZMUFUzV3g3d1pCVjdZSWtJYk5mTmxFUThDRDhPc29mbUN0RXhiMUpRYTJuYkRzUzVRR2xReCs1bzYySGcxQ3hJblFCeDloTDJ4WFZWeFB1R2UvV2ZvUlM4bDNTWm1MVDRtSHJqRDNyTGE5QmpxZEFkNXJuZE9BZExENHZ0UEVWekMwM1NkTVB3TmQzZTBNdTU1WjVmcnc5b2J6Kzl0SmhtZlpHenBzTWxIN0lwUkdKTTl2RkZvU2IzQnMwS3RIbFNoSlJ3V1lGbWZ0azBmU0lmRmJFd0g0ZG5ucDJ1RXZ5UFE1ZEtyY2FOM0Z4ZDd6dlBTY3hIcUtObE5DeUxKcURyZE5xY1gwODU2ZmtDOE9SM0RmZ0RTVjlNblloeHlMVXpQUlpvYy9pSzB5aFRVazg2NmdBZ3hvSXUvWXh3N2s1clU9In1dfQ==",
"signature": "gqNQlbqcLK4UyLU72FtaeCVSA4s="
},
"file_id": "b825f4d7-c771-4640-b223-b7f39dcc1205"
}
Step 1.2: POST the File to the Presigned URL
Make a POST request to the url returned from Step 1.1, using the fields returned from Step 1.1 as payload, along with the lab results file that you wish to upload.
File Constraints
Only PDF, JPG/JPEG, PNG and TIFF formats are accepted
Example Request
POST https://sd-platform-prod-userdatas3bucket-1qwn1x3iixoi2.s3.amazonaws.com/
Payload:
key: tmp/ocr/requests/3763cae3-e8fe-44cc-911c-6278ff3e1292/b825f4d7-c771-4640-b223-b7f39dcc1205
AWSAccessKeyId: ASIA24LREHUC4HG434D3
x-amz-security-token: IQoJb3JpZ2luX2VjEOf//////////wEaCXVzLWVhc3QtMSJHMEUCIQDoNxCXcNgaGCmERsc3Ei4pQ00sO2oatmAUibYKf9BWPAIgKxJjIZa0keCPeP1W4jVfMWrZyoMvAEW2AVBTQSCh2JIqowMIEBAEGgw3NDgwOTg0MzYzNTciDBEmM5Iav+AQdOC86CqAAz8L8Wz6AYCMbfiTB13fnwx+JxUl1ifJNsQe+qRSYBeYSiYtn36FGiTmEkVw79qMCkNG4RFRNvuigYAQcgxDegakJ21N+HzstpXmEscLf5PE4uvC21kYwguwUnj7JKNwBumFOkPq/EsWfFJo1/8oXhrtWY2OsbNXNZMw7icEoBAu09H4+8968V2yD9CWsSHPl4g4b4Re1salgpsDKOFlhvYnp6+8OrfFSlKZRPl3p/x78TFY2Y2y7QhWaG3YCk01eMCZbfcFemoOy/0quhfwgdXMmTf4/ty+dqst5QzATQjs/LgpfrBwr6WrjahP6+McK6D38HFa6+ZCVhjWRg6ZthDqJKdal3GnqSfDh7AYy+1mSMxVSg3h37HmSNcp0tdEQzo+z98OVDGb/SCPwuL0m1ZaDUTbW6LPU3Wx7wZBV7YIkIbNfNlEQ8CD8OsofmCtExb1JQa2nbDsS5QGlQx+5o62Hg1CxInQBx9hL2xXVVxPuGe/WfoRS8l3SZmLT4mHrjD3rLa9BjqdAd5rndOAdLD4vtPEVzC03SdMPwNd3e0Mu55Z5frw9obz+9tJhmfZGzpsMlH7IpRGJM9vFFoSb3Bs0KtHlShJRwWYFmftk0fSIfFbEwH4dnnp2uEvyPQ5dKrcaN3Fxd7zvPScxHqKNlNCyLJqDrdNqcX0856fkC8OR3DfgDSV9MnYhxyLUzPRZoc/iK0yhTUk866gAgxoIu/Yxw7k5rU=
policy: eyJleHBpcmF0aW9uIjogIjIwMjUtMDItMTNUMDc6Mjk6NDBaIiwgImNvbmRpdGlvbnMiOiBbeyJidWNrZXQiOiAic2QtcGxhdGZvcm0tcHJvZC11c2VyZGF0YXMzYnVja2V0LTFxd24xeDNpaXhvaTIifSwgeyJrZXkiOiAidG1wL29jci9yZXF1ZXN0cy9iODI1ZjRkNy1jNzcxLTQ2NDAtYjIyMy1iN2YzOWRjYzEyMDUifSwgeyJ4LWFtei1zZWN1cml0eS10b2tlbiI6ICJJUW9KYjNKcFoybHVYMlZqRU9mLy8vLy8vLy8vL3dFYUNYVnpMV1ZoYzNRdE1TSkhNRVVDSVFEb054Q1hjTmdhR0NtRVJzYzNFaTRwUTAwc08yb2F0bUFVaWJZS2Y5QldQQUlnS3hKaklaYTBrZUNQZVAxVzRqVmZNV3JaeW9NdkFFVzJBVkJUUVNDaDJKSXFvd01JRUJBRUdndzNORGd3T1RnME16WXpOVGNpREJFbU01SWF2K0FRZE9DODZDcUFBejhMOFd6NkFZQ01iZmlUQjEzZm53eCtKeFVsMWlmSk5zUWUrcVJTWUJlWVNpWXRuMzZGR2lUbUVrVnc3OXFNQ2tORzRSRlJOdnVpZ1lBUWNneERlZ2FrSjIxTitIenN0cFhtRXNjTGY1UEU0dXZDMjFrWXdndXdVbmo3SktOd0J1bUZPa1BxL0VzV2ZGSm8xLzhvWGhydFdZMk9zYk5YTlpNdzdpY0VvQkF1MDlINCs4OTY4VjJ5RDlDV3NTSFBsNGc0YjRSZTFzYWxncHNES09GbGh2WW5wNis4T3JmRlNsS1pSUGwzcC94NzhURlkyWTJ5N1FoV2FHM1lDazAxZU1DWmJmY0ZlbW9PeS8wcXVoZndnZFhNbVRmNC90eStkcXN0NVF6QVRRanMvTGdwZnJCd3I2V3JqYWhQNitNY0s2RDM4SEZhNitaQ1ZoaldSZzZadGhEcUpLZGFsM0ducVNmRGg3QVl5KzFtU014VlNnM2gzN0htU05jcDB0ZEVRem8rejk4T1ZER2IvU0NQd3VMMG0xWmFEVVRiVzZMUFUzV3g3d1pCVjdZSWtJYk5mTmxFUThDRDhPc29mbUN0RXhiMUpRYTJuYkRzUzVRR2xReCs1bzYySGcxQ3hJblFCeDloTDJ4WFZWeFB1R2UvV2ZvUlM4bDNTWm1MVDRtSHJqRDNyTGE5QmpxZEFkNXJuZE9BZExENHZ0UEVWekMwM1NkTVB3TmQzZTBNdTU1WjVmcnc5b2J6Kzl0SmhtZlpHenBzTWxIN0lwUkdKTTl2RkZvU2IzQnMwS3RIbFNoSlJ3V1lGbWZ0azBmU0lmRmJFd0g0ZG5ucDJ1RXZ5UFE1ZEtyY2FOM0Z4ZDd6dlBTY3hIcUtObE5DeUxKcURyZE5xY1gwODU2ZmtDOE9SM0RmZ0RTVjlNblloeHlMVXpQUlpvYy9pSzB5aFRVazg2NmdBZ3hvSXUvWXh3N2s1clU9In1dfQ==
signature: gqNQlbqcLK4UyLU72FtaeCVSA4s=
file: (binary)
You should get "Status Code: 204 No Content" as the response, indicating successful file upload.
Step 2: Initiate OCR on the Uploaded File by Creating a Lab OCR Job
Now that the file has been uploaded to s3, it has to be read and the lab marker names, units, values and date of test needs to be extracted from the file. To proceed, create a lab OCR job by calling the POST /lab-ocr-job/ endpoint with the two data fields: profile_id and file_id.
The file_id would be used from Step 1.1.
Example Request
POST https://selfdecode.com/service/b2b-integrations/lab-ocr-job/
Payload:
{
"profile_id": "91997a8b-f8d8-4a02-b207-25c7258ba6ee",
"file_id": "b825f4d7-c771-4640-b223-b7f39dcc1205"
}
Example Response
{
"profile_id": "91997a8b-f8d8-4a02-b207-25c7258ba6ee",
"file_id": "b825f4d7-c771-4640-b223-b7f39dcc1205",
"id": "8eb92fd0-d2bf-4b8f-bac2-1f61439ab596",
"user_id": "3763cae3-e8fe-44cc-911c-6278ff3e1292",
"status": "processing",
"date_on_lab_test_report": null,
"results": {},
"created_at": "2025-02-13T07:57:25.548404Z",
"updated_at": "2025-02-13T07:57:25.548422Z",
"error": "",
"handling_failed": false
}
Python Example for Step 1 + Step 2
Python example code to upload file to s3 + create a lab OCR job with file_id to trigger OCR processing on the file:
import requests
headers = {"Authorization": "Token XYZ"}
base_url = "https://selfdecode.com/service/b2b-integrations/"
# Step 1.1: Get presgined POST url
response = requests.post(base_url + "lab-ocr-job/upload-file/", headers=headers, timeout=30).json()
url, fields, file_id = response["url"], response["fields"], response["file_id"]
# Step 1.2: Upload your lab results file to s3
# upload file to s3, assuming your file is named "Lab Result Report.pdf" and placed in the same directory as this Python module
file_path = "Lab Result Report.pdf"
with open(file_path, "rb") as f:
files = {"file": (file_path, f)}
requests.post(url, data=fields, files=files, timeout=60)
# Step 2: Trigger OCR file processing, by creating a lab OCR job, passing the `file_id`.
# generate lab OCR job with file_id
base_payload = {
"profile_id": "91997a8b-f8d8-4a02-b207-25c7258ba6ee",
"file_id": file_id,
}
response = requests.post(base_url + "lab-ocr-job/", headers=headers, json=base_payload)
Step 3: Retrieve Results from the Job
You can retrieve the results of the job by calling the GET lab-ocr-job/{job-id} endpoint or list all jobs by calling the GET lab-ocr-job/ endpoint. It might take some minutes before the job is complete and results are populated.
Webhook Integration
If you want to be automatically notified right after the results are available and don't want to poll the
GET /lab-ocr-job/endpoint repeatedly while the job is still in processing, you can implement webhook integration using this guide.
Example Request
GET https://selfdecode.com/service/b2b-integrations/lab-ocr-job/8eb92fd0-d2bf-4b8f-bac2-1f61439ab596/
Example Success Response
{
"profile_id": "91997a8b-f8d8-4a02-b207-25c7258ba6ee",
"file_id": "b825f4d7-c771-4640-b223-b7f39dcc1205",
"id": "1f89e774-01f7-41e0-bb04-69e0c9a53fbf",
"user_id": "3763cae3-e8fe-44cc-911c-6278ff3e1292",
"status": "completed",
"date_on_lab_test_report": "2021-08-10",
"results": [
{
"marker": {
"id": "48f165d5-7b00-c7f4-781e-f86f5c8cc1ab",
"name": "White Blood Cell Count"
},
"level": 10.700000000000001,
"unit": {
"id": "02f16d3d-8f4c-29e7-d92b-0703f7467ac9",
"name": "Thousand/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "993955be-5888-6f39-137c-56af8c5187c1",
"name": "Red Blood Cell Count"
},
"level": 5.0,
"unit": {
"id": "8d62d777-8090-c4bd-d6a7-4dbe3e572e0f",
"name": "million/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "bd023447-34aa-53ff-3278-e0a594ac807a",
"name": "Hemoglobin"
},
"level": 15.4,
"unit": {
"id": "6018366c-f658-f7a7-5ed3-4fe53a096533",
"name": "g/dL"
},
"discrete_value": null
},
{
"marker": {
"id": "9af49740-96f5-b0ee-5e25-02420ae59635",
"name": "Hematocrit"
},
"level": 44.9,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "7b89296c-6dcb-ac50-0857-7eb1924770d3",
"name": "MCV"
},
"level": 99.9,
"unit": {
"id": "7b89296c-6dcb-ac50-0857-7eb1924770d3",
"name": "fL"
},
"discrete_value": null
},
{
"marker": {
"id": "6018366c-f658-f7a7-5ed3-4fe53a096533",
"name": "MCH"
},
"level": 32.9,
"unit": {
"id": "6513270e-269e-0d37-f2a7-4de452e6b438",
"name": "pg"
},
"discrete_value": null
},
{
"marker": {
"id": "4462ebfc-5f91-5ef0-9cfb-ac6e7687a66e",
"name": "MCHC"
},
"level": 35.9,
"unit": {
"id": "6018366c-f658-f7a7-5ed3-4fe53a096533",
"name": "g/dL"
},
"discrete_value": null
},
{
"marker": {
"id": "5bc8fbbc-bde5-c099-4164-d8399f767c45",
"name": "RDW"
},
"level": 14.9,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "85750621-02fb-cd4f-357f-bc5af71a1bfc",
"name": "Platelet Count"
},
"level": 399.9,
"unit": {
"id": "02f16d3d-8f4c-29e7-d92b-0703f7467ac9",
"name": "Thousand/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "4a37fa2d-f2d7-d40f-c785-9faeecc3f80c",
"name": "MPV"
},
"level": 12.4,
"unit": {
"id": "7b89296c-6dcb-ac50-0857-7eb1924770d3",
"name": "fL"
},
"discrete_value": null
},
{
"marker": {
"id": "9530fcd9-d6fd-1d9b-6203-2801b65c1c28",
"name": "Absolute Neutrophils"
},
"level": 7799.9,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "6ad9d8ea-272b-f385-9f58-5b6d32793f45",
"name": "Absolute Metamyelocytes"
},
"level": 1.0,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "c37b699c-6342-4023-ed82-0abc8cf556ab",
"name": "Absolute Myelocytes"
},
"level": 1.0,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "8a51d00d-ffa2-9d38-a898-cfdee7a3c563",
"name": "Absolute Promyelocytes"
},
"level": 1.0,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "4dad2986-ce83-4960-6a06-e9ab85a0bcc1",
"name": "Absolute Lymphocytes"
},
"level": 3899.9,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "e539a78b-c8ef-f346-0b12-ae6ead581e57",
"name": "Absolute Monocytes"
},
"level": 949.9,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "c963cfe0-afae-5a3b-b909-6a04e7d80068",
"name": "Absolute Eosinophils"
},
"level": 499.9,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "72e63ac7-a953-8322-1f70-d5dc2e675fc7",
"name": "Absolute Basophils"
},
"level": 199.9,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "5b4d71c7-39cf-2503-fc0e-b4f65bd6eed7",
"name": "Absolute Blasts"
},
"level": 1.0,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "9de7c55d-2dff-8241-ec73-b666f05c8756",
"name": "Absolute Nucleated RBC"
},
"level": 1.0,
"unit": {
"id": "c15521b1-b3dc-a50a-9daa-37e51b591d75",
"name": "cells/uL"
},
"discrete_value": null
},
{
"marker": {
"id": "5e92a710-845c-7c87-1d68-b6d8bedcfefc",
"name": "Neutrophils (%)"
},
"level": 79.9,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "540bc2dc-167f-b796-d219-ae5f3a9487f6",
"name": "Lymphocytes (%)"
},
"level": 48.9,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "d130c0ae-e8ef-a937-5851-2321c99322a5",
"name": "Monocytes (%)"
},
"level": 12.9,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "bd0fd87c-2e08-9d5a-4937-17c4ed22d6f2",
"name": "Eosinophils (%)"
},
"level": 7.9,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "2cd4c982-9fed-bae1-8510-ccca416a4001",
"name": "Basophils (%)"
},
"level": 1.9,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "f8463cd1-8389-7c06-8827-ecebc299f156",
"name": "Iron, Total"
},
"level": 99.9,
"unit": null,
"discrete_value": null
},
{
"marker": {
"id": "c9b68d3b-9640-6983-eb18-7cde929ae94f",
"name": "Metamyelocytes %"
},
"level": 42.0,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "e40508d1-6e66-100b-5116-33567c50b1c1",
"name": "Myelocytes %"
},
"level": 42.0,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "277582f0-93f5-0c2c-488e-44f94ecc6c7f",
"name": "Bands"
},
"level": 4.9,
"unit": null,
"discrete_value": null
},
{
"marker": {
"id": "5133c68b-920d-61c4-2db5-ae42a45a62c6",
"name": "Promyelocytes %"
},
"level": 42.0,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "2b23a752-af02-822e-aa0c-c3ec505c06bc",
"name": "Blasts %"
},
"level": 42.0,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "f268d1c3-733a-2a58-61c4-61dc31850b2b",
"name": "Immature Granulocyte %"
},
"level": 1.4,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "8700cfed-571a-31b0-a32e-a7fbb2bec49f",
"name": "Granulocytes %"
},
"level": 73.4,
"unit": {
"id": "14a03569-d26b-9496-92e5-dfe8cb1855fe",
"name": "%"
},
"discrete_value": null
},
{
"marker": {
"id": "8d62d777-8090-c4bd-d6a7-4dbe3e572e0f",
"name": "Na"
},
"level": 1.0,
"unit": null,
"discrete_value": null
}
],
"created_at": "2025-02-13T15:43:36.861051Z",
"updated_at": "2025-02-13T15:43:36.861065Z",
"error": "",
"handling_failed": false
}
The fields of major concern in the response are the date_on_lab_test_report and results fields. The date_on_lab_test_report field contains the date of the lab test, and the results field is an array with each item representing the information of each marker, its unit and its value read from the file. The nested level field represents the value of the numeric markers, along with the nested unit field. While if there is a discrete marker, its value is populated in the discrete_value field. For more details about the API schema, refer to the endpoint.
Please note that the markers that are read from the file are matched to a list of supported markers stored in our database, and the matched markers are returned. So, there is a possibility of some lab markers present in a lab results file, but not supported by SD; hence they would not be returned in the response.
Example Failure Response
{
"profile_id": "91997a8b-f8d8-4a02-b207-25c7258ba6ee",
"file_id": "a4878fd5-8e65-4ee1-af65-c3c0e03f79f6",
"id": "4ecc0134-deca-4b79-b52d-db239dd69c32",
"user_id": "3763cae3-e8fe-44cc-911c-6278ff3e1292",
"status": "failed",
"date_on_lab_test_report": null,
"results": [],
"created_at": "2025-02-13T15:39:07.051392Z",
"updated_at": "2025-02-13T15:39:07.051408Z",
"error": "Job failed with error_message='INVALID_IMAGE_TYPE'",
"handling_failed": false
}
In case of job failure, the error field would be populated with the error message.
Limitations of OCR and Filling in Missing Data
Our system may not recognise all lab markers in the file and works best with results from Labcorp or Quest Diagnostics. You can review and edit each entry before further analysis.
Even for the markers it reads, the OCR is not always 100% accurate, and sometimes it may not correctly read the date, or it may miss the marker unit or the marker value. In those cases, the JSON response from Step 3 may have some null or incorrect values. The values can be manually filled / fixed after making comparisons with the original lab results file.
Getting Details of the Matched Markers
Now that you have a list of matched lab markers returned in the results field from Step 3, you can fetch the details of those markers, like their synonyms, list of available units / discrete values, etc. These details can be used to fill in the missing units / discrete values if needed.
To fetch details, call the /marker-summary/ list endpoint. This would return all markers available, and you can filter on your own the marker IDs received from Step 3. For more details, refer here.
Creating Results for Analysis
Now that the results have been parsed and read by the OCR, they can be analyzed. Analysis would give a clear picture of each marker and its value, i.e. whether the value lies in optimal, sub-optimal or other range and the recommendations (if any) that can be helpful given the lab result.
To do so, you'd basically be creating the results in SD - the details of which are provided here.
In short, OCR job will give you the JSON of results read from your lab results file as mentioned in step 3 above, which you can use as input (after modifications if needed) to the bulk result creation endpoints. OCR helps you minimise the manual work of extracting out the lab results on your own from your lab test result files.
Let's see how we can create top two of the results returned in the response under Step 3:
POST https://selfdecode.com/service/b2b-integrations/labs/numeric-result/bulk-create/
Payload:
[
{
"marker_id": "48f165d5-7b00-c7f4-781e-f86f5c8cc1ab", // results[0].marker.id
"synonym": "White Blood Cell Count", // results[0].marker.name
"taken_at": "2021-08-10", // date_on_lab_test_report
"unit_id": "02f16d3d-8f4c-29e7-d92b-0703f7467ac9", // results[0].unit.id
"value": 10.7, // results[0].level
"profile_id": "91997a8b-f8d8-4a02-b207-25c7258ba6ee"
},
{
"marker_id": "993955be-5888-6f39-137c-56af8c5187c1", // results[1].marker.id
"synonym": "Red Blood Cell Count", // results[1].marker.name
"taken_at": "2021-08-10", // date_on_lab_test_report
"unit_id": "8d62d777-8090-c4bd-d6a7-4dbe3e572e0f", // results[1].unit.id
"value": 5, // results[1].level
"profile_id": "91997a8b-f8d8-4a02-b207-25c7258ba6ee"
}
]
Closing Remarks
This guide covers everything related to the OCR and its usage. For more details about lab markers and lab results, you can go through the complete Managing Lab Results page.
