"""HaulProof API client for Python 3.8+ (standard library only). Usage: HP_KEY=hp_test_... python haulproof.py Shows: tiers for a list, a full record, a bulk watchlist, the nightly tier file (download + parse), a webhook + test.""" import csv, gzip, json, os, time, urllib.error, urllib.parse, urllib.request BASE = os.environ.get("HP_BASE", "https://haulproof.ca") KEY = os.environ["HP_KEY"] def api(path, method="GET", body=None): for attempt in range(4): req = urllib.request.Request(BASE + path, method=method, data=None if body is None else json.dumps(body).encode(), headers={"Authorization": f"Bearer {KEY}", "Content-Type": "application/json", "User-Agent": "haulproof-python-sample"}) try: with urllib.request.urlopen(req, timeout=30) as r: raw = r.read() return (json.loads(raw) if raw else None), r.headers except urllib.error.HTTPError as e: if e.code == 429 and attempt < 3: time.sleep(float(e.headers.get("Retry-After", "1"))) continue raise RuntimeError(f"{e.code} {e.read().decode()}") from None def tiers(ids): """Tiers for any number of identifiers, 100 per call.""" out = [] for i in range(0, len(ids), 100): out += api("/v1/tiers?ids=" + urllib.parse.quote(",".join(ids[i:i + 100])))[0]["data"] return out def tier_file(): """Streams the first tier file the key can see (the full file has ~2.2 million rows), one dict per row.""" files = [f for f in api("/v1/tier-files")[0]["data"] if f.get("url")] if not files: return req = urllib.request.Request(files[0]["url"], headers={"User-Agent": "haulproof-python-sample"}) with urllib.request.urlopen(req, timeout=300) as r, gzip.open(r, "rt", encoding="utf-8", newline="") as text: yield from csv.DictReader(text) if __name__ == "__main__": sandbox = KEY.startswith("hp_test_") ids = ["99990001", "MC990004", "us:99990007"] if sandbox else ["264184", "MC133655", "R-048945-1"] print("tiers:", "; ".join(f"{t['id']} -> {t.get('tier') or t.get('error')}" for t in tiers(ids))) rec, headers = api(f"/v1/carriers/{ids[0]}") print("record:", rec["data"]["name"], rec["data"]["tier"], "cache:", headers.get("X-HP-Cache", "n/a"), "as_of:", rec.get("as_of", "n/a")) first, count = None, 0 for row in tier_file(): first = first or row count += 1 print("tier file rows:", count, f"first: {first['key']} {first['name']} {first['tier']}" if first else "") if sandbox: print("bulk add: needs a live key (watchlists hold real carriers)") else: w = api("/v1/watchlists", "POST", {"name": f"python sample {int(time.time())}"})[0]["data"] print("bulk add:", json.dumps(api(f"/v1/watchlists/{w['id']}/items/bulk", "POST", {"items": ids + ["not a carrier"]})[0]["data"])) api(f"/v1/watchlists/{w['id']}", "DELETE") # the sample's list only if os.environ.get("HP_WEBHOOK_URL"): h = api("/v1/webhooks", "POST", {"url": os.environ["HP_WEBHOOK_URL"], "min_severity": "warning"})[0]["data"] print("test send:", json.dumps(api(f"/v1/webhooks/{h['id']}/test", "POST")[0]["data"])) api(f"/v1/webhooks/{h['id']}", "DELETE")