Skip to main content

pharos-sdk

Official Python SDK for Pharos Connect.

Installation

pip install pharos-sdk

Supports Python 3.8+.

API Client

Use the client directly when your application owns a long-lived process.

import os

from pharos_sdk import PharosClient

client = PharosClient(os.environ["PHAROS_API_KEY"])
latest = client.declarations.latest(limit=10)

for summary in latest.data:
    declaration = client.declarations.get(summary.full_number)
    process_declaration(declaration)

client.close()

Use a context manager when the client is short-lived.

import os

from pharos_sdk import PharosClient

with PharosClient(os.environ["PHAROS_API_KEY"]) as client:
    importers = client.importers.list()

Sending the key in X-Pharos-Key

The key travels in Authorization: Bearer … by default. Pharos also accepts it in X-Pharos-Key, which is what to reach for when something between you and the API takes the Authorization header for itself — a corporate proxy or an API gateway that authenticates you to itself and rewrites it on the way out.

client = PharosClient(os.environ["PHAROS_API_KEY"], auth_scheme="api_key")

Both clients take it, and both accept the AuthScheme enum if you would rather not pass a string:

from pharos_sdk import AuthScheme

client = PharosClient(os.environ["PHAROS_API_KEY"], auth_scheme=AuthScheme.API_KEY)

Anything other than "bearer" or "api_key" raises ConfigurationError when the client is built, rather than on the first request.

Async Client

import os

from pharos_sdk import AsyncPharosClient

client = AsyncPharosClient(os.environ["PHAROS_API_KEY"])
latest = await client.declarations.latest()
await client.close()
import os

from pharos_sdk import AsyncPharosClient

async with AsyncPharosClient(os.environ["PHAROS_API_KEY"]) as client:
    importers = await client.importers.list()

Errors

Every SDK exception derives from PharosError. Failures that reached the API and came back with a status derive from APIError, which carries status_code, the documented code, and the response headers.

declaration = client.declarations.get("118-2026-10-001234-00")

Catch NotFoundError when a missing declaration is part of your normal workflow. Catch RateLimitError or ServerError at your job boundary if you want to back off and retry.

Exception Raised when
AuthenticationError The key is missing or invalid (401)
AuthorizationError Pharos Connect is not enabled for the account, or the source IP is not on the key's allowlist (403)
NotFoundError The resource does not exist or is outside your scope (404)
RateLimitError The hourly request limit was exceeded (429). retry_after holds the seconds to wait
InvalidRequestError A parameter is not valid (400)
ServerError An unexpected failure on the Pharos side, including gateway errors (5xx)
TransportError The request never got a response — DNS, TLS, connection or timeout
ResponseDecodeError A successful response did not match the published contract

The SDK does not retry. Reads are idempotent, so retrying is safe, but the policy is yours: catch RateLimitError and ServerError, and back off using retry_after when it is set.

Declaration Numbers

Declaration methods accept either the full DUA number as a string or a structured DeclarationNumber.

from pharos_sdk.models import DeclarationNumber

number = DeclarationNumber(
    customs_office="118",
    year=2026,
    regime="10",
    number=1234,
    control_number="00",
)

declaration = client.declarations.get(number)
items = client.declarations.items(number)

Webhooks

import os

from pharos_sdk.webhooks import WebhookVerifier

verifier = WebhookVerifier(os.environ["PHAROS_WEBHOOK_SECRET"])
event = verifier.verify(request_body, signature_header)
handle_event(event)

Pass the body exactly as it arrived, before any parsing: the signature covers the raw bytes. verify checks the signature and the timestamp — five minutes of tolerance by default — and raises WebhookVerificationError if either fails.

Form-encoded endpoints

An endpoint can be configured to receive application/x-www-form-urlencoded instead of JSON. Pass the request's Content-Type and the verifier reads either:

event = verifier.verify(
    request_body,
    signature_header,
    content_type=request.headers["Content-Type"],
)

Nothing is inferred from the body — Pharos always sends the header, so it is the sender's own statement of which shape it used. Omit the argument and the body is read as JSON, which is what an endpoint receives unless you asked for the other. A Content-Type that is neither raises WebhookVerificationError rather than being read as a guess.

You get the same event object either way. A form carries no types, so Pharos sends every value as text and the two objects as text holding JSON; the verifier undoes exactly that. The one difference is event.extra: any additional field configured for your endpoint stays a string, because the form did not carry its type either.

An event type newer than your installed SDK raises UnsupportedWebhookEvent, which is deliberately not a WebhookVerificationError: the delivery is authentic, only its type is unknown. Catch it and answer 2xx, or repeated failures will suspend your endpoint.

event = verifier.verify(request_body, signature_header)
handle_event(event)
return http_200()

At your webhook boundary, answer 2xx for UnsupportedWebhookEvent only when your system deliberately ignores event types unknown to this SDK version.

Deduplicate on event.event_id. It is the same across retries and across every endpoint subscribed to the same fact, which the delivery id is not.

Anything Pharos was asked to add to your notifications — a tenant identifier, a routing key — arrives in event.extra, keyed as you configured it. The documented fields are attributes; extra is everything else.

Development

uv run pytest
uv run ruff check .
uv run mypy --no-site-packages pharos_sdk

pytest prints coverage by default.

Metadata

Release files for pharos-sdk 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pharos-sdk 0.2.0
File Size Uploaded Provenance
pharos_sdk-0.2.0.tar.gz 35.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pharos-sdk 0.2.0
File Interpreter ABI Platform
pharos_sdk-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 68.2 kB

Release files / pharos_sdk-0.2.0.tar.gz

Download URL pharos_sdk-0.2.0.tar.gz
Size 35.0 kB
Tags Source
SHA-256 checksum
How to use checksums
c217727892b03791c529d1d9c83267f3469f9a1ecd6c191bee0628ea14b55a23
BLAKE2b-256 checksum
How to use checksums
64300f6e2ccaee7a246414a5334a4c5a48d2017f7c0fa62cb532e5291d5f17af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.

Transparency log

Release files / pharos_sdk-0.2.0-py3-none-any.whl

Download URL pharos_sdk-0.2.0-py3-none-any.whl
Size 33.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eaf8065268dfd82d4ac1f2c7c4562f4abb9bb37ec6e4d34884cef5bdf57edb5a
BLAKE2b-256 checksum
How to use checksums
da0efeb462a7be7fa9b3921f35d8593122a152c309ee9e7eaf6355aa69a5f352
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.

Transparency log

Security for pharos-sdk 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pharos_sdk-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 3, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page