Skip to main content

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.

This is the long version of the Quickstart. If you just want the 5-minute happy path, start there. This guide covers everything around the edges: the full lifecycle from first line of code to recurring payouts.

Lifecycle overview

1

Design

Decide where ads go in your UI, which placements you need, and whether you’re using the SDK or calling the API directly.
2

Integrate

Install the SDK. Wire gravityContext() on the client, gravity.getAds() on the server, GravityAd (or your own renderer) in the UI.
3

Test

Keep production: false. Verify placements render, impressions fire on visibility, clicks route through clickUrl.
4

Install the pixel

Drop the Gravity pixel onto your site. One <script> tag — mints a visitor ID the SDK automatically forwards on every ad request, which tightens targeting and attribution. If you also open ad links inside a native in-app browser, add the in-app browser install on top.
5

Go live

Flip production: true. Watch the going-live checklist.
6

Optimize

Let experiments test ad variants against your real traffic. The engine moves traffic toward the highest-converting options automatically.
7

Get paid

Monitor payouts in the dashboard. Revenue accrues per impression.

Pick placements

A placement is where an ad sits relative to your UI. Every ad request declares one or more. Common options:
PlacementWhen to use
below_responseUnder the assistant’s message. Most common in chat UIs.
above_responsePinned header above the response. Use sparingly.
inline_responseInside the response stream (markdown).
left_response / right_responseSidebar next to the response.
search_resultWithin a search result list.
top_page / bottom_pageStatic, page-level placement.
Each placement has a placement_id you pick — anything identifying the slot ("main", "mobile-footer", etc.). Keep them stable across requests; you’ll see per-slot metrics in the dashboard. You can request up to 10 placements per call and the engine returns an ad per slot (or null per slot with no fill).

Choose: SDK or API

Both hit the same engine. The SDK is a thin wrapper that handles context preparation, impression tracking, retry, and experiments. The API is what the SDK calls under the hood. Use the SDK when:
  • You’re on Node, TypeScript, Python, or React
  • You want pre-built visual variants
  • You want to participate in experiments (which auto-optimize your creative mix)
  • You want impression tracking to “just work”
Use the API directly when:
  • You’re on an unusual language / runtime
  • You render ads in a heavily custom way that can’t use GravityAd
  • You’re prototyping or building a CLI / non-UI surface
You’re not locked in either way. The API is fully supported and never going away.

Typical integration shapes

Next.js / App Router

// app/api/chat/route.ts
import { Gravity } from '@gravity-ai/api';

const gravity = new Gravity({ production: process.env.NODE_ENV === 'production' });

export async function POST(req: Request) {
  const { messages } = await req.json();

  const adPromise = gravity.getAds(req, messages, [
    { placement: 'below_response', placement_id: 'main' }
  ]);

  const stream = new ReadableStream({
    async start(controller) {
      for await (const chunk of callLLM(messages)) {
        controller.enqueue(`data: ${JSON.stringify({ type: 'chunk', content: chunk })}\n\n`);
      }
      const { ads } = await adPromise;
      controller.enqueue(`data: ${JSON.stringify({ type: 'done', ads })}\n\n`);
      controller.close();
    },
  });

  return new Response(stream, { headers: { 'Content-Type': 'text/event-stream' } });
}

Express

import { Gravity } from '@gravity-ai/api';
const gravity = new Gravity({ production: true });

app.post('/api/chat', async (req, res) => {
  const { messages } = req.body;
  const adPromise = gravity.getAds(req, messages, [{ placement: 'below_response', placement_id: 'main' }]);
  // ... stream LLM, await adPromise, append ads
});

FastAPI / Python

import asyncio
from gravity_sdk import Gravity

gravity = Gravity(production=True)

@app.post("/api/chat")
async def chat(request: Request):
    body = await request.json()
    ad_task = asyncio.create_task(
        gravity.get_ads(request, body["messages"], [
            {"placement": "below_response", "placement_id": "main"}
        ])
    )
    # ... stream LLM, await ad_task, append ads

Error handling

The SDK never throws. Failure modes:
SituationResult
Network error / timeout{ ads: [] } (JS) / AdResult(ads=[]) (Python)
204 No Content (no match){ ads: [] }
Invalid API key{ ads: [] } + warn in logs
Downstream engine error{ ads: [] } + warn
Your UI should always be safe to render with an empty ads array. Never block your LLM response on the ad fetch.

Going live checklist

See Going live. Short version: flip production: true, verify impressions land, confirm clicks route through clickUrl, watch the dashboard.

What’s next

Request ads

Server-side patterns in depth.

Show ads

Client rendering, variants, custom renderers.

Pixel

Drop gr-pix.js on your site — SDK auto-picks up the visitor ID for better attribution. Optional WebView install for native apps.

Experiments

How the engine auto-optimizes creative mix for your traffic.