BeaconBox
Esc

Type to search.

Dashboard

SDKs

Python and PHP. What they do for you that a hand-rolled client will not, and the settings worth changing before you ship.

Package Requires Sync Async
Python beaconbox 3.10+, one dependency (httpx) yes yes
PHP beaconbox/beaconbox-php 8.1+, ext-curl and PSR interfaces yes no

Both cover the same twenty operations and are MIT licensed.

# No SDK needed. The API is JSON over HTTPS and every page here has a curl form.

What they handle

Idempotency, generated per call and reused across the SDK’s own retries, so a connection that dies with the answer in flight cannot become a duplicate email and a duplicate charged text. Pass your own key whenever you have a natural one.

Retries, with exponential backoff and full jitter, on connection failures, 429 and 5xx. Never on a 4xx, because sending the same wrong request again asks the same question.

Webhook signatures, including the timestamp check and a constant-time comparison.

Pagination, as a generator, so a year of history is not held in memory.

Configuration

# Configuration for a hand-rolled client is three rules:
#   - refuse a non-https base URL for anything but localhost
#   - never follow redirects (the Authorization header follows them)
#   - send an Idempotency-Key on every POST, and reuse it when you retry

Reuse one client. It holds a connection pool, and in Python it is safe to share between threads.

user_agent_suffix makes your integration identifiable in a support conversation. It is worth setting.

Turn the SDK’s retries off inside a job runner

Backpressure

A Retry-After is honoured in full rather than clamped to the backoff ceiling, because it is the server’s own answer to when it will be ready and retrying earlier only earns a second refusal. Jitter is added on top of it, not sampled from within it, since every worker that hit the same 429 was handed the same number.

If the server asks for longer than the policy will sit through (30 seconds by default), the SDK stops instead of retrying early, and raises RateLimitError / RateLimitException with the server’s number on it so you can schedule a real retry.

The two policies are deliberately identical down to the constants, so a shop running one service in each language gets the same behaviour out of a bad ten minutes.

Concurrency, in Python

max_connections is a ceiling, and exceeding it does not look like one: the overflow queues, and a queue wait that outlives the pool timeout surfaces as APIConnectionError, never as anything mentioning a pool. An asyncio.gather over a few hundred pushes makes that easy to hit, so raise the ceiling to match your fan-out.

Async, in Python

Same client, same signatures, awaited. Nothing blocks the event loop, including retry backoff.

python
from beaconbox import AsyncBeaconBox, OrderStatus

async with AsyncBeaconBox() as client:
  result = await client.messages.push(
      recipient_email="buyer@example.com",
      subject="Your order has shipped",
      body="Tracking XY123456789EE.",
      reference="#A-10294",
      order_status=OrderStatus.SHIPPED,
      idempotency_key="order-4711-shipped",
  )

  async for message in client.messages.iterate(recipient_email="buyer@example.com"):
      print(message.id, message.delivery.opened)

Logging

Both are silent until you ask, because a library that writes to stderr of its own accord is a library fighting the host application.

# Nothing to configure. Log what you send and what comes back,
# and keep the Authorization header out of it.

DEBUG logs every request and response with status and elapsed time. WARNING logs a retry and giving up after the last one. Nothing is emitted at INFO or above in normal operation, so a warning from either SDK always means something went wrong.

Bringing your own HTTP client

In PHP, inject any PSR-18 client. The bundled curl transport keeps one persistent handle for connection reuse, which makes it unsafe to share across coroutines, so Swoole and ReactPHP users should inject.

In Python, pass your own httpx.Client, and then you own closing it. timeout and verify are refused alongside it, never silently ignored, because accepting a setting while doing nothing with it is how somebody discovers during an incident that the timeout they set was never applied.

Source

Both SDKs are mirrored to public repositories under beaconbox-eu: beaconbox-python and beaconbox-php.