Fetch every report
This Python 3 example uses the standard library. Supply ETHICSPORTAL_TOKEN and ETHICSPORTAL_ORGANIZATION_ID through your environment, then run the downloadable example.
python3 paginate.pyIt prints one report metadata object per line. It passes each page’s next_cursor back as the cursor parameter, so it only ever requests the report collection on the EthicsPortal origin, and it refuses redirects.
"""Fetch authorised report metadata using Python 3's standard library."""import jsonimport osimport urllib.errorimport urllib.parseimport urllib.request
ORIGIN = "https://secure.ethicsportal.eu"
class NoRedirects(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None
def reports(token, organization_id, opener=None): base = f"{ORIGIN}/organizations/{urllib.parse.quote(organization_id, safe='')}/whistleblower/reports.json" opener = opener or urllib.request.build_opener(NoRedirects()) cursor, seen = None, set() while True: query = {"limit": "100", **({"cursor": cursor} if cursor else {})} request = urllib.request.Request(f"{base}?{urllib.parse.urlencode(query)}", headers={ "Authorization": f"Bearer {token}", "Accept": "application/json" }) with opener.open(request, timeout=20) as response: page = json.load(response) if not isinstance(page, dict) or not isinstance(page.get("data"), list): raise ValueError("Expected a report list") yield from page["data"] cursor = page.get("next_cursor") if not cursor: return if cursor in seen: raise ValueError("Repeated pagination cursor; restart reconciliation") seen.add(cursor)
if __name__ == "__main__": try: for report in reports(os.environ["ETHICSPORTAL_TOKEN"], os.environ["ETHICSPORTAL_ORGANIZATION_ID"]): print(json.dumps(report)) except urllib.error.HTTPError as error: retry = error.headers.get("Retry-After", "unspecified") if error.code == 429 else None raise SystemExit(f"API returned HTTP {error.code}" + (f"; retry after {retry} seconds" if retry else "")) from NoneA 429 stops the example and reports the wait interval. In a scheduled integration, wait and retry with bounded backoff. Start a fresh traversal after an expired cursor and reconcile by report ID; permissions can change between pages. The output contains case metadata, so handle it according to your organisation’s access and retention rules.