Skip to main content
POST
/
api
/
v1
/
ssp
/
bid-request
{
  "userId": "user_123",
  "chatId": "chat_456",
  "messages": [
    {
      "role": "user",
      "content": "Looking for running shoes",
      "timestamp": "2025-11-23T10:00:00Z"
    },
    {
      "role": "assistant",
      "content": "What's your budget?",
      "timestamp": "2025-11-23T10:00:15Z"
    }
  ],
  "summary": "User is looking for running shoes for marathon training",
  "config": {
    "ad_offset": 3,
    "max_frequency": 5,
    "max_headline_chars": 60,
    "experiment_tag": "variant_a"
  },
  "user_metadata": {
    "age_range": "25-34"
  },
  "turn_number": 4,
  "force": false
}
{
  "requestId": "api_req_123",
  "timestamp": "2025-11-24T21:51:52.240297Z",
  "totalTime": 0.123,
  "status": "success",
  "message": "Bid successful",
  "data": {
    "bid": {
      "ad_format": "sponsored_message",
      "price": 5.50,
      "advertiser": "Nike",
      "domain": "nike.com",
      "headline": "Premium Running Shoes",
      "description": "Perfect for marathon training. Lightweight and comfortable.",
      "cta_text": "Shop Now",
      "url": "https://ssp.thrads.ai/api/v1/tracking/redirect?token=abc123",
      "image_url": "https://cdn.example.com/nike-shoes.jpg",
      "dsp": "thrad_dsp",
      "bidId": "bid_abc123"
    }
  },
  "error": null
}

Authorizations

thrad-api-key
string
required
Your API key for authentication. The key’s environment (staging or production) determines how ads are served — see Staging vs. Production.
Content-Type
string
required
Must be application/json

End-User Headers

These headers identify the real end-user behind the request. They are required on every bid request and every view URL ping.
X-Forwarded-For
string
required
The end-user’s original IP address. Must be the real client IP, not your server’s IP.
User-Agent
string
required
The end-user’s original device/browser string exactly as received from the client.

Geolocation & Device Headers

The server resolves the user’s location and device from request headers. These are optional but strongly recommended for server-side integrations — sending them skips the server-side IP lookup and reduces latency.
X-User-Country
string
ISO 3166-1 alpha-2 country code (e.g. "US", "DE"). When present, X-User-Device and X-User-Timezone are required.
X-User-Device
string
User’s device type: "mobile" or "desktop". Required when X-User-Country is present. This is critical for device-targeted campaigns — without it, targeting will not work correctly.
X-User-Timezone
string
IANA timezone (e.g. "America/Los_Angeles"). Required when X-User-Country is present. Used for time-of-day pacing.
X-User-Region
string
Region or state (e.g. "CA", "Bavaria"). Optional but recommended for regional targeting.
X-User-City
string
City name (e.g. "San Francisco"). Optional but recommended for city-level targeting.
If you send X-User-Country without X-User-Device and X-User-Timezone, the request will be rejected with a 400 Bad Request. These three headers must always be sent together.
If none of the above headers are sent, the server falls back to IP-based geolocation using X-Forwarded-For. For direct calls (browser or mobile app → our API), this is handled automatically by the load balancer. If you’re proxying requests through your own backend, you must forward the user’s IP via X-Forwarded-For — otherwise the server sees your server’s IP and geo will be wrong.
If unsure about which headers to send, contact marco@thrads.ai and we’ll help you set it up.

Body

userId
string
required
Unique, anonymous user identifier (e.g. a UUID like user_a1b2c3d4-...). Must be stable across sessions for the same user. Do not use email, name, or other PII. On web, store in localStorage; on mobile, use the platform’s persistent storage (e.g. UserDefaults on iOS, SharedPreferences on Android).
chatId
string
contextual only Conversation identifier. One unique ID per conversation, not per user. Reset when user starts a new chat. Required when request_type is "contextual".
messages
array
contextual only Conversation history. Required when request_type is "contextual". Each message must have a role ("user" or "assistant") and content. PII (names, emails, addresses, etc.) should be masked or removed before sending.
config
object
Publisher configuration for pacing, creative constraints, and analytics. All fields are optional — omitted fields fall back to defaults set on your chatbot in the platform dashboard.
request_type
string
default:"contextual"
Pipeline selector. Determines which ad pipeline runs and what fields are required.
  • "contextual" (default) — in-chat ads. Requires chatId and messages.
  • "opener" — pre-chat ads (e.g. sponsored prompts, banners). chatId and messages are not required.
ad_formats
array
Ad formats the publisher can render. The server picks the best match from this list.
  • Contextual: ["sponsored_message"] (default). More formats coming soon — see Ad Formats & Rendering.
  • Opener: ["sponsored_message"], ["sponsored_prompt"], or both. Defaults to ["sponsored_prompt"] when omitted.
turn_number
integer
contextual only Current turn number in the conversation. Used for ad offset pacing and analytics. Must be >= 0. Only needed if you’re not calling the endpoint on every user-assistant exchange — if you are, the system derives this from the message count.
turns_from_last_ad
integer
contextual only Number of requests since the last ad was shown in this conversation. Used by the max frequency check. Optional — the system tracks this automatically. Only send this if you want to override the server-side counter.
summary
string
contextual only Publisher-provided conversation summary. Useful when you already have a summary from your chatbot and want to reduce latency.
force
boolean
default:"false"
contextual only When true, the system serves an ad as a last resort even if the conversation context doesn’t strongly match any campaign. The normal pipeline (contextual scoring → retargeting → exploration) runs first; force only kicks in when everything else returns no bid. Hard filters (banned content, geo targeting, budget caps) still apply — force never overrides safety or budget constraints.We recommend starting without force (the default) and only enabling it if your fill rate is too low. See Maximising Fill Rate for guidance.
user_metadata
object
User metadata for targeting and compliance. All fields are optional. Extra fields are accepted and stored for future use.
{
  "userId": "user_123",
  "chatId": "chat_456",
  "messages": [
    {
      "role": "user",
      "content": "Looking for running shoes",
      "timestamp": "2025-11-23T10:00:00Z"
    },
    {
      "role": "assistant",
      "content": "What's your budget?",
      "timestamp": "2025-11-23T10:00:15Z"
    }
  ],
  "summary": "User is looking for running shoes for marathon training",
  "config": {
    "ad_offset": 3,
    "max_frequency": 5,
    "max_headline_chars": 60,
    "experiment_tag": "variant_a"
  },
  "user_metadata": {
    "age_range": "25-34"
  },
  "turn_number": 4,
  "force": false
}
{
  "requestId": "api_req_123",
  "timestamp": "2025-11-24T21:51:52.240297Z",
  "totalTime": 0.123,
  "status": "success",
  "message": "Bid successful",
  "data": {
    "bid": {
      "ad_format": "sponsored_message",
      "price": 5.50,
      "advertiser": "Nike",
      "domain": "nike.com",
      "headline": "Premium Running Shoes",
      "description": "Perfect for marathon training. Lightweight and comfortable.",
      "cta_text": "Shop Now",
      "url": "https://ssp.thrads.ai/api/v1/tracking/redirect?token=abc123",
      "image_url": "https://cdn.example.com/nike-shoes.jpg",
      "dsp": "thrad_dsp",
      "bidId": "bid_abc123"
    }
  },
  "error": null
}
A 200 OK response with "bid": null is not an error - it means the auction ran successfully but no DSP submitted a winning bid. This is normal behavior.

Response

requestId
string
Unique request identifier used in logs and tracing.
timestamp
string
ISO 8601 timestamp of when the response was generated.
totalTime
float
Total processing time for this request in seconds.
status
string
Response status: "success" or "error".
message
string
Human-readable message describing the result.
data
object
Response data payload.
error
object|null
Error details when status = "error" (otherwise null).

Status Codes

Status CodeMeaningScenario
200 OKSuccessAuction ran successfully (with or without winning bid), or pacing blocked
400 Bad RequestInvalid inputMalformed request body or missing required fields
401 UnauthorizedAuthentication failedMissing or invalid thrad-api-key
403 ForbiddenOrigin not allowedRequest from non-whitelisted domain (browser integration only)
429 Too Many RequestsRate limit exceededPublisher exceeded request quota
500 Internal Server ErrorServer errorDSP render failed, validation error, or internal exception