Skip to main content
POST
/
api
/
v1
/
ad
curl -X POST https://server.trygravity.ai/api/v1/ad \
  -H "Authorization: Bearer <your_publisher_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "How do I set up a PostgreSQL database?"},
      {"role": "assistant", "content": "Here are the steps to set up PostgreSQL..."}
    ],
    "sessionId": "sess_abc123",
    "placements": [
      {"placement": "below_response", "placement_id": "main"}
    ],
    "user": {"id": "user_789"}
  }'

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 primary ad endpoint. Send conversation messages and get back a contextually matched ad with generated creative.

Headers

Authorization
string
required
Your Gravity API key. Format: Bearer <key>

Body

messages
array
required
Conversation history. Array of {role, content} message objects. The engine uses the last few messages for contextual matching.
sessionId
string
required
Session identifier. Used for frequency capping, experiment bucketing, and reporting. If the Gravity pixel is installed and you’re on @gravity-ai/api ≥ 1.1.7, the SDK auto-forwards the pixel’s gr_sess_-prefixed session ID (30-min idle timeout, 1-day max) via window.gravityPixel.getSessionId(). Pass your own sessionId to override if you have a better session scope.
placements
array
required
Ad placement configuration. 1–10 placements per request.
FieldTypeDescription
placementstringPlacement type. One of above_response, below_response, inline_response, left_response, right_response, search_result, top_page, bottom_page, center_page, left_page, right_page.
placement_idstringUnique slot identifier on your side. Ties dashboard analytics back to the spot in your UI.
user
object
Optional. User context for targeting and attribution. All fields are optional on the wire — the engine accepts any shape and passes extras through.
FieldTypeDescription
idstringStable per-user identifier on your side. Used for frequency capping, attribution, and identity linking. The SDK defaults this to "anonymous" if you don’t supply one.
ipstringUser’s IP address. Auto-populated by the SDK from the incoming request.
hashed_emailstringSHA-256 of email.strip().lower(). Passing this significantly improves attribution; see Data quality.
hashed_phonestringSHA-256 of digits-only phone.
Extra fields (gender, age, subscription tier, interests, etc.) are accepted and stored as request context.
device
object
Optional. Device signals. The SDK auto-populates this from the client; for direct HTTP you should at least send ip for geo-targeting and fraud detection. Fields: ip, ua, country, os, ifa, plus any extras (timezone, locale, browser, etc.).
relevancy
number
Optional. Minimum relevancy threshold, 0.0–1.0. When omitted, the engine falls back to the publisher baseline configured in your dashboard. Both SDKs default to 0.2.
excludedTopics
array
Optional. Array of topic strings to exclude from matching (e.g. ["politics"]).
testAd
boolean
default:"false"
Optional. When true, returns test creative and skips billing/metrics. The SDKs set this from the inverse of their production flag.
curl -X POST https://server.trygravity.ai/api/v1/ad \
  -H "Authorization: Bearer <your_publisher_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "How do I set up a PostgreSQL database?"},
      {"role": "assistant", "content": "Here are the steps to set up PostgreSQL..."}
    ],
    "sessionId": "sess_abc123",
    "placements": [
      {"placement": "below_response", "placement_id": "main"}
    ],
    "user": {"id": "user_789"}
  }'

Response

On a successful match the endpoint returns HTTP 200 with a JSON array of ad objects — one per requested placement:
[
  {
    "adText": "Serverless Postgres that scales to zero. Start free.",
    "title": "Neon Serverless Postgres",
    "brandName": "Neon",
    "cta": "Try Neon Free",
    "url": "https://neon.tech",
    "favicon": "https://icons.duckduckgo.com/ip3/neon.tech.ico",
    "clickUrl": "https://api.trygravity.ai/track/click?p=...",
    "impUrl": "https://api.trygravity.ai/ack?p=...",
    "placement": "below_response",
    "placement_id": "main"
  }
]
The SDKs wrap this array in a convenience object — { 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.

Ad object

FieldTypeDescription
adTextstringGenerated ad copy, contextually matched
titlestringProduct/campaign title
brandNamestringAdvertiser brand name
ctastringCall to action text
urlstringLanding page URL
faviconstringBrand favicon URL
clickUrlstringTracked click URL — use this for links
impUrlstringImpression pixel URL — fire when ad is visible
placementstringEchoes back the placement type this ad filled
placement_idstringEchoes back your slot correlation ID
campaignIdstringCampaign identifier for the matched ad
leadFormobjectLead form configuration, when the campaign has a lead form attached
Null fields are omitted from the response. When an experiment is active, the ad object also includes:
FieldTypeDescription
variantstringHuman-readable arm label
experiment_idstringCanonical experiment ID
composition_idstringPer-render composition ID (unique per impression)
renderer_keystringWhich renderer to use
compositionobjectTokens and props for the assigned renderer
Experiment identity is already baked into impUrl and clickUrl — downstream analytics join automatically.
Always use clickUrl for ad links (not url directly) and fire impUrl when the ad becomes visible. This ensures accurate tracking and billing.

No ad available

When no ad matches the context (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 gracefully hide the slot in that case.