API docs should document User-Agent header requirement #2436
-
|
When making API requests to app.fizzy.do using Python's default Steps to Reproduce
from urllib.request import Request, urlopen
import json
url = "https://app.fizzy.do/{account}/boards/{board}/cards.json"
payload = json.dumps({"card": {"title": "Test"}}).encode("utf-8")
req = Request(
url,
data=payload,
method="POST",
headers={
"Authorization": "Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
},
)
with urlopen(req) as resp: # Returns 403 with HTML body
print(resp.read())OR …/git/playground ❯ curl -v -H "Authorization: Bearer ${FIZZY_TOKEN}" -H "Accept: application/json" -H "User-Agent: Pyth
on-urllib/3.4" https://app.fizzy.do/my/identity
* Host app.fizzy.do:443 was resolved.
* IPv6: (none)
* IPv4: 104.18.1.28, 104.18.0.28
* Trying 104.18.1.28:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL Trust Anchors:
* CAfile: /etc/ssl/certs/ca-certificates.crt
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519MLKEM768 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
* subject: CN=app.fizzy.do
* start date: Dec 15 14:23:35 2025 GMT
* expire date: Mar 15 15:23:32 2026 GMT
* issuer: C=US; O=Google Trust Services; CN=WE1
* Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA256
* Certificate level 1: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
* Certificate level 2: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
* subjectAltName: "app.fizzy.do" matches cert's "app.fizzy.do"
* SSL certificate verified via OpenSSL.
* Established connection to app.fizzy.do (104.18.1.28 port 443) from 10.0.0.8 port 44176
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://app.fizzy.do/my/identity
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: app.fizzy.do]
* [HTTP/2] [1] [:path: /my/identity]
* [HTTP/2] [1] [authorization: Bearer {redacted}]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Python-urllib/3.4]
> GET /my/identity HTTP/2
> Host: app.fizzy.do
> Authorization: Bearer {redacted}
> Accept: application/json
> User-Agent: Python-urllib/3.4
>
* Request completely sent off
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 403
< date: Mon, 26 Jan 2026 20:18:57 GMT
< content-type: text/plain; charset=UTF-8
< content-length: 16
< x-frame-options: SAMEORIGIN
< referrer-policy: same-origin
< cache-control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< expires: Thu, 01 Jan 1970 00:00:01 GMT
< server: cloudflare
< cf-ray: 9c42b3d5f8f45f24-ORD
<
* Connection #0 to host app.fizzy.do:443 left intact
error code: 1010
SolutionAdding a custom headers={
"Authorization": "Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
"User-Agent": "my-app/1.0", # Required!
}RequestPlease add a note to the API documentation mentioning that:
This would save developers time when building custom integrations. The official fizzy-api-client Python library works fine because httpx sets a proper User-Agent by default, but anyone writing a simple script with urllib will hit this issue. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
|
Thanks for reporting this! I've created a PR to add a note to the API docs about this: #2505. You're right — Cloudflare's Browser Integrity Check blocks the default |
Beta Was this translation helpful? Give feedback.
-
|
We've made our Cloudflare WAF rules a bit more liberal in what's accepted. You should be OK now to use whatever user-agent string you wish. |
Beta Was this translation helpful? Give feedback.
Thanks for reporting this! I've created a PR to add a note to the API docs about this: #2505. You're right — Cloudflare's Browser Integrity Check blocks the default
Python-urllibuser agent before the request ever reaches the app.