ionq_core.ionq_client
IonQ-specific client convenience wrapper.
The IonQClient factory function is the recommended way to create an API client.
It reads the API key from the environment, configures retries with exponential
backoff, sets a descriptive User-Agent header, and wires up both the sync and
async httpx transports.
1# SPDX-FileCopyrightText: 2026 IonQ, Inc. 2# SPDX-License-Identifier: Apache-2.0 3 4"""IonQ-specific client convenience wrapper. 5 6The `IonQClient` factory function is the recommended way to create an API client. 7It reads the API key from the environment, configures retries with exponential 8backoff, sets a descriptive User-Agent header, and wires up both the sync and 9async httpx transports. 10""" 11 12__all__ = ["IonQClient", "__version__"] 13 14import os 15import platform 16import warnings 17from importlib.metadata import PackageNotFoundError 18from importlib.metadata import version as _pkg_version 19 20import httpx 21 22from ._transport import DEFAULT_MAX_RETRIES, RETRYABLE_STATUS_CODES, build_transport 23from .client import AuthenticatedClient 24from .extensions import ClientExtension, HookTransport 25 26try: 27 __version__ = _pkg_version("ionq-core") 28except PackageNotFoundError: 29 __version__ = "0.0.0" 30 31DEFAULT_BASE_URL = "https://api.ionq.co/v0.4" 32DEFAULT_TIMEOUT = httpx.Timeout(60.0, connect=10.0) 33_AUTH_PREFIX = "apiKey" 34_AUTH_HEADER = "Authorization" 35 36 37# Factory named in PascalCase (deliberately, not a class) so call sites read 38# like construction. Returns the generated `AuthenticatedClient`. 39def IonQClient( 40 *, 41 api_key: str | None = None, 42 base_url: str = DEFAULT_BASE_URL, 43 max_retries: int | None = None, 44 timeout: httpx.Timeout | None = None, 45 additional_user_agent: str | None = None, 46 extension: ClientExtension | None = None, 47 **kwargs, 48) -> AuthenticatedClient: 49 """Create an authenticated IonQ API client. 50 51 This is the recommended entry point for using the library. It handles 52 authentication, retry configuration, User-Agent construction, and transport 53 setup for both sync and async usage. 54 55 Args: 56 api_key: IonQ API key. If not provided, reads the ``IONQ_API_KEY`` 57 environment variable. 58 base_url: API base URL. Defaults to the IonQ production API. 59 max_retries: Maximum retry attempts for transient errors (429, 5xx). 60 Defaults to 2. Set to 0 to disable retries. 61 timeout: Request timeout as an ``httpx.Timeout`` instance. Defaults to 62 60 seconds with a 10-second connect timeout. 63 additional_user_agent: Extra token appended to the User-Agent header, 64 useful for identifying calling applications. 65 extension: A `ClientExtension` bundle provided by a downstream SDK. 66 Allows injecting hooks, custom headers, transport wrappers, and 67 error mappers. 68 **kwargs: Passed through to `AuthenticatedClient`. 69 70 Returns: 71 An `AuthenticatedClient` configured with retry transport and 72 authentication headers, ready for both sync and async API calls. 73 74 Raises: 75 ValueError: If no API key is provided and ``IONQ_API_KEY`` is not set. 76 77 Examples: 78 Basic usage with environment variable: 79 80 ```python 81 from ionq_core import IonQClient 82 from ionq_core.api.backends import get_backends 83 84 client = IonQClient() 85 backends = get_backends.sync(client=client) 86 ``` 87 88 Explicit configuration: 89 90 ```python 91 import httpx 92 from ionq_core import IonQClient 93 94 client = IonQClient( 95 api_key="your-api-key", 96 max_retries=5, 97 timeout=httpx.Timeout(30.0, connect=10.0), 98 ) 99 ``` 100 101 Async usage with context manager: 102 103 ```python 104 async with IonQClient() as client: 105 backends = await get_backends.asyncio(client=client) 106 ``` 107 """ 108 key = api_key or os.environ.get("IONQ_API_KEY") 109 if not key: 110 raise ValueError("api_key or IONQ_API_KEY environment variable required") 111 112 if not base_url.startswith("https://"): 113 warnings.warn( 114 f"base_url {base_url!r} does not use HTTPS. API keys will be sent in cleartext.", 115 UserWarning, 116 stacklevel=2, 117 ) 118 if kwargs.get("verify_ssl") is False: 119 warnings.warn( 120 "verify_ssl=False disables TLS certificate verification. " 121 "Your API key may be intercepted by a network attacker.", 122 UserWarning, 123 stacklevel=2, 124 ) 125 126 ext = extension or ClientExtension() 127 ua_parts = [ 128 f"ionq-core/{__version__}", 129 f"python/{platform.python_version()}", 130 f"httpx/{httpx.__version__}", 131 f"os/{platform.system().lower()}", 132 *filter(None, (additional_user_agent, ext.user_agent_token)), 133 ] 134 user_agent = " ".join(ua_parts) 135 effective_timeout = timeout or ext.timeout or DEFAULT_TIMEOUT 136 effective_retries = next(v for v in (max_retries, ext.max_retries, DEFAULT_MAX_RETRIES) if v is not None) 137 138 headers = {**ext.default_headers, "User-Agent": user_agent} 139 140 sync_transport = async_transport = build_transport( 141 effective_retries, 142 ext.retryable_status_codes or RETRYABLE_STATUS_CODES, 143 ) 144 145 if ext.event_hooks or ext.error_mapper: 146 sync_transport = HookTransport( 147 sync_transport, 148 ext.event_hooks, 149 debug=ext.debug_hooks, 150 error_mapper=ext.error_mapper, 151 ) 152 if ext.async_event_hooks or ext.error_mapper: 153 async_transport = HookTransport( 154 async_transport, 155 ext.async_event_hooks, 156 debug=ext.debug_hooks, 157 error_mapper=ext.error_mapper, 158 ) 159 if ext.transport_wrapper: 160 sync_transport = ext.transport_wrapper(sync_transport) 161 if ext.async_transport_wrapper: 162 async_transport = ext.async_transport_wrapper(async_transport) 163 164 client = AuthenticatedClient( 165 base_url=base_url, 166 token=key, 167 prefix=_AUTH_PREFIX, 168 auth_header_name=_AUTH_HEADER, 169 timeout=effective_timeout, 170 headers=headers, 171 httpx_args={"transport": sync_transport}, 172 **kwargs, 173 ) 174 # `set_async_httpx_client` bypasses `AuthenticatedClient`'s lazy auth-header 175 # injection (see generated `client.py::get_async_httpx_client`), so we merge 176 # `Authorization` in manually here. The `_verify_ssl` / `_follow_redirects` 177 # fields are private on the generated `AuthenticatedClient` but are the only 178 # way to mirror the caller's choices onto the async transport; do not add a 179 # public accessor in the hand-written layer — they belong to generated code. 180 client.set_async_httpx_client( 181 httpx.AsyncClient( 182 base_url=base_url, 183 headers={**headers, _AUTH_HEADER: f"{_AUTH_PREFIX} {key}"}, 184 timeout=effective_timeout, 185 transport=async_transport, 186 verify=client._verify_ssl, 187 follow_redirects=client._follow_redirects, 188 ) 189 ) 190 return client
def
IonQClient( *, api_key: str | None = None, base_url: str = 'https://api.ionq.co/v0.4', max_retries: int | None = None, timeout: httpx.Timeout | None = None, additional_user_agent: str | None = None, extension: ionq_core.ClientExtension | None = None, **kwargs) -> ionq_core.AuthenticatedClient:
40def IonQClient( 41 *, 42 api_key: str | None = None, 43 base_url: str = DEFAULT_BASE_URL, 44 max_retries: int | None = None, 45 timeout: httpx.Timeout | None = None, 46 additional_user_agent: str | None = None, 47 extension: ClientExtension | None = None, 48 **kwargs, 49) -> AuthenticatedClient: 50 """Create an authenticated IonQ API client. 51 52 This is the recommended entry point for using the library. It handles 53 authentication, retry configuration, User-Agent construction, and transport 54 setup for both sync and async usage. 55 56 Args: 57 api_key: IonQ API key. If not provided, reads the ``IONQ_API_KEY`` 58 environment variable. 59 base_url: API base URL. Defaults to the IonQ production API. 60 max_retries: Maximum retry attempts for transient errors (429, 5xx). 61 Defaults to 2. Set to 0 to disable retries. 62 timeout: Request timeout as an ``httpx.Timeout`` instance. Defaults to 63 60 seconds with a 10-second connect timeout. 64 additional_user_agent: Extra token appended to the User-Agent header, 65 useful for identifying calling applications. 66 extension: A `ClientExtension` bundle provided by a downstream SDK. 67 Allows injecting hooks, custom headers, transport wrappers, and 68 error mappers. 69 **kwargs: Passed through to `AuthenticatedClient`. 70 71 Returns: 72 An `AuthenticatedClient` configured with retry transport and 73 authentication headers, ready for both sync and async API calls. 74 75 Raises: 76 ValueError: If no API key is provided and ``IONQ_API_KEY`` is not set. 77 78 Examples: 79 Basic usage with environment variable: 80 81 ```python 82 from ionq_core import IonQClient 83 from ionq_core.api.backends import get_backends 84 85 client = IonQClient() 86 backends = get_backends.sync(client=client) 87 ``` 88 89 Explicit configuration: 90 91 ```python 92 import httpx 93 from ionq_core import IonQClient 94 95 client = IonQClient( 96 api_key="your-api-key", 97 max_retries=5, 98 timeout=httpx.Timeout(30.0, connect=10.0), 99 ) 100 ``` 101 102 Async usage with context manager: 103 104 ```python 105 async with IonQClient() as client: 106 backends = await get_backends.asyncio(client=client) 107 ``` 108 """ 109 key = api_key or os.environ.get("IONQ_API_KEY") 110 if not key: 111 raise ValueError("api_key or IONQ_API_KEY environment variable required") 112 113 if not base_url.startswith("https://"): 114 warnings.warn( 115 f"base_url {base_url!r} does not use HTTPS. API keys will be sent in cleartext.", 116 UserWarning, 117 stacklevel=2, 118 ) 119 if kwargs.get("verify_ssl") is False: 120 warnings.warn( 121 "verify_ssl=False disables TLS certificate verification. " 122 "Your API key may be intercepted by a network attacker.", 123 UserWarning, 124 stacklevel=2, 125 ) 126 127 ext = extension or ClientExtension() 128 ua_parts = [ 129 f"ionq-core/{__version__}", 130 f"python/{platform.python_version()}", 131 f"httpx/{httpx.__version__}", 132 f"os/{platform.system().lower()}", 133 *filter(None, (additional_user_agent, ext.user_agent_token)), 134 ] 135 user_agent = " ".join(ua_parts) 136 effective_timeout = timeout or ext.timeout or DEFAULT_TIMEOUT 137 effective_retries = next(v for v in (max_retries, ext.max_retries, DEFAULT_MAX_RETRIES) if v is not None) 138 139 headers = {**ext.default_headers, "User-Agent": user_agent} 140 141 sync_transport = async_transport = build_transport( 142 effective_retries, 143 ext.retryable_status_codes or RETRYABLE_STATUS_CODES, 144 ) 145 146 if ext.event_hooks or ext.error_mapper: 147 sync_transport = HookTransport( 148 sync_transport, 149 ext.event_hooks, 150 debug=ext.debug_hooks, 151 error_mapper=ext.error_mapper, 152 ) 153 if ext.async_event_hooks or ext.error_mapper: 154 async_transport = HookTransport( 155 async_transport, 156 ext.async_event_hooks, 157 debug=ext.debug_hooks, 158 error_mapper=ext.error_mapper, 159 ) 160 if ext.transport_wrapper: 161 sync_transport = ext.transport_wrapper(sync_transport) 162 if ext.async_transport_wrapper: 163 async_transport = ext.async_transport_wrapper(async_transport) 164 165 client = AuthenticatedClient( 166 base_url=base_url, 167 token=key, 168 prefix=_AUTH_PREFIX, 169 auth_header_name=_AUTH_HEADER, 170 timeout=effective_timeout, 171 headers=headers, 172 httpx_args={"transport": sync_transport}, 173 **kwargs, 174 ) 175 # `set_async_httpx_client` bypasses `AuthenticatedClient`'s lazy auth-header 176 # injection (see generated `client.py::get_async_httpx_client`), so we merge 177 # `Authorization` in manually here. The `_verify_ssl` / `_follow_redirects` 178 # fields are private on the generated `AuthenticatedClient` but are the only 179 # way to mirror the caller's choices onto the async transport; do not add a 180 # public accessor in the hand-written layer — they belong to generated code. 181 client.set_async_httpx_client( 182 httpx.AsyncClient( 183 base_url=base_url, 184 headers={**headers, _AUTH_HEADER: f"{_AUTH_PREFIX} {key}"}, 185 timeout=effective_timeout, 186 transport=async_transport, 187 verify=client._verify_ssl, 188 follow_redirects=client._follow_redirects, 189 ) 190 ) 191 return client
Create an authenticated IonQ API client.
This is the recommended entry point for using the library. It handles authentication, retry configuration, User-Agent construction, and transport setup for both sync and async usage.
Arguments:
- api_key: IonQ API key. If not provided, reads the
IONQ_API_KEYenvironment variable. - base_url: API base URL. Defaults to the IonQ production API.
- max_retries: Maximum retry attempts for transient errors (429, 5xx). Defaults to 2. Set to 0 to disable retries.
- timeout: Request timeout as an
httpx.Timeoutinstance. Defaults to 60 seconds with a 10-second connect timeout. - additional_user_agent: Extra token appended to the User-Agent header, useful for identifying calling applications.
- extension: A
ClientExtensionbundle provided by a downstream SDK. Allows injecting hooks, custom headers, transport wrappers, and error mappers. - **kwargs: Passed through to
AuthenticatedClient.
Returns:
An
AuthenticatedClientconfigured with retry transport and authentication headers, ready for both sync and async API calls.
Raises:
- ValueError: If no API key is provided and
IONQ_API_KEYis not set.
Examples:
Basic usage with environment variable:
from ionq_core import IonQClient from ionq_core.api.backends import get_backends client = IonQClient() backends = get_backends.sync(client=client)Explicit configuration:
import httpx from ionq_core import IonQClient client = IonQClient( api_key="your-api-key", max_retries=5, timeout=httpx.Timeout(30.0, connect=10.0), )Async usage with context manager:
async with IonQClient() as client: backends = await get_backends.asyncio(client=client)
__version__ =
'0.1.1'