Skip to content

Python SDK

Reference
For:
Developers

The HostRepute Python SDK, hostrepute version 0.1.0, is a typed client for the REST API v1 with a synchronous Client and an AsyncClient built on httpx. It needs Python 3.10 or later, httpx (0.27 up to, not including, 0.29) and typing_extensions. Version 0.1.0 manages domains and IPs, check profiles, alert channels and delivery hours well, but can’t read check results or metrics yet: read known problems in 0.1.0 before you build on it.

Install (when it's published)
pip install hostrepute

hostrepute.Client takes a team API token, or a hostrepute.Config with the token and other settings. Create the token in Settings, then API tokens with the abilities your code needs; Authentication and API tokens explains them. The SDK doesn’t read environment variables itself: the examples on this page read the token from HOSTREPUTE_API_TOKEN.

main.py
import os
import hostrepute
config = hostrepute.Config(
token=os.environ["HOSTREPUTE_API_TOKEN"],
timeout=45.0,
user_agent="deploy-checks/1.4",
)
with hostrepute.Client(config) as client:
for host in client.hosts.iterate(per_page=100):
print(host.host, host.is_blocklisted)

Leaving the with block closes the client’s connections; without it, call client.close(). Config raises a ValueError for an empty token, a base URL without a scheme or host, a timeout of zero or less, or an invalid retry setting. Client.builder() offers the same settings as with_… methods, such as Client.builder().with_token(token).with_timeout(45.0).build().

Config field Default What it does
token required The team API token, sent as Authorization: Bearer <token>.
base_url https://app.hostrepute.com The API’s address. Give the origin only: the SDK adds /api/v1/… to every path.
timeout 30.0 seconds The httpx read, write and pool timeouts.
connect_timeout 10.0 seconds The httpx connect timeout.
user_agent hostrepute-python/0.1.0 httpx/<version> The User-Agent header.
retry_max_attempts, retry_base_delay_ms, retry_max_delay_ms 3, 200, 5000 The retry policy. See retries.
default_headers {} Extra headers on every request. They are applied last, so they also replace Authorization or User-Agent if you name them.

To use your own httpx.Client (for proxies or instrumentation), pass it as hostrepute.Client(config, http_client=...). The SDK then uses it as is: timeout and connect_timeout don’t apply, and the SDK doesn’t close it.

hostrepute.AsyncClient has the same resources and methods as coroutines; iterate() is an async iterator:

List domains and IPs with the async client
async with hostrepute.AsyncClient(
hostrepute.Config(token=os.environ["HOSTREPUTE_API_TOKEN"])
) as client:
async for host in client.hosts.iterate(per_page=100):
print(host.host)

The client has one attribute per API resource. The names follow the API, not the portal:

In HostRepute Attribute Methods API
Domains and IPs client.hosts list, iterate, get, create, update, delete /api/v1/hosts
Blocklists client.data_sources list, iterate, get /api/v1/data-sources
Check profiles client.monitoring_profiles list, iterate, get, create, update, delete /api/v1/data-source-groups
Alert channels client.alerts list, iterate, get, create, update, delete /api/v1/alerts
Delivery hours client.contact_groups list, iterate, get, create, update, delete /api/v1/alert-groups
Blocklist checks client.blacklist check, status, check_and_wait, wait_for /api/v1/blacklist
Certificate checks client.certificates check, status, check_and_wait, wait_for /api/v1/certificates
Email authentication checks client.deliverability check, status, check_and_wait, wait_for /api/v1/deliverability
Prometheus metrics client.metrics get /api/v1/metrics
  • list(page=..., per_page=...) returns a Page with data, meta, links, current_page, last_page, per_page, total and has_next_page. per_page is 1–100; the API sends 15 when you leave it out. iterate() walks every page, 50 items per request unless you set per_page. Read Pagination.
  • get, update and delete take the uuid, except data_sources.get, which takes the blocklist’s numeric id.
  • update sends PUT with the fields you pass, and raises a ValueError when you pass none.
  • The SDK has no method for sending a test alert. Call POST /api/v1/alerts/{alert}/test directly.

Every domain or IP needs a check profile. The SDK calls it monitoring_profile_uuid and sends it as data_source_group_uuid:

Add a domain with the default check profile
profiles = client.monitoring_profiles.list()
profile = next(p for p in profiles.data if p.is_default)
host = client.hosts.create(
name="Mail",
host="mail.example.com",
monitoring_profile_uuid=profile.uuid,
)

Delivery hours are contact_groups in the SDK. schedule is a dict that the SDK sends unchanged, so give it the shape the API expects:

  • Keys mon, tue, wed, thu, fri, sat and sun, each with enabled, start and end. Times are HH:MM, and an enabled day must end after it starts.
  • A day you leave out gets no alerts. Leave schedule out entirely for delivery hours that are open at any time.
  • update leaves out None arguments, so it can’t turn existing delivery hours back to any time. Send "schedule": null with the REST API instead.
  • The SDK’s README shows a days and hours shape: the API rejects it with 422.
Weekdays 08:00 to 18:00 in New York
weekday = {"enabled": True, "start": "08:00", "end": "18:00"}
weekend = {"enabled": False, "start": "08:00", "end": "18:00"}
hours = client.contact_groups.create(
name="Business hours",
timezone="America/New_York",
schedule={
"mon": weekday, "tue": weekday, "wed": weekday, "thu": weekday, "fri": weekday,
"sat": weekend, "sun": weekend,
},
)

An alert channel is alerts. settings holds the field the channel type needs (email here; Set up alert channels lists them all), and contact_group_uuids the delivery hours it follows:

An email alert channel that follows the delivery hours
channel = client.alerts.create(
type=hostrepute.AlertType.EMAIL,
frequency=hostrepute.AlertFrequency.ONCE_ON_LISTED_DELISTED,
settings={"email": "ops@example.com"},
contact_group_uuids=[hours.uuid],
)

For frequency, use AlertFrequency.ONCE_ON_LISTED_DELISTED (Listed and removed in the portal) or AlertFrequency.ONCE_PER_CHECK_WHILE_LISTED (Only when listed). The SDK also defines AlertFrequency.DAILY, which the API rejects with 422, and AlertFrequency.EVERY_CHECK, a retired value that the API stores as once_on_listed_delisted.

Each check uses 1 credit (credit costs). check() starts a blocklist or certificate check and returns it with status pending; its uuid identifies the check:

Start a blocklist check
scan = client.blacklist.check(host="mail.example.com")
print(scan.uuid, scan.status)

certificates.check() also takes protocol, port and sni, and both take callback_url to be told when the check ends. A callback’s signature can’t be verified yet, so treat the callback as a notification and read the result from the status endpoint, as Receive webhooks explains. The SDK’s own webhook helpers don’t match what HostRepute sends, so don’t use them.

The SDK also has status(uuid), wait_for(uuid, timeout=..., poll_interval=...) and check_and_wait(host, ..., timeout=..., poll_interval=...). The wait helpers poll status until the check is completed or failed, for up to timeout (60 seconds by default) every poll_interval (2 seconds), and then raise hostrepute.TimeoutError with the scan_uuid. In 0.1.0 they don’t work against the API (see below), so poll GET /api/v1/blacklist/status/{uuid} or GET /api/v1/certificates/status/{uuid} yourself, or use a callback, as Run checks and wait for results shows.

  • Status and wait helpers fail while a check runs. status(), wait_for() and check_and_wait() on blacklist and certificates raise ValueError: 'processing' is not a valid ScanStatus as soon as the API reports the check as running. It’s a plain ValueError, so except hostrepute.HostreputeError doesn’t catch it.
  • Results are missing. The blocklist and certificate check objects have no result and no counts (such as how many blocklists list the host), so even a completed check doesn’t tell you what it found.
  • Email authentication checks always raise. Every deliverability method raises a ValueError (or a DecodingError for failed) on the API’s status, after the API has run the check and used the credit.
  • metrics.get() raises a DecodingError, because it reads the Prometheus text as JSON.
  • 402 has no typed error. Read err.body["required_credits"] and err.body["available_credits"] on the ApiError.

The SDK retries a failed request up to 3 attempts in total:

  • 429 is retried for every method. A 429 means the API refused the request before running it, so a retried check isn’t charged twice.
  • 5xx and network errors are retried only for GET, HEAD, OPTIONS, PUT and DELETE. A POST that creates something or starts a check isn’t retried, so the SDK never starts a check twice.
  • Waits. When the response has Retry-After (seconds or an HTTP date), the SDK waits that long. Otherwise it waits a random delay between zero and 200 ms × 2attempt − 1, capped at 5 seconds.
  • When the attempts run out, you get the error for the last response, for example a RateLimitError.
Five attempts with longer waits
client = hostrepute.Client(
hostrepute.Config(
token=os.environ["HOSTREPUTE_API_TOKEN"],
retry_max_attempts=5,
retry_base_delay_ms=500,
retry_max_delay_ms=10_000,
)
)

retry_max_attempts=1 turns retries off. Rate limits lists the limits behind 429.

All SDK errors extend hostrepute.HostreputeError. API errors extend ApiError, with status_code, errors (field names to messages), body (the parsed response) and response; the message is str(err).

Status or cause Class Extra fields
401 AuthenticationError
403 AuthorizationError
404 NotFoundError
422 ValidationError errors
429 RateLimitError retry_after, in seconds
5xx ServerError
Any other status, including 402 ApiError
No response, or an httpx timeout NetworkError __cause__, the httpx error
A wait helper’s timeout TimeoutError scan_uuid
A response the SDK can’t read DecodingError

hostrepute.TimeoutError isn’t Python’s built-in TimeoutError: catch it by its full name.

Handle the errors a check can return
try:
client.blacklist.check(host="mail.example.com")
except hostrepute.ValidationError as err:
print(err.errors)
except hostrepute.RateLimitError as err:
print(f"Rate limited, retry in {err.retry_after} s")
except hostrepute.ApiError as err:
if err.status_code != 402:
raise
print(f"Needs {err.body['required_credits']} credits, {err.body['available_credits']} left")

Errors shows the response body behind each status.