Documentation Index
Fetch the complete documentation index at: https://docs.trygravity.ai/llms.txt
Use this file to discover all available pages before exploring further.
The pattern
Fire the ad request in parallel with your LLM call. Two reasons:- Latency budget. The user’s perceived latency is your LLM stream — the ad request should never block it.
- Fire and forget. Ad fetch can fail silently without affecting the user’s chat.
What’s in the request
| Field | Required | Description |
|---|---|---|
messages | Yes | Recent conversation messages. Array of {role, content}. The engine uses the last few for contextual matching. |
sessionId | Yes | Your session identifier. Used for frequency capping and experiment bucketing. Enforced by the engine. Free if the Gravity pixel is installed — the pixel mints a gr_sess_-prefixed session ID (30-min idle, 1-day max) and the SDK auto-forwards it via window.gravityPixel.getSessionId() (requires @gravity-ai/api ≥ 1.1.7). Override with your own value if you have a better session scope. |
placements | Yes | Array of {placement, placement_id}. 1–10 per request. |
user.userId | Yes for JS SDK; Optional for Python SDK | Stable per-user identifier on your side (normalized to id on the wire). Used for frequency capping, attribution, and identity linking. The JS SDK’s gravityContext() throws if missing — pass "anonymous" (or any constant) yourself if you don’t have a real one. The Python SDK’s server-side fallback defaults to "anonymous". |
user.email / user.phone | Optional | See User data & hashing below — raw values are hashed by the SDK before they leave the client. |
excludedTopics | Optional | Array of topic strings to exclude (e.g. ["politics"]). |
relevancy | Optional | Minimum relevancy score 0.0–1.0. When omitted, the engine falls back to the publisher baseline configured in your dashboard; the SDKs default to 0.2. See Relevancy tradeoff below. |
testAd | Optional | Defaults to false. When true, returns test creative and skips billing/metrics. The SDKs set this from the inverse of their production flag. |
User data & hashing
Passing the user’s email or phone number on the ad request materially improves attribution and reporting — particularly view-through attribution, where a conversion on an advertiser’s site ties back to an ad the user saw (not just clicked). Raw email/phone never leaves the client. The SDK hashes them (SHA-256, normalized form) before the request is sent:- email:
sha256(email.strip().lower()) - phone:
sha256(re.sub(r"\D+", "", phone))(digits only)
user.hashed_email and user.hashed_phone. Don’t send raw email or phone over the wire.
What’s in the response
On a successful match the endpoint returns HTTP200 with a JSON array of ad objects — one per requested placement:
{ ads, status, elapsed } in JS, AdResult(ads, status, elapsed_ms, ...) in Python — but that envelope is SDK-only and does not appear on the wire.
Always use clickUrl (not url) for ad links — it routes through tracking, records the click, and 302s to the landing page. Always fire impUrl when the ad becomes visible — in the SDK, GravityAd does this automatically via IntersectionObserver.
When no ad matches (or the request is filtered as a bot, times out, or hits an unrecoverable error), the endpoint returns an HTTP 204 No Content with an empty body — there is no JSON payload. Your UI should hide the slot gracefully.
Framework examples
Next.js (App Router)
FastAPI
Multiple placements
Request multiple placements in one call; they share the same auction:null. Render conditionally.
Tuning
Relevancy tradeoff
relevancy is a score from 0.0 to 1.0. Default is 0.2. It’s the single most common misconfiguration in Gravity integrations, so it’s worth understanding the tradeoff:
| Setting | What happens | When to use |
|---|---|---|
Lower (e.g. 0.1) | Looser contextual match. Higher fill rate — ads serve more often. Individual ads may feel less tied to the conversation. | Pages where you want to monetize every turn. New integrations where you want to see traffic flow. |
Higher (e.g. 0.4–0.5) | Tighter contextual match. Lower fill rate — many turns return no ad. When you do serve, ads feel very on-topic. | Premium surfaces where you’d rather show nothing than show a loose match. |
0.2 is our recommendation for most integrations — enough match quality to feel relevant, enough fill to be worth monetizing. Start there and adjust if per-slot data tells you to.
Next
Show ads
Render them in your UI.
API Reference
Full HTTP surface with all parameters.