Search for LOINC codes with keywords using the FHIR REST API

Hello,
I’m trying to use the FHIR REST API to find a LOINC code containing a given keyword. A bit like SearchLOINC, but using the API directly.

For example I want to find a test result containing “calcium”:

According to the FHIR standard, this should work:
https://fhir.loinc.org/CodeSystem/_search?description:contains=calcium

But I get a “HAPI-1258: :contains modifier is disabled on this server”.

Any idea on how to to this ?

Cheers,

Antonio

Hello Antonio,

Actually, we quietly announced a new LOINC Search API at the end of last year. I would suggest using it for this purpose. This is the same API that runs SearchLOINC. The API is not yet documented. Here are the endpoints to use. Be sure to pass your LOINC username/password as authentication.

EndPoints - all of these endpoints search the scope specified and utilize the same parameters

https://loinc.regenstrief.org/searchapi/loincs
https://loinc.regenstrief.org/searchapi/answerlists
https://loinc.regenstrief.org/searchapi/parts
https://loinc.regenstrief.org/searchapi/groups

Parameters

  • query – search string
  • rows* – integer value to indicate the number of rows to return
  • offset* – integer value to indicate the offset, for paging
  • sortorder* – string value, specifying which field to sort on and order (desc and asc)
  • language* – integer value to indicate which language to return. The integer value for specific languages can be found in the LingusticVariants.csv file in the LOINC release.
  • includefiltercounts* – boolean value to indicate if information for filters should be returned with the results

*All of these parameters have default values and are optional

Example Queries

Without Optional Parameters
https://loinc.regenstrief.org/searchapi/loincs?query=glucose

With All Optional Parameters
https://loinc.regenstrief.org/searchapi/loincs?query=glucose&rows=100&offset=100&sortorder=loinc_num

Please report back any feedback you have on this as it is a new endeavor.

2 Likes

Perfect, Thanks a lot Tim! It works well.

As finding the right code can be tricky, we will use this API to develop a help tool for the lab people to find the right code, directly in our LIS app.

-antonio

2 Likes

Hello Tim, Hello Antonio,

I am also trying to use the LOINC Search API but i don’t quite understand where should i pass my LOINC username/password.
I have used some APIs but they all use some kind of tokens so this is kinda new for me.

Maybe a bit background: I am trying to implement the Search API into the Mirth Connect’s transformer to convert incoming HL7 message to a FHIR Resource.

I would be very grateful if you could help me with this.

Many thanks and best regards from Germany,
Reynald

Hello Reynald,

It’s just Basic Auth. Here’s an example using Postman. Let me if this helps or not.

Sorry, I should have included actual steps to reproduce this in your code.

In Postman, you can click on the Code Snippet icon </> to see the underlying command. (I did change my authorization string.) It’s your username and password concatenated with a colon ( : ) separating them. That’s then base64 encoded.

curl --location 'https://loinc.regenstrief.org/searchapi/loincs?query=glucose' \
--header 'Authorization: Basic NGlt2Dsfc2NvZT3='

I found this page that does a good job of explaining it.

Hi Tim, sorry for the late answer! And thanks for the answer!

Yes it helps me a lot, and i can operate the API in Postman.
Currently looking for a way to implement it in a mirth channel for conversion of HL7v2 to FHIR :slight_smile:
Thanks once again!

Best Regards,
Reynald

1 Like

Hi Tim,

A bit late to the thread. But thank You for sharing this. Works great. As you mention filters: Is there any way to filter by certain fields?

Rob

1 Like

Hi Robert,

Yes, API allows a result set to be filtered. Keep in mind this is the same API we use to power our SearchLOINC tool. Within the web app, say you search for influenza. You can click the Filter icon to see the available filters and available rows. See example screenshot.

This FilterCounts data is returned in every API response—so long as you set the optional ‘includefiltercounts’ parameter to true.

Here is the request/response to that same ‘influenza’ search via the API. (Note I have removed the ‘Results’ node and truncated ‘FilterCounts’ from the snippet below so this post would fit within the forum’s character limit.)

https://loinc.regenstrief.org/searchapi/loincs?query=influenza&includefiltercounts=true

    "ResponseSummary": {
        "RecordsFound": 585,
        "StartingOffset": 0,
        "RowsReturned": 50,
        "LoincVersion": "2.78",
        "Copyright": "Copyright © 2024 Regenstrief Institute, Inc. All Rights Reserved.",
        "QueryUrl": "https://loinc.regenstrief.org/searchapi/loincs?query=influenza&includefiltercounts=true",
        "Next": "https://loinc.regenstrief.org/searchapi/loincs?query=influenza&includefiltercounts=true&offset=50",
        "QueryExecutionTime": "2024-09-24T16:17:27.4800469-04:00",
        "QueryDuration": 0.073304
    },
    "Results": [

    ],
    "FilterCounts": {
        "System": [],
        "Method": [],
        "Property": [],
        "Timing": [],
        "Scale": [],
        "Class": [],
        "VersionFirstReleased": [],
        "VersionLastChanged": [],
        "Tags": [],
        "ClassType": [],
        "OrderObs": [
            {
                "Label": "Both",
                "Search": "=orderobs:Both",
                "Count": 506
            },
            {
                "Label": "Observation",
                "Search": "=orderobs:Observation",
                "Count": 53
            },
            {
                "Label": "Order",
                "Search": "=orderobs:Order",
                "Count": 18
            },
            {
                "Label": "Subset",
                "Search": "=orderobs:Subset",
                "Count": 1
            }
        ],
        "PanelType": [
            {
                "Label": "Organizer",
                "Search": "=paneltype:Organizer",
                "Count": 2
            },
            {
                "Label": "Panel",
                "Search": "=paneltype:Panel",
                "Count": 20
            }
        ],
        "CodeSystems": [
            {
                "Label": "CMS IRF-PAI 1.4",
                "Search": "=codesystems:\"CMS IRF\\-PAI 1.4\"",
                "Count": 4
            },
            {
                "Label": "CMS IRF-PAI 1.5",
                "Search": "=codesystems:\"CMS IRF\\-PAI 1.5\"",
                "Count": 4
            },
            {
                "Label": "CMS IRF-PAI 2.0",
                "Search": "=codesystems:\"CMS IRF\\-PAI 2.0\"",
                "Count": 4
            },
            {
                "Label": "CMS LCDS 3.00",
                "Search": "=codesystems:\"CMS LCDS 3.00\"",
                "Count": 4
            },
            {
                "Label": "CMS LCDS 4.00",
                "Search": "=codesystems:\"CMS LCDS 4.00\"",
                "Count": 4
            },
            {
                "Label": "CMS MDS 1.14.1",
                "Search": "=codesystems:\"CMS MDS 1.14.1\"",
                "Count": 4
            },
            {
                "Label": "CMS MDS 1.15.1",
                "Search": "=codesystems:\"CMS MDS 1.15.1\"",
                "Count": 4
            },
            {
                "Label": "CMS MDS 1.16.1",
                "Search": "=codesystems:\"CMS MDS 1.16.1\"",
                "Count": 4
            },
            {
                "Label": "CMS MDS 1.17.1",
                "Search": "=codesystems:\"CMS MDS 1.17.1\"",
                "Count": 4
            },
            {
                "Label": "CMS MDS 1.17.2",
                "Search": "=codesystems:\"CMS MDS 1.17.2\"",
                "Count": 4
            },
            {
                "Label": "CMS MDS v1.18.11",
                "Search": "=codesystems:\"CMS MDS v1.18.11\"",
                "Count": 4
            },
            {
                "Label": "CMS OASIS C2",
                "Search": "=codesystems:\"CMS OASIS C2\"",
                "Count": 2
            },
            {
                "Label": "CMS OASIS D",
                "Search": "=codesystems:\"CMS OASIS D\"",
                "Count": 2
            },
            {
                "Label": "CMS OASIS D1",
                "Search": "=codesystems:\"CMS OASIS D1\"",
                "Count": 2
            },
            {
                "Label": "CMS OASIS E",
                "Search": "=codesystems:\"CMS OASIS E\"",
                "Count": 2
            },
            {
                "Label": "https://www.phenxtoolkit.org",
                "Search": "=codesystems:https\\://www.phenxtoolkit.org",
                "Count": 1
            }
        ],
        "Status": [
            {
                "Label": "ACTIVE",
                "Search": "=status:ACTIVE",
                "Count": 565
            },
            {
                "Label": "DEPRECATED",
                "Search": "=status:DEPRECATED",
                "Count": 10
            },
            {
                "Label": "DISCOURAGED",
                "Search": "=status:DISCOURAGED",
                "Count": 10
            }
        ]
    }
}

To “apply” a filter, simply add the ‘Search’ to the query parameter. For instance, to get the 18 results indicated by ‘OrderObs’ equal to ‘Order’, the encoded request would be:

https://loinc.regenstrief.org/searchapi/loincs?query=influenza%20%3Dorderobs:Order&includefiltercounts=true

I hope this makes sense. Let us know if you need more information.