"""C1K REST API with the Python standard library only (API-09). C1K_API_KEY=c1k_rest_... C1K_ORG_ID=org_... python3 docs/examples/api.py """ import json import os import sys import urllib.error import urllib.parse import urllib.request import uuid KEY = os.environ.get("C1K_API_KEY") ORG = os.environ.get("C1K_ORG_ID") if not KEY or not ORG: sys.exit("Set C1K_API_KEY and C1K_ORG_ID") BASE = f"https://c1k.me/api/v1/orgs/{ORG}" def call(method, path, body=None, headers=None): data = json.dumps(body).encode("utf-8") if body is not None else None req = urllib.request.Request(BASE + path, data=data, method=method) req.add_header("Authorization", f"Bearer {KEY}") if data is not None: req.add_header("Content-Type", "application/json") for name, value in (headers or {}).items(): req.add_header(name, value) try: with urllib.request.urlopen(req, timeout=15) as res: return json.load(res), res.headers.get("ETag") except urllib.error.HTTPError as err: error = json.load(err).get("error", {}) retry = err.headers.get("Retry-After") hint = f" (retry after {retry}s)" if retry else "" raise SystemExit(f"{err.code} {error.get('code')}: {error.get('message')}{hint} [{error.get('request_id')}]") # Create with an idempotency key so a retry after a lost response cannot duplicate it. created, etag = call( "POST", "/links", {"destination_url": "https://venture.example/launch", "title": "Launch page"}, {"Idempotency-Key": f"example-{uuid.uuid4()}"}, ) link = created["data"] print("created", link["short_url"], "version", link["version"]) # Edit with If-Match carrying the current version (API-07). edited, _ = call("PATCH", f"/links/{link['id']}", {"title": "Launch page (v2)"}, {"If-Match": etag}) print("edited to version", edited["data"]["version"]) # List all pages. cursor = None while True: query = "?limit=100" + (f"&cursor={urllib.parse.quote(cursor)}" if cursor else "") page, _ = call("GET", "/links" + query) for item in page["data"]: print(item["short_url"], "->", item["destination_url"]) cursor = page["meta"]["next_cursor"] if not cursor: break # Analytics and one feed poll; keep meta.next_cursor for the next poll. stats, _ = call("GET", "/analytics") print("recorded requests", stats["data"]["totals"]["total"]) feed, _ = call("GET", "/events") for event in feed["data"]: print(event["id"], event["time"], event["action"]) print("next cursor", feed["meta"]["next_cursor"])