HTTP Handlers¶
Note
For the full method reference see the API Reference → HTTP section.
All HTTP handlers are built on httpx and expose both a synchronous and an asynchronous API. A single handler instance can be used for both.
Choosing the right handler¶
| Handler | When to use |
|---|---|
HttpHandler |
Any REST API that needs no special authentication |
BazeHttpHandler |
Bazefield — API key or user/password auth |
CceeHttpHandler |
CCEE Dados Abertos — handles WAF warm-up automatically |
OnsHttpHandler |
ONS SAGER / SINAPSE — full OAuth2 multi-step login |
Connection properties¶
All HTTP handlers take an HttpConnProperties (or a subclass) as their first argument:
from echo_connhandler import HttpConnProperties
props = HttpConnProperties(
host="https://api.example.com",
api_key="my-secret-key", # preferred auth
# user="...", password="...", # alternative auth (cannot use both)
conn_timeout=30, # seconds to establish a connection
request_timeout=300, # seconds to wait for a response
verify_ssl=False, # disable SSL verification
headers={"accept": "application/json"},
payload_kwargs={"api_token": "xyz"}, # merged into every request body
payload_kwargs_key="json", # where to merge payload_kwargs
)
ONS — extended properties¶
OnsHttpHandler requires OnsHttpConnProperties, which adds a login_target field:
from echo_connhandler import OnsHttpConnProperties, OnsHttpHandler
props = OnsHttpConnProperties(
host="https://integra.ons.org.br/api/sager/renovaveis/api",
user="my.user",
password="my-password",
login_target="SAGER", # or "SINAPSE"
)
handler = OnsHttpHandler(props)
Synchronous usage¶
Basic requests¶
from echo_connhandler import HttpConnProperties, BazeHttpHandler
props = HttpConnProperties(
host="https://bazefield.echoenergia.com.br",
api_key="my-api-key",
)
handler = BazeHttpHandler(props)
response = handler.get("BazeField.Services/api/allocationTypes")
data = response.json()
Endpoints can be relative (appended to host) or full URLs — the handler normalises them automatically.
All HTTP verbs¶
handler.get("api/resource")
handler.post("api/resource", json={"key": "value"})
handler.put("api/resource/1", json={"key": "updated"})
handler.patch("api/resource/1", json={"key": "patched"})
handler.delete("api/resource/1")
Every method accepts response_ok (default 200) — a RuntimeError is raised if the status code doesn't match. Pass response_ok=None to skip the check entirely.
Context manager¶
with BazeHttpHandler(props) as handler:
data = handler.get("api/endpoint").json()
# connection is closed automatically
Accessing the underlying client¶
The connection property returns the live httpx.Client, useful for inspecting cookies or headers after requests:
handler.get("api/session/start")
print(handler.connection.cookies)
Asynchronous usage¶
All HTTP handlers expose async counterparts for every sync method. Async connections are lazy — the async client is only created when you first call an async_* method (or await handler.async_reconnect() explicitly). No changes to the constructor are needed.
Basic async requests¶
import asyncio
from echo_connhandler import HttpConnProperties, BazeHttpHandler
props = HttpConnProperties(
host="https://bazefield.echoenergia.com.br",
api_key="my-api-key",
)
async def main():
async with BazeHttpHandler(props, skip_connect=True) as handler:
response = await handler.async_get("BazeField.Services/api/allocationTypes")
data = response.json()
asyncio.run(main())
Tip
Use skip_connect=True for async-only usage to avoid creating an unnecessary synchronous connection on construction.
All async HTTP verbs¶
await handler.async_get("api/resource")
await handler.async_post("api/resource", json={"key": "value"})
await handler.async_put("api/resource/1", json={"key": "updated"})
await handler.async_patch("api/resource/1", json={"key": "patched"})
await handler.async_delete("api/resource/1")
Async context manager¶
async with BazeHttpHandler(props, skip_connect=True) as handler:
data = (await handler.async_get("api/endpoint")).json()
# async_close() called automatically
Explicit async lifecycle¶
handler = BazeHttpHandler(props, skip_connect=True)
await handler.async_reconnect() # connect explicitly
data = (await handler.async_get("api/endpoint")).json()
await handler.async_close() # close explicitly
Accessing the async client¶
The async_connection property returns the live httpx.AsyncClient after the first async request:
await handler.async_get("api/session/start")
print(handler.async_connection.cookies)
Mixed sync + async on the same instance¶
Sync and async connections are independent. A single instance can serve both:
handler = BazeHttpHandler(props) # sync connects here
sync_data = handler.get("api/resource").json() # uses sync client
async_data = (await handler.async_get("api/resource")).json() # lazy async connect
Warning
Do not call async_* methods on the same handler instance concurrently from multiple coroutines. The async client is stored as a plain instance attribute (not task-local). For concurrent usage, create one handler per coroutine.
Authentication patterns¶
API key¶
props = HttpConnProperties(host="https://api.example.com", api_key="my-key")
handler = BazeHttpHandler(props)
# Authorization: Bearer my-key is set on every request automatically
User / password¶
props = HttpConnProperties(
host="https://bazefield.echoenergia.com.br",
user="my.user",
password="my-password",
)
handler = BazeHttpHandler(props)
# Login POST is called during _connect()
Per-request token (payload_kwargs)¶
Some APIs require a token injected into every request body:
props = HttpConnProperties(
host="https://api.example.com",
payload_kwargs={"api_token": "xyz"},
payload_kwargs_key="json", # merges into the json= body
)
handler = HttpHandler(props)
# Every handler.post("endpoint", json={...}) will automatically include api_token
OAuth2 (ONS)¶
OnsHttpHandler handles the full OAuth2 flow transparently during _connect():
- GET federation endpoint (sets cookies)
- POST OAuth2 token endpoint → JWT retrieved
- Bearer token injected into session headers
- (SINAPSE only) GET profile endpoint →
Perfil-Selecionadoheader set
No extra code needed — just pass the right OnsHttpConnProperties.
Retry behaviour¶
All requests (sync and async) automatically retry using exponential backoff. On each failure the handler reconnects before the next attempt.
Default parameters (configurable in the constructor):
| Parameter | Default | Meaning |
|---|---|---|
max_retries |
8 | Attempts per request |
max_conn_retries |
3 | Attempts to re-establish the connection |
retry_wait_time |
30 s | Maximum wait between retries |
exponential_min_retry_wait_time |
0.1 s | Minimum wait between retries |
exponential_multiplier |
1 | Backoff multiplier |
# Low-latency endpoint: fail fast
handler = BazeHttpHandler(props, max_retries=0)
# Resilient background job: keep retrying for longer
handler = BazeHttpHandler(props, max_retries=12, retry_wait_time=60)