openfigi-client


Nameopenfigi-client JSON
Version 0.4.1 PyPI version JSON
download
home_pageNone
SummaryPython client for the OpenFIGI API.
upload_time2024-09-18 18:16:24
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseMIT License Copyright (c) [2024] [ljnsn] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords api bloomberg figi openfigi python
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # openfigi-client

[![python](https://img.shields.io/badge/python-3.10_%7C_3.11_%7C_3.12-blue)](https://gitlab.com/cryptoledger-x/openfigi-client)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Black](https://img.shields.io/badge/code%20style-black-black)](https://black.readthedocs.io/en/stable/)

Python wrapper for the [OpenFIGI API](https://www.openfigi.com/api) v3.

## Table of contents

- [About OpenFIGI](#about-openfigi)
- [Installation](#installation)
- [API key](#api-key)
- [Mapping](#mapping)
- [Filtering](#filtering)
- [Troubleshooting](#troubleshooting)

## About OpenFIGI

- The **F**inancial **I**nstrument **G**lobal **I**dentifier (FIGI) is a
  universal system for identifying instruments globally and across all asset
  classes
- OpenFIGI is an application programming interface that provides automated
  access to mapping various symbols with their corresponding FIGI. It is
  available at <https://www.openfigi.com/>
- [openfigi_client](https://github.com/tlouarn/openfigi_client) is a thin Python
  wrapper to access OpenFIGI

The API contains 3 endpoints:

| endpoint | description                                          |
| -------- | ---------------------------------------------------- |
| /mapping | Map third-party identifiers to FIGIs                 |
| /filter  | Filter for FIGIs using keywords and optional filters |
| /search  | Search for FIGIs using keywords and optional filters |

_Note: given that the _/search_ endpoint is strictly superseded by the _/filter_
endpoint, we choose not to include it in the wrapper._

## Installation

**openfigi_client** is published on
[PyPI](https://pypi.org/project/openfigi_client/). To install it, simply run:

```commandline
pip install openfigi_client
```

## API key

The API can be used with or without API key. Getting an API key is free and
loosens the [rate limits](https://www.openfigi.com/api#rate-limit).

When instantiating the wrapper, the API key is optional:

```python
from openfigi_client import OpenFigiSync

client = OpenFigiSync()
client = OpenFigiSync(api_key="XXXXXXXXXX")
```

## Async

In addition to the synchronous client, there is an equivalent asynchronous
client available. All examples below work equally, simply import
`OpenFigiAsync` instead and `await` the calls.

## Mapping

The `map()` method takes a list of `MappingJob` as argument and returns a list
of `MappingJobResult`. The result of the request at index `i` in the list of
mapping jobs is located at index `i` in the list of results.

```python
from openfigi_client import OpenFigiSync, MappingJob

mapping_job = MappingJob(id_type="TICKER", id_value="IBM", exch_code="US")
mapping_jobs = [mapping_job]
results = OpenFigiSync().map(mapping_jobs)

>>> results
[
    MappingJobResultFigiList(
        data = [
            FigiResult(
                  figi='BBG000BLNNH6', 
                  security_type='Common Stock', 
                  market_sector='Equity', 
                  ticker='IBM', 
                  name='INTL BUSINESS MACHINES CORP', 
                  exch_code='US', 
                  share_class_figi='BBG001S5S399', 
                  composite_figi='BBG000BLNNH6', 
                  security_type2='Common Stock', 
                  security_description='IBM', 
                  metadata=None
            )
        ]
    )
]
```

A `MappingJobResult` can either be a `MappingJobResultFigiList`, a
`MappingJobResultFigiNotFound` or a `MappingJobResultError`.

The `MappingJob` object has 2 required properties which are `id_type` and
`id_value`. The other properties are optional but subject to specific rules in
case they are provided. These rules are modeled and checked using **Pydantic**.

Below is the full list of properties for `MappingJob`:

| property                  | required | type | example                                  |
| ------------------------- | -------- | ---- | ---------------------------------------- |
| id_type                   | X        | str  | `"TICKER"`                               |
| id_value                  | X        | str  | `"IBM"`                                  |
| exch_code                 |          | str  | `"UN"`                                   |
| mic_code                  |          | str  | `"XNYS"`                                 |
| currency                  |          | str  | `"USD"`                                  |
| market_sec_des            |          | str  | `"Equity"`                               |
| security_type             |          | str  | `"Common Stock"`                         |
| security_type_2           |          | str  | `"Common Stock"`                         |
| include_unlisted_equities |          | bool |                                          |
| option_type               |          | str  | `"Call"`                                 |
| strike                    |          | list | `[100, 200]`                             |
| contract_size             |          | list | `[0, 100]`                               |
| coupon                    |          | list | `[0, 2.5]`                               |
| expiration                |          | list | `[date(2023, 6, 1), date(2023, 12, 31)]` |
| maturity                  |          | list | `[date(2023, 6, 1), date(2023, 12, 31)]` |
| state_code                |          | str  | `"AZ"`                                   |

Some of the properties in the `MappingJob` are "enum-like". For each of these
properties, it is possible to retrieve the current list of accepted values via
specific methods:

| property        | method                   | examples |
| --------------- | ------------------------ | -------- |
| id_type         | `get_id_types()`         |          |
| exch_code       | `get_exch_codes()`       |          |
| mic_code        | `get_mic_codes()`        |          |
| currency        | `get_currencies()`       |          |
| market_sec_des  | `get_market_sec_des()`   |          |
| security_type   | `get_security_types()`   |          |
| security_type_2 | `get_security_types_2()` |          |
| state_code      | `get_state_codes()`      |          |

For example, to retrieve the current values for `id_type`:

```python
from openfigi_client import OpenFigiSync

id_types = OpenFigiSync().get_id_types()
```

## Filtering

The `filter()` method takes a `Filter` object as argument and returns a list of
`FigiResult`.

- The `Filter` object is very similar to the `MappingJob` object
- The only difference are that the `id_type` and `id_value` are replaced by a
  single `query` property
- All the "enum-like" properties are the same and the list of accepted values is
  the same
- The maximum number of results is limited to 15,000

| property                  | required | type | example                                  |
| ------------------------- | -------- | ---- | ---------------------------------------- |
| query                     | X        | str  | `"SJIM"`                                 |
| exch_code                 |          | str  | `"UN"`                                   |
| mic_code                  |          | str  | `"XNYS"`                                 |
| currency                  |          | str  | `"USD"`                                  |
| market_sec_des            |          | str  | `"Equity"`                               |
| security_type             |          | str  | `"Common Stock"`                         |
| security_type_2           |          | str  | `"Common Stock"`                         |
| include_unlisted_equities |          | bool |                                          |
| option_type               |          | str  | `"Call"`                                 |
| strike                    |          | list | `[100, 200]`                             |
| contract_size             |          | list | `[0, 100]`                               |
| coupon                    |          | list | `[0, 2.5]`                               |
| expiration                |          | list | `[date(2023, 6, 1), date(2023, 12, 31)]` |
| maturity                  |          | list | `[date(2023, 6, 1), date(2023, 12, 31)]` |
| state_code                |          | str  | `"AZ"`                                   |

Example

```python
from openfigi_client import OpenFigiSync, Filter

query = Filter(query="SJIM")
results = OpenFigiSync().filter(query)
```

In order to know the total number of matches for a given query before starting
to request them, it is possible to use the `get_total_number_of_matches()`
method:

```python
from openfigi_client import OpenFigiSync, Filter

query = Filter(query="SJIM")
number_of_results = OpenFigiSync().get_total_number_of_matches(query)

>>> number_of_results
36
```

## Troubleshooting

Several kinds of errors can occur.

- `ValidationError`: the `MappingJob` and `Filter` objects are modelled using
  **msgspec** and therefore need to be properly instantiated. If an error
  occurs, a `msgspec.ValidationError` will be raised.

- `HTTPError`: in case the status code of the HTTP response is not 200, an
  HTTPError exception will be raised. Note: in case a particular symbol is not
  found, the API will still respond with a status 200 and a `MappingNotFound`
  object. HTTP errors only occur if there is a real error like a malformed
  `MappingJob` request (which should not happen since all `MappingJob` objects
  are checked by **Pydantic** prior to being sent), a rate limitation or an
  internal server error.

Here is how to check for `ValidationError` in case the mapping jobs are
instantiated programmatically:

```python
from msgspec import ValidationError

from openfigi_client import MappingJob

tickers = ["IBM", "XRX", "TSLA", None, "MSFT"]

mapping_jobs = []
for ticker in tickers:
    try:
        mapping_job = MappingJob(id_type="TICKER", id_value=ticker, exch_code="US")
        mapping_jobs.append(mapping_job)
    except ValidationError:
        print(f"Error when trying to build a MappingJob with {ticker=}")
        # Do something
        continue
```

And here is how to check for `HTTPError` in case exceptions need to be handled:

```python
from openfigi_client import OpenFigiSync, MappingJob
from openfigi_client.exceptions import HTTPError

mapping_jobs = [MappingJob(id_type="TICKER", id_value="IBM", exch_code="US")]

try:
    results = OpenFigiSync().map(mapping_jobs)
except HTTPError as e:
    print(f"{e}")
    # Do something
```

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "openfigi-client",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "api, bloomberg, figi, openfigi, python",
    "author": null,
    "author_email": "ljnsn <info@ljnsn.com>",
    "download_url": "https://files.pythonhosted.org/packages/d1/be/0376e3bde182145172281b064b57dadf2cf614f38f042b5c757e0df8efa3/openfigi_client-0.4.1.tar.gz",
    "platform": null,
    "description": "# openfigi-client\n\n[![python](https://img.shields.io/badge/python-3.10_%7C_3.11_%7C_3.12-blue)](https://gitlab.com/cryptoledger-x/openfigi-client)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![Black](https://img.shields.io/badge/code%20style-black-black)](https://black.readthedocs.io/en/stable/)\n\nPython wrapper for the [OpenFIGI API](https://www.openfigi.com/api) v3.\n\n## Table of contents\n\n- [About OpenFIGI](#about-openfigi)\n- [Installation](#installation)\n- [API key](#api-key)\n- [Mapping](#mapping)\n- [Filtering](#filtering)\n- [Troubleshooting](#troubleshooting)\n\n## About OpenFIGI\n\n- The **F**inancial **I**nstrument **G**lobal **I**dentifier (FIGI) is a\n  universal system for identifying instruments globally and across all asset\n  classes\n- OpenFIGI is an application programming interface that provides automated\n  access to mapping various symbols with their corresponding FIGI. It is\n  available at <https://www.openfigi.com/>\n- [openfigi_client](https://github.com/tlouarn/openfigi_client) is a thin Python\n  wrapper to access OpenFIGI\n\nThe API contains 3 endpoints:\n\n| endpoint | description                                          |\n| -------- | ---------------------------------------------------- |\n| /mapping | Map third-party identifiers to FIGIs                 |\n| /filter  | Filter for FIGIs using keywords and optional filters |\n| /search  | Search for FIGIs using keywords and optional filters |\n\n_Note: given that the _/search_ endpoint is strictly superseded by the _/filter_\nendpoint, we choose not to include it in the wrapper._\n\n## Installation\n\n**openfigi_client** is published on\n[PyPI](https://pypi.org/project/openfigi_client/). To install it, simply run:\n\n```commandline\npip install openfigi_client\n```\n\n## API key\n\nThe API can be used with or without API key. Getting an API key is free and\nloosens the [rate limits](https://www.openfigi.com/api#rate-limit).\n\nWhen instantiating the wrapper, the API key is optional:\n\n```python\nfrom openfigi_client import OpenFigiSync\n\nclient = OpenFigiSync()\nclient = OpenFigiSync(api_key=\"XXXXXXXXXX\")\n```\n\n## Async\n\nIn addition to the synchronous client, there is an equivalent asynchronous\nclient available. All examples below work equally, simply import\n`OpenFigiAsync` instead and `await` the calls.\n\n## Mapping\n\nThe `map()` method takes a list of `MappingJob` as argument and returns a list\nof `MappingJobResult`. The result of the request at index `i` in the list of\nmapping jobs is located at index `i` in the list of results.\n\n```python\nfrom openfigi_client import OpenFigiSync, MappingJob\n\nmapping_job = MappingJob(id_type=\"TICKER\", id_value=\"IBM\", exch_code=\"US\")\nmapping_jobs = [mapping_job]\nresults = OpenFigiSync().map(mapping_jobs)\n\n>>> results\n[\n    MappingJobResultFigiList(\n        data = [\n            FigiResult(\n                  figi='BBG000BLNNH6', \n                  security_type='Common Stock', \n                  market_sector='Equity', \n                  ticker='IBM', \n                  name='INTL BUSINESS MACHINES CORP', \n                  exch_code='US', \n                  share_class_figi='BBG001S5S399', \n                  composite_figi='BBG000BLNNH6', \n                  security_type2='Common Stock', \n                  security_description='IBM', \n                  metadata=None\n            )\n        ]\n    )\n]\n```\n\nA `MappingJobResult` can either be a `MappingJobResultFigiList`, a\n`MappingJobResultFigiNotFound` or a `MappingJobResultError`.\n\nThe `MappingJob` object has 2 required properties which are `id_type` and\n`id_value`. The other properties are optional but subject to specific rules in\ncase they are provided. These rules are modeled and checked using **Pydantic**.\n\nBelow is the full list of properties for `MappingJob`:\n\n| property                  | required | type | example                                  |\n| ------------------------- | -------- | ---- | ---------------------------------------- |\n| id_type                   | X        | str  | `\"TICKER\"`                               |\n| id_value                  | X        | str  | `\"IBM\"`                                  |\n| exch_code                 |          | str  | `\"UN\"`                                   |\n| mic_code                  |          | str  | `\"XNYS\"`                                 |\n| currency                  |          | str  | `\"USD\"`                                  |\n| market_sec_des            |          | str  | `\"Equity\"`                               |\n| security_type             |          | str  | `\"Common Stock\"`                         |\n| security_type_2           |          | str  | `\"Common Stock\"`                         |\n| include_unlisted_equities |          | bool |                                          |\n| option_type               |          | str  | `\"Call\"`                                 |\n| strike                    |          | list | `[100, 200]`                             |\n| contract_size             |          | list | `[0, 100]`                               |\n| coupon                    |          | list | `[0, 2.5]`                               |\n| expiration                |          | list | `[date(2023, 6, 1), date(2023, 12, 31)]` |\n| maturity                  |          | list | `[date(2023, 6, 1), date(2023, 12, 31)]` |\n| state_code                |          | str  | `\"AZ\"`                                   |\n\nSome of the properties in the `MappingJob` are \"enum-like\". For each of these\nproperties, it is possible to retrieve the current list of accepted values via\nspecific methods:\n\n| property        | method                   | examples |\n| --------------- | ------------------------ | -------- |\n| id_type         | `get_id_types()`         |          |\n| exch_code       | `get_exch_codes()`       |          |\n| mic_code        | `get_mic_codes()`        |          |\n| currency        | `get_currencies()`       |          |\n| market_sec_des  | `get_market_sec_des()`   |          |\n| security_type   | `get_security_types()`   |          |\n| security_type_2 | `get_security_types_2()` |          |\n| state_code      | `get_state_codes()`      |          |\n\nFor example, to retrieve the current values for `id_type`:\n\n```python\nfrom openfigi_client import OpenFigiSync\n\nid_types = OpenFigiSync().get_id_types()\n```\n\n## Filtering\n\nThe `filter()` method takes a `Filter` object as argument and returns a list of\n`FigiResult`.\n\n- The `Filter` object is very similar to the `MappingJob` object\n- The only difference are that the `id_type` and `id_value` are replaced by a\n  single `query` property\n- All the \"enum-like\" properties are the same and the list of accepted values is\n  the same\n- The maximum number of results is limited to 15,000\n\n| property                  | required | type | example                                  |\n| ------------------------- | -------- | ---- | ---------------------------------------- |\n| query                     | X        | str  | `\"SJIM\"`                                 |\n| exch_code                 |          | str  | `\"UN\"`                                   |\n| mic_code                  |          | str  | `\"XNYS\"`                                 |\n| currency                  |          | str  | `\"USD\"`                                  |\n| market_sec_des            |          | str  | `\"Equity\"`                               |\n| security_type             |          | str  | `\"Common Stock\"`                         |\n| security_type_2           |          | str  | `\"Common Stock\"`                         |\n| include_unlisted_equities |          | bool |                                          |\n| option_type               |          | str  | `\"Call\"`                                 |\n| strike                    |          | list | `[100, 200]`                             |\n| contract_size             |          | list | `[0, 100]`                               |\n| coupon                    |          | list | `[0, 2.5]`                               |\n| expiration                |          | list | `[date(2023, 6, 1), date(2023, 12, 31)]` |\n| maturity                  |          | list | `[date(2023, 6, 1), date(2023, 12, 31)]` |\n| state_code                |          | str  | `\"AZ\"`                                   |\n\nExample\n\n```python\nfrom openfigi_client import OpenFigiSync, Filter\n\nquery = Filter(query=\"SJIM\")\nresults = OpenFigiSync().filter(query)\n```\n\nIn order to know the total number of matches for a given query before starting\nto request them, it is possible to use the `get_total_number_of_matches()`\nmethod:\n\n```python\nfrom openfigi_client import OpenFigiSync, Filter\n\nquery = Filter(query=\"SJIM\")\nnumber_of_results = OpenFigiSync().get_total_number_of_matches(query)\n\n>>> number_of_results\n36\n```\n\n## Troubleshooting\n\nSeveral kinds of errors can occur.\n\n- `ValidationError`: the `MappingJob` and `Filter` objects are modelled using\n  **msgspec** and therefore need to be properly instantiated. If an error\n  occurs, a `msgspec.ValidationError` will be raised.\n\n- `HTTPError`: in case the status code of the HTTP response is not 200, an\n  HTTPError exception will be raised. Note: in case a particular symbol is not\n  found, the API will still respond with a status 200 and a `MappingNotFound`\n  object. HTTP errors only occur if there is a real error like a malformed\n  `MappingJob` request (which should not happen since all `MappingJob` objects\n  are checked by **Pydantic** prior to being sent), a rate limitation or an\n  internal server error.\n\nHere is how to check for `ValidationError` in case the mapping jobs are\ninstantiated programmatically:\n\n```python\nfrom msgspec import ValidationError\n\nfrom openfigi_client import MappingJob\n\ntickers = [\"IBM\", \"XRX\", \"TSLA\", None, \"MSFT\"]\n\nmapping_jobs = []\nfor ticker in tickers:\n    try:\n        mapping_job = MappingJob(id_type=\"TICKER\", id_value=ticker, exch_code=\"US\")\n        mapping_jobs.append(mapping_job)\n    except ValidationError:\n        print(f\"Error when trying to build a MappingJob with {ticker=}\")\n        # Do something\n        continue\n```\n\nAnd here is how to check for `HTTPError` in case exceptions need to be handled:\n\n```python\nfrom openfigi_client import OpenFigiSync, MappingJob\nfrom openfigi_client.exceptions import HTTPError\n\nmapping_jobs = [MappingJob(id_type=\"TICKER\", id_value=\"IBM\", exch_code=\"US\")]\n\ntry:\n    results = OpenFigiSync().map(mapping_jobs)\nexcept HTTPError as e:\n    print(f\"{e}\")\n    # Do something\n```\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) [2024] [ljnsn]  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "Python client for the OpenFIGI API.",
    "version": "0.4.1",
    "project_urls": null,
    "split_keywords": [
        "api",
        " bloomberg",
        " figi",
        " openfigi",
        " python"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ae471c6b762453e1ac21975e7dc5536c2589a353ce804bcf100e0e9c2a5e3bc0",
                "md5": "d7993887ea4f33c6cf4a129b67cd5288",
                "sha256": "7a9aa2dd53cb49cdb21272bef37ed1ff58f78a05f80f9dae1976a97b22d03298"
            },
            "downloads": -1,
            "filename": "openfigi_client-0.4.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d7993887ea4f33c6cf4a129b67cd5288",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 12151,
            "upload_time": "2024-09-18T18:16:03",
            "upload_time_iso_8601": "2024-09-18T18:16:03.250519Z",
            "url": "https://files.pythonhosted.org/packages/ae/47/1c6b762453e1ac21975e7dc5536c2589a353ce804bcf100e0e9c2a5e3bc0/openfigi_client-0.4.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d1be0376e3bde182145172281b064b57dadf2cf614f38f042b5c757e0df8efa3",
                "md5": "f203dcfad2ce8ec0cb8f2b2e596d0ec2",
                "sha256": "6819b39ec664b7cb7f5f48e395e4be1f9237b7caca36d1e69ad9497bbed9c8e6"
            },
            "downloads": -1,
            "filename": "openfigi_client-0.4.1.tar.gz",
            "has_sig": false,
            "md5_digest": "f203dcfad2ce8ec0cb8f2b2e596d0ec2",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 60977,
            "upload_time": "2024-09-18T18:16:24",
            "upload_time_iso_8601": "2024-09-18T18:16:24.577256Z",
            "url": "https://files.pythonhosted.org/packages/d1/be/0376e3bde182145172281b064b57dadf2cf614f38f042b5c757e0df8efa3/openfigi_client-0.4.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-18 18:16:24",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "openfigi-client"
}
        
Elapsed time: 0.31733s