Skip to content

Python

Starting point for services, workers, and scheduled jobs that call Estia API from Python.

The main thing to get right early: token reuse. A new token per request works in a demo, but in production it adds noise and unnecessary complexity.

Full example with requests

import os
import time
import threading
import requests
from typing import Any, Optional


class EstiaApiClient:
    def __init__(
        self,
        client_id: str,
        client_secret: str,
        api_base_url: str = "https://api.insurancegateway.gr",
        auth_url: str = "https://auth.insurancegateway.gr/realms/estia/protocol/openid-connect/token",
    ) -> None:
        self.client_id = client_id
        self.client_secret = client_secret
        self.api_base_url = api_base_url.rstrip("/")
        self.auth_url = auth_url
        self._token: Optional[str] = None
        self._token_expires_at: float = 0.0
        self._token_lock = threading.Lock()

    def _get_token(self) -> str:
        if self._token and time.time() < self._token_expires_at - 300:
            return self._token

        with self._token_lock:
            if self._token and time.time() < self._token_expires_at - 300:
                return self._token

            resp = requests.post(
                self.auth_url,
                data={
                    "grant_type": "client_credentials",
                    "client_id": self.client_id,
                    "client_secret": self.client_secret,
                },
                timeout=10,
            )
            resp.raise_for_status()
            body = resp.json()
            self._token = body["access_token"]
            self._token_expires_at = time.time() + body["expires_in"]
            return self._token

    def get(self, path: str) -> Any:
        return self._request("GET", path)

    def post(self, path: str, json_body: Any) -> Any:
        return self._request("POST", path, json=json_body)

    def _request(self, method: str, path: str, **kwargs: Any) -> Any:
        token = self._get_token()
        resp = requests.request(
            method,
            f"{self.api_base_url}{path}",
            headers={"Authorization": f"Bearer {token}"},
            timeout=30,
            **kwargs,
        )
        resp.raise_for_status()
        return resp.json()

Example usage

client = EstiaApiClient(
    client_id=os.environ["ESTIA_CLIENT_ID"],
    client_secret=os.environ["ESTIA_CLIENT_SECRET"],
)

brands = client.get("/intersalonica/auto/brands")

Async variant with httpx

import httpx

class AsyncEstiaApiClient:
    def __init__(self, client_id: str, client_secret: str):
        self.client_id = client_id
        self.client_secret = client_secret
        self._token: Optional[str] = None
        self._token_expires_at: float = 0.0
        self._client = httpx.AsyncClient(base_url="https://api.insurancegateway.gr")

    async def _get_token(self) -> str:
        if self._token and time.time() < self._token_expires_at - 300:
            return self._token
        resp = await self._client.post(
            "https://auth.insurancegateway.gr/realms/estia/protocol/openid-connect/token",
            data={
                "grant_type": "client_credentials",
                "client_id": self.client_id,
                "client_secret": self.client_secret,
            },
        )
        resp.raise_for_status()
        body = resp.json()
        self._token = body["access_token"]
        self._token_expires_at = time.time() + body["expires_in"]
        return self._token

    async def get(self, path: str) -> Any:
        token = await self._get_token()
        resp = await self._client.get(path, headers={"Authorization": f"Bearer {token}"})
        resp.raise_for_status()
        return resp.json()