From af084f281d20231e1c1a919e67481556fbff63c3 Mon Sep 17 00:00:00 2001 From: apinator-dev Date: Sun, 25 Oct 2026 01:17:33 +0200 Subject: [PATCH] fix: send an Apinator User-Agent so Cloudflare doesn't block requests Cloudflare in front of ws-{cluster}.apinator.io rejects urllib's default "Python-urllib/x.y" User-Agent with error 1010 (403), before the request reaches the API. Every call the SDK made against production failed with AuthenticationError: API request failed: Forbidden. Requests now carry "apinator-server-python/{version} (Python x.y.z)". --- apinator/client.py | 11 +++++++++++ tests/test_client.py | 2 ++ 2 files changed, 13 insertions(+) diff --git a/apinator/client.py b/apinator/client.py index b86068b..41ce0a2 100644 --- a/apinator/client.py +++ b/apinator/client.py @@ -5,6 +5,7 @@ from __future__ import annotations import json +import platform import time import urllib.request import urllib.error @@ -17,6 +18,15 @@ from .errors import ValidationError, AuthenticationError, ApiError +def _user_agent() -> str: + # Imported here because the package's __init__ imports this module. + from . import __version__ + + # Cloudflare in front of the API blocks urllib's default + # "Python-urllib/x.y" User-Agent (error 1010), so always send our own. + return f"apinator-server-python/{__version__} (Python {platform.python_version()})" + + class Apinator: """ Apinator server SDK client. @@ -229,6 +239,7 @@ def _request( # Set headers req.add_header('Content-Type', 'application/json') + req.add_header('User-Agent', _user_agent()) req.add_header('X-Realtime-Key', self.key) req.add_header('X-Realtime-Timestamp', str(timestamp)) req.add_header('X-Realtime-Signature', signature) diff --git a/tests/test_client.py b/tests/test_client.py index 95365c0..6998b42 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -82,6 +82,8 @@ def test_trigger_single_channel(self): assert f"/apps/{TEST_APP_ID}/events" in request.full_url assert request.get_header('Content-type') == 'application/json' assert request.get_header('X-realtime-key') == TEST_KEY + # Cloudflare blocks urllib's default User-Agent (error 1010). + assert request.get_header('User-agent').startswith('apinator-server-python/') # Verify body body = json.loads(request.data.decode('utf-8'))