The IMGT just became a little FAIRer

The IMGT have adopted a more permissive licence for their data. That is a really good thing. These days, they also have an API. But the service is somewhat hamstrung by usability issues. Also, detailed and digestible documentation for such a sprawling suite of databases and tools never just appears overnight, so this post includes a demo to help you get started.

A more permissive licence

The International Immunogenetics Information System, IMGT, is a long-established platform from the research group of Profs Sofia Kossida and Marie-Paule Lefranc of the Université de Montpelier and the CNRS. It provides numerous services to the immunology research community, including catalogues of the genetic encoding of naturally-occurring immunoglobulin (antibody), T cell receptor and major histocompatibility complex protein domains across several vertebrate species.

This month, the IMGT announced that they have been granted Core Data Resource status from ELIXIR, the European life-sciences infrastructure for biological information. This recognition highlights the importance of the IMGT to immunology research, but it comes with one or two strings attached. Services supported by ELIXIR must adhere to certain standards of data stewardship, to enhance the FAIR access of data. In the case of the IMGT, this includes relaxing the long-standing and rather restrictive licence conditions they formerly imposed on the use of their tools. The IMGT used to use the Creative Commons Attribution-NonCommercial-NoDerivatives (CC BY-NC-ND 4.0) licence, which made it very difficult for anyone else to write interoperable tools. Since 1st July 2026, the IMGT is instead available under a simple Creative Commons Attribution (CC BY 4.0) licence.

The trouble with CC BY-NC-ND for science

The CC BY-NC-ND licence is a tricky beast. To the inexperienced academic scientist, it may seem perfectly innocuous. Of course I want attribution for my work, but I also want to make sure that no-one drags my name into the mud by reprocessing my data or creating derivative software that I think is misguided, or just plain wrong. And I am an altruistic sort of person, I want my work to contribute to the sum of human knowledge, unsullied by base commerce… but my latest grant is running out soon, so if anyone wants to make money off the back of my research, then I would like a cut, please. A non-commercial, no-derivatives licence, you say? Sounds great!

Now think of it from the point of view of a user. If we in OPIG want to create a piece of software that does some intricate things to model antibody sequences or structures, and we think this tool would be enriched by including some bits of information only available under a CC BY-NC-ND licence, are we allowed to use the data? What if someone wants to use our software in a commercial setting? Do we need to refuse? Or else enter a three-way negotiation between us, our users and the CC BY-NC-ND licence holder for a separate commercial licence? Is our software, in fact, a derivative of the original data? What if we are just writing a journal article that re-analyses some of those CC BY-NC-ND data, is that forbidden as a derivative product too?

In practice, of course, people end up doing one of two things: either they break the terms of the licence, usually unwittingly, or they avoid the issue altogether and find some other work to do. The CC BY-NC-ND does not achieve its holder’s objective. It generates none of that promised revenue and it can even put the brakes on research. It is all too often a poor fit for scientific work.

Good news, everyone!

Hooray, we are now allowed to use the IMGT’s data! But how exactly does one do that?

The IMGT has for several years operated a website with various interfaces for querying their several databases, but that still requires a human in the loop. If I want to write software to query the data at scale, how do I do so?

This brings us to a second very positive recent development in the IMGT’s renewed commitment to the principles of findable, accessible, interoperable and reusable (FAIR) data — the publication of a resource description format (RDF) knowledge graph, the IMGT-KG and its SPARQL API (see here for a primer on RDF knowledge graphs). But writing good documentation is hard, especially if you are trying to retro-fit an API onto something that has been in use since 1989. Unsurprisingly, the IMGT-KG is a little tricky to use for the uninitiated.

Firstly, an RDF knowledge graph is described by its ontology, detailing all the triples that define the relationships between data entities. The IMGT ontology reuses a lot of vocabulary from various other ontologies, in the spirit of the Open Biological and Biomedical Ontology Foundry. It is documented in HTML format (beware, it takes a long time to load the page at the time of going to press), but links to download the ontology in standard machine-readable formats are all broken at the time of writing, leaving us flying somewhat blind. It is not clear whether the ontology for the IMGT-KG is the same as the previously published IMGT-ONTOLOGY (last update, v1.0.3, released July 2019). Nevertheless, there is some discursive documentation and some SPARQL query examples that we can adapt.

This query gets the gene and allele for each instance of an immunoglobulin protein entity that IMGT has catalogued from the PDB, returning just the first ten entries.

PREFIX imgt: <https://www.imgt.org/imgt-ontology#>
PREFIX obo:  <http://purl.obolibrary.org/obo/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT DISTINCT
    ?pdb_link
    ?pdb_entry
    ?pdb_entity
    ?pdb_chain_instance
    ?chain_label
    ?coding_region
    ?prototype
    ?gene
    ?allele
WHERE {
    ?region imgt:hasAlignment ?alignment ;
            obo:BFO_0000050 ?chain_resource .

    ?chain_resource
        rdfs:label ?pdb_chain_instance ;
        imgt:hasImgtLabel/rdfs:label ?chain_label ;
        imgt:isChainInStruct ?structure .

    ?alignment imgt:forAllele ?allele_resource .

    ?allele_resource
        obo:GENO_0000408 ?gene_resource ;
        rdfs:label ?allele ;
        imgt:isInRefSequence ?core .

    ?gene_resource rdfs:label ?gene .

    ?core
        imgt:isInPrototype ?prototype_resource ;
        imgt:hasImgtLabel/rdfs:label ?coding_region .

    ?prototype_resource
        imgt:hasImgtLabel/rdfs:label ?prototype ;
        imgt:isPrototypeInSeq ?prototype_sequence .

    ?crystal_sequence
        rdfs:label ?pdb_entry ;
        imgt:isCrystalOf ?structure ;
        imgt:pdb_link ?pdb_link .

    ?structure rdfs:label ?pdb_entity .

    FILTER(CONTAINS(UCASE(STR(?coding_region)), "REGION"))
}
ORDER BY
    ?pdb_entry
    ?pdb_entity
    ?pdb_chain_instance
    ?gene
    ?allele
LIMIT 10

Now we need to know where to send our query. The SPARQL endpoint is documented in the IMGT-KG’s vocabulary of interlinked datasets (VoID). There are actually two documented endpoints, one with and one without an inference engine, but only the one without inference seems to be operational at the time of writing.

We can submit our query to that endpoint like this:

curl 'https://www.imgt.org/fuseki/ImgtKg/sparql' \
  -H 'Content-Type: application/sparql-query' \
  -H 'Accept: application/sparql-results+json' \
  --data-binary @sample-query.rq

And we get the following response:

{
  "head": {
    "vars": [
      "pdb_link",
      "pdb_entry",
      "pdb_entity",
      "pdb_chain_instance",
      "chain_label",
      "coding_region",
      "prototype",
      "gene",
      "allele"
    ]
  },
  "results": {
    "bindings": [
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E81"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8M"
        },
        "chain_label": {
          "type": "literal",
          "value": "L-KAPPA"
        },
        "coding_region": {
          "type": "literal",
          "value": "C-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "C-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGKC-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGKC*01"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E81"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8M"
        },
        "chain_label": {
          "type": "literal",
          "value": "L-KAPPA"
        },
        "coding_region": {
          "type": "literal",
          "value": "C-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "C-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGKC-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGKC*02"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E81"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8M"
        },
        "chain_label": {
          "type": "literal",
          "value": "L-KAPPA"
        },
        "coding_region": {
          "type": "literal",
          "value": "J-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "J-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGKJ5-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGKJ5*01"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E81"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8M"
        },
        "chain_label": {
          "type": "literal",
          "value": "L-KAPPA"
        },
        "coding_region": {
          "type": "literal",
          "value": "V-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "V-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGKV6-13-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGKV6-13*01"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E81"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8P"
        },
        "chain_label": {
          "type": "literal",
          "value": "VH-CH1"
        },
        "coding_region": {
          "type": "literal",
          "value": "J-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "J-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGHJ3-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGHJ3*01"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E81"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8P"
        },
        "chain_label": {
          "type": "literal",
          "value": "VH-CH1"
        },
        "coding_region": {
          "type": "literal",
          "value": "V-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "V-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGHV14-4-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGHV14-4*02"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E82"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8H"
        },
        "chain_label": {
          "type": "literal",
          "value": "VH-CH1"
        },
        "coding_region": {
          "type": "literal",
          "value": "J-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "J-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGHJ3-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGHJ3*01"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E82"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8H"
        },
        "chain_label": {
          "type": "literal",
          "value": "VH-CH1"
        },
        "coding_region": {
          "type": "literal",
          "value": "V-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "V-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGHV14-4-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGHV14-4*02"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E82"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8L"
        },
        "chain_label": {
          "type": "literal",
          "value": "L-KAPPA"
        },
        "coding_region": {
          "type": "literal",
          "value": "C-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "C-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGKC-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGKC*01"
        }
      },
      {
        "pdb_link": {
          "type": "literal",
          "value": "https://www.ebi.ac.uk/pdbe/entry/pdb/12E8"
        },
        "pdb_entry": {
          "type": "literal",
          "value": "CrystalStruct-12E8"
        },
        "pdb_entity": {
          "type": "literal",
          "value": "Struct-12E82"
        },
        "pdb_chain_instance": {
          "type": "literal",
          "value": "Chain-12E8L"
        },
        "chain_label": {
          "type": "literal",
          "value": "L-KAPPA"
        },
        "coding_region": {
          "type": "literal",
          "value": "C-REGION"
        },
        "prototype": {
          "type": "literal",
          "value": "C-GENE"
        },
        "gene": {
          "type": "literal",
          "value": "Musmus_IGKC-Gene"
        },
        "allele": {
          "type": "literal",
          "value": "Musmus_IGKC*02"
        }
      }
    ]
  }
}

Web services for science are difficult

We at OPIG know all too well that running web services for the scientific community on an academic research group’s budget is hard. We run several ourselves, so we have sympathy for the folks at the IMGT trying to keep all of this valuable knowledge current and accessible. Hopefully, their new ELIXIR support will help them iron out a few of the usability kinks. That said, at the time of publishing this, the IMGT-KG servers have been inaccessible for two days… Good luck trying out the example above! 😕

Author