|
1 | | -# Data Platform APIs HTTP REST Application using HTTPX |
| 1 | +# Data Platform APIs HTTP REST Application using Httpx |
2 | 2 |
|
3 | 3 | - Version: 1.0 |
4 | 4 | - Last update: Mar 2026 |
5 | 5 | - Environment: Python + JupyterLab + Data Platform Account |
6 | 6 | - Prerequisite: Data Platform access/entitlements |
7 | 7 |
|
8 | | -Python examples that use [`httpx`](https://www.python-httpx.org/) to authenticate with [LSEG Data Platform APIs](https://developers.lseg.com/en/api-catalog/refinitiv-data-platform/refinitiv-data-platform-apis) (RDP, also known as Delivery Platform) using OAuth 2.0 Password Grant, then call sample REST endpoints — covering both synchronous and asynchronous patterns. |
| 8 | +## Overview |
| 9 | + |
| 10 | +The [Requests](https://requests.readthedocs.io/en/latest/) library is widely regarded as *the de facto* standard HTTP client for Python applications. Many Python developers first learn REST API calls through Requests — including through our [Data Platform APIs Tutorials](https://developers.lseg.com/en/api-catalog/refinitiv-data-platform/refinitiv-data-platform-apis/tutorials) (or you can try RDP HTTP operations with the [built-in http.client](https://docs.python.org/3/library/http.client.html) if you enjoy a challenge.). |
| 11 | + |
| 12 | +That said, there are other Python HTTP libraries worth considering — [HTTPX](https://www.python-httpx.org/), [Aiohttp](https://docs.aiohttp.org/en/stable/), [Urllib3](https://urllib3.readthedocs.io/en/stable/), [Grequests](https://pypi.org/project/grequests/), [PycURL](http://pycurl.io/docs/latest/index.html), and more — each offering different trade-offs in performance and features that may better suit your requirements. |
| 13 | + |
| 14 | +I was drawn to HTTPX because it provides a **requests-compatible API** while also supporting **asynchronous operations** out of the box. That combination made migrating from Requests to HTTPX straightforward, with the added benefit of async support when needed. |
| 15 | + |
| 16 | +This project demonstrates how to use [`httpx`](https://www.python-httpx.org/) to authenticate and retrieve data from [LSEG Data Platform APIs](https://developers.lseg.com/en/api-catalog/refinitiv-data-platform/refinitiv-data-platform-apis) via HTTP endpoints — covering both synchronous and asynchronous patterns for comparison. |
| 17 | + |
| 18 | +**Note**: A basic knowledge of Python [built-in asyncio](https://docs.python.org/3/library/asyncio.html) library is required to understand example codes. |
9 | 19 |
|
10 | 20 | ## What is Data Platform APIs? |
11 | 21 |
|
@@ -79,6 +89,69 @@ async def main(): |
79 | 89 | asyncio.run(main()) |
80 | 90 | ``` |
81 | 91 |
|
| 92 | +## What are Synchronous and Asynchronous Execution Models? |
| 93 | + |
| 94 | +**Synchronous** code runs tasks one at a time in a strict sequence — each task must finish before the next one starts. The application pauses and waits at every blocking call. For example, the `httpx.get()` function call below (equivalent to `requests.get()`) blocks the entire program until the HTTP response arrives: |
| 95 | + |
| 96 | +```python |
| 97 | +import httpx |
| 98 | + |
| 99 | +def fetch(url): |
| 100 | + """Fetch the content of the URL synchronously.""" |
| 101 | + r = httpx.get(url, verify=False) |
| 102 | + print("Fetched:", url, "status:", r.status_code) |
| 103 | + return r.text |
| 104 | + |
| 105 | +def main(): |
| 106 | + """ Main function.""" |
| 107 | + fetch("https://example.org") |
| 108 | + print("This line prints ONLY after the request is done!") |
| 109 | + |
| 110 | +if __name__ == "__main__": |
| 111 | + main() |
| 112 | +``` |
| 113 | + |
| 114 | + |
| 115 | + |
| 116 | +If the HTTP request takes 60 seconds, the program idles for those 60 seconds before executing the next line. For a single request this is fine, but it becomes a bottleneck when you need to fetch data for many symbols or endpoints. |
| 117 | + |
| 118 | +On the other hand, **Asynchronous** code allows multiple tasks to run concurrently in a non-blocking manner. While one task is waiting for I/O (such as a network response), the event loop can hand control to another task (execute next line of codes) instead of sitting idle. The example below uses `asyncio.create_task()` to launch a fetch in the background and immediately continues to the next line — without waiting for the response: |
| 119 | + |
| 120 | +```python |
| 121 | +import asyncio |
| 122 | +import httpx |
| 123 | + |
| 124 | +async def fetch(url): |
| 125 | + """Fetch the content of the URL asynchronously.""" |
| 126 | + async with httpx.AsyncClient(verify=False) as client: |
| 127 | + r = await client.get(url) |
| 128 | + print("Fetched:", url, "status:", r.status_code) |
| 129 | + return r.text |
| 130 | + |
| 131 | +async def main(): |
| 132 | + """ Main function.""" |
| 133 | + asyncio.create_task(fetch("https://example.org")) |
| 134 | + print("Task launched and not awaited!") |
| 135 | + # Sleep to allow the fetch task to complete before the program exits. |
| 136 | + await asyncio.sleep(2) |
| 137 | +if __name__ == "__main__": |
| 138 | + asyncio.run(main()) |
| 139 | +``` |
| 140 | + |
| 141 | + |
| 142 | + |
| 143 | +The real payoff of async comes when you have **many requests to make**. With `asyncio.gather()`, you can fire all of them concurrently so the total wall-clock time is roughly that of the single slowest response — instead of the sum of all response times. That is exactly the pattern used in `example_async_gather.py` and `async_call_nb.ipynb` examples for fetching multiple RICs. |
| 144 | + |
| 145 | +## Prerequisites |
| 146 | + |
| 147 | +- Python 3.11+ (required for `asyncio.TaskGroup` and `except*`) |
| 148 | +- LSEG Data Platform credentials with Historical Pricing permission: |
| 149 | + - Machine ID |
| 150 | + - Password |
| 151 | + - AppKey |
| 152 | + |
| 153 | +If you do not have access yet, contact your LSEG representative or account manager. |
| 154 | + |
82 | 155 | ## Project Structure |
83 | 156 |
|
84 | 157 | ``` |
@@ -171,15 +244,7 @@ Demonstrates: |
171 | 244 | - `POST /auth/oauth2/v1/revoke` — session revocation using HTTP Basic Auth |
172 | 245 | - Per-call `verify=False` passed directly to each `httpx` function |
173 | 246 |
|
174 | | -## Prerequisites |
175 | 247 |
|
176 | | -- Python 3.11+ (required for `asyncio.TaskGroup` and `except*`) |
177 | | -- LSEG RDP credentials: |
178 | | - - Machine ID |
179 | | - - Password |
180 | | - - AppKey |
181 | | - |
182 | | -If you do not have access yet, contact your LSEG representative or account manager. |
183 | 248 |
|
184 | 249 | ## Setup |
185 | 250 |
|
@@ -235,7 +300,15 @@ Apache 2.0. See [LICENSE.md](LICENSE.md). |
235 | 300 |
|
236 | 301 | ## References |
237 | 302 |
|
238 | | -- https://realpython.com/async-io-python/ |
239 | | -- https://www.twilio.com/en-us/blog/asynchronous-http-requests-in-python-with-httpx-and-asyncio |
240 | | -- https://docs.python.org/3/library/asyncio-task.html#task-groups |
| 303 | +For further details, please check out the following resources: |
| 304 | + |
| 305 | +- [LSEG Data Platform](https://developers.lseg.com/en/api-catalog/refinitiv-data-platform/refinitiv-data-platform-apis) on the [LSEG Developers Portal](https://developers.lseg.com/en/) website. |
| 306 | +- [HTTPX library](https://www.python-httpx.org/) and [GitHub](https://github.com/encode/httpx) pages. |
| 307 | +- [Python Asyncio library](https://docs.python.org/3/library/asyncio.html) page. |
| 308 | +- [Python's asyncio: A Hands-On Walkthrough](https://realpython.com/async-io-python/) |
| 309 | +- [Asynchronous HTTP Requests in Python with HTTPX and asyncio](https://www.twilio.com/en-us/blog/asynchronous-http-requests-in-python-with-httpx-and-asyncio) |
| 310 | +- [Asyncio gather function document](https://docs.python.org/3/library/asyncio-task.html#asyncio.gather) page. |
| 311 | +- [Asyncio TaskGroup function document](https://docs.python.org/3/library/asyncio-task.html#task-groups) page. |
| 312 | + |
| 313 | +For any questions related to Data Platform APIs, please use the [Developers Community Q&A page](https://community.developers.refinitiv.com/). |
241 | 314 |
|
0 commit comments