Why curl tests against AxiTrace always return 200 even when the event was silently dropped as bot traffic, and how to test correctly.

Troubleshooting: "Everything Returns 200 When I Test with curl"

If every request you send with a bare curl command returns 200 OK - even ones you'd expect to fail validation, or ones sent with an obviously wrong Secret Key - the bot filter is almost certainly intercepting the request before it reaches authentication or validation at all.

Why This Happens

The ingestion API runs a bot-detection pipeline (botFilterMiddleware) as the first middleware in the chain, ahead of CORS, authentication, and request validation. When a request is classified as a bot, the API responds with:

200 OK
{"status": "ok"}

...and the request is dropped immediately. It never reaches your workspace's authentication check, its field validators, or your Errors tab - so from the outside, a bot-dropped request and a genuinely successful request look identical: both return 200 with a small JSON body.

Why a Silent 200 Instead of a 403?

This is intentional. Returning an error status (or a distinguishable body) to a detected bot would let scripted traffic learn it had been detected and adapt its User-Agent or headers to evade the filter next time. A uniform 200 OK gives no signal back to the caller.

Why curl Gets Flagged

curl's default User-Agent header is curl/<version> (e.g. curl/8.7.1). That pattern is matched by the bot filter's crawler/User-Agent detector - the same list that catches generic HTTP clients, scripts, and scrapers - so an un-customized curl request is classified as a bot on the User-Agent check alone, regardless of how correct the rest of the request is.

How to Tell the Difference

Check for X-Axitrace-Dropped: bot

A response that carries the X-Axitrace-Dropped: bot header was intercepted by the bot filter and never processed - regardless of the 200 status code. If that header is absent, the request reached authentication/validation and a 200 means what it normally means.

# Include response headers (-i) to check for the bot-drop marker
curl -i -X POST https://stat.axitrace.com/v1/page/view \
  -u "sk_live_YOUR_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -H "User-Agent: curl/8.7.1" \
  -d '{"label": "test", "client": {"customId": "test-user"}, "params": {"url": "https://example.com/"}}'

# Response includes: X-Axitrace-Dropped: bot
# -> the request was NEVER validated or processed, despite the 200

Testing Correctly

Override curl's default User-Agent with something that looks like a real browser or a clearly identified application - both work, as long as it isn't a bare curl/…, python-requests/…, Go-http-client/…, etc.

# A realistic browser User-Agent passes the bot filter for manual testing
curl -i -X POST https://stat.axitrace.com/v1/page/view \
  -u "sk_live_YOUR_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" \
  -d '{"label": "test", "client": {"customId": "test-user"}, "params": {"url": "https://example.com/"}}'

# No X-Axitrace-Dropped header -> the request was actually authenticated and validated.

For real server-side integrations (not manual testing), always send a descriptive application User-Agent such as MyApp/1.0 - see HTTP API for the full endpoint reference.

Still Nothing After Fixing the User-Agent?

Once you've confirmed the request isn't being bot-dropped, work through the rest of the checklist in Troubleshooting: Event Sent, Nothing Visible - the Errors tab, the ~10s conversion delay, and domain verification.