Docs/Wire contract (v1)

User-Agent rules

The single most common cause of an integration that silently never fills. Two independent backend filters check the User-Agent, and a match on either kills the impression.

The two filters

The backend runs two UA filters. A match on either causes silent no-fill or a fraud score bump:

NameTypeRequiredNotes
is_bot blocklistsubstring, case-insensitiveOptionalbot, crawler, spider, scraper, google, bing, yahoo, facebook, twitter, instagram, pinterest, slack → immediate {fill:false}.
Fraud bot_signaturessubstring, +0.45 scoreOptionalbot, crawler, spider, scraper, HeadlessChrome, PhantomJS, Selenium, Puppeteer, Playwright, curl/, wget/, python-requests, Go-http-client, httpclient, Java/, okhttp — rejects outright above a 0.75 fraud score.

Default client UAs are on this list

python-requests/2.x, okhttp/4.x, Java/17 and Go-http-client/1.1 — the defaults of the most common HTTP libraries — all match. An integration built by hand against the raw contract that forgets to set a custom User-Agent will no-fill 100% of the time with no error, no exception, no log line — just an empty {"fill": false}. This is the #1 support question for anyone integrating without an SDK.

What every official SDK sends

Every SDK sets an explicit UA that (a) avoids all of the substrings above and (b) includes a platform token so get_device_type classifies correctly (android/iphone/ipad/mobile/tablet, else desktop):

NameTypeRequiredNotes
PythonstringOptionaltheaimart-adx-python/<ver>
NodestringOptionaltheaimart-adx-node/<ver>
RuststringOptionaltheaimart-adx-rust/<ver>
WebstringOptional(browser sets its own real UA — do not override)
React NativestringOptionaltheaimart-adx-reactnative/<ver> (<os>)
AndroidstringOptionaltheaimart-adx-android/<ver> (Android <osver>; <model>)
iOSstringOptionaltheaimart-adx-ios/<ver> (iPhone; iOS <osver>)
FlutterstringOptionaltheaimart-adx-flutter/<ver> (<android|iPhone>...)
UnitystringOptionaltheaimart-adx-unity/<ver> (<platform>)

Server-side SDKs: forward the real user

Python, Node and Rust are typically used to proxy an ad request on behalf of an end user (e.g. server-side rendering a page). In that case, the integrator must forward the end user's own User-Agent and IP (via X-Forwarded-For) — not the server's. Otherwise every impression across every user looks like one device from one datacenter IP, which gets fraud-scored to a no-fill. Both SDKs expose userAgent/user_agent and clientIp/client_ip per-request overrides for exactly this — see Node or Python.