@gravity-ai/api
v1.1.6
Published
Gravity JS SDK for retrieving targeted advertisements
Downloads
2,539
Readme
@gravity-ai/api
JavaScript/TypeScript SDK for Gravity ads.
import { Gravity } from '@gravity-ai/api';
const gravity = new Gravity({ production: true }); // reads GRAVITY_API_KEY from env
const result = await gravity.getAds(req, messages, placements);Works with Express, Fastify, Next.js, Node http, Bun, and Deno.
Install
npm install @gravity-ai/apiSet GRAVITY_API_KEY in your server environment.
Integration
Client — your chat component
+ import { gravityContext } from '@gravity-ai/api';
async function sendMessage(prompt) {
+ const gravity_context = gravityContext({
+ sessionId: chatSession.id,
+ user: { userId: currentUser.id },
+ });
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ messages }),
+ body: JSON.stringify({ messages, gravity_context }),
});
}
sessionIdanduserIdare required. Pass your chat/conversation session ID and your authenticated user's ID.
gravityContext() auto-detects the runtime (browser vs Node/Bun/Deno) and collects the appropriate signals — user-agent, screen size, timezone, locale, etc. Nothing is written to disk, localStorage, or cookies.
Server — Node.js (Express / Fastify / any framework)
+ 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 your LLM response as usual...
res.setHeader('Content-Type', 'text/event-stream');
for await (const token of streamYourLLM(messages)) {
res.write(`data: ${JSON.stringify({ type: 'chunk', content: token })}\n\n`);
}
+ const { ads } = await adPromise;
+ res.write(`data: ${JSON.stringify({ type: 'done', ads })}\n\n`);
res.end();
});gravity.getAds() reads gravity_context from req.body, extracts the end-user's IP from headers, and calls the Gravity API. It never throws — returns { ads: [] } on any failure so your chat flow is never blocked.
Next.js Route Handler
import { Gravity } from '@gravity-ai/api';
const gravity = new Gravity({ production: true });
export async function POST(request: Request) {
const body = await request.json();
const { ads } = await gravity.getAds(
{ body, headers: Object.fromEntries(request.headers) },
body.messages,
[{ placement: 'below_response', placement_id: 'main' }],
);
return Response.json({ ads });
}Parallel with your LLM call
const [adResult, llmResult] = await Promise.allSettled([
gravity.getAds(req, messages, placements),
yourLLMCall(messages),
]);Ad latency never blocks the chat response.
Server — Python
For Python backends (FastAPI, Django, Flask), use the gravity-py package:
pip install gravity-sdk+ from gravity_sdk import Gravity
+ gravity = Gravity(production=True)
@app.post("/api/chat")
async def chat(request: Request):
body = await request.json()
messages = body["messages"]
+ ad_task = asyncio.create_task(
+ gravity.get_ads(request, messages, [{"placement": "chat", "placement_id": "main"}])
+ )
async def event_stream():
async for token in stream_your_llm(messages):
yield f"data: {json.dumps({'type': 'chunk', 'content': token})}\n\n"
+ ad_result = await ad_task
+ ads = [a.to_dict() for a in ad_result.ads]
+ yield f"data: {json.dumps({'type': 'done', 'ads': ads})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")API Reference
new Gravity(opts?)
Configure once at startup. All options are optional.
const gravity = new Gravity({
production: true,
relevancy: 0.3,
excludedTopics: ['gambling'],
});| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | process.env.GRAVITY_API_KEY | Gravity API key |
| production | boolean | false | true = real ads. false = test ads (no billing). |
| relevancy | number | 0.2 | Minimum relevancy threshold (0-1). Lower = more ads. |
| timeoutMs | number | 3000 | Request timeout in ms |
| excludedTopics | string[] | — | Topics to exclude from ad matching |
| gravityApi | string | production URL | Custom API endpoint |
gravity.getAds(req, messages, placements, overrides?)
Fetch ads. Never throws. Returns { ads: [] } on any failure.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| req | object | Yes | Server request object. gravity_context is read from req.body. |
| messages | MessageObject[] | Yes | Conversation [{ role, content }] — last 2 turns sent |
| placements | PlacementObject[] | Yes | Ad slots, e.g. [{ placement: "below_response", placement_id: "main" }] |
| overrides | GravityAdsOptions | No | Per-call overrides merged on top of constructor config |
Returns: { ads: Ad[], status: number, elapsed: string, requestBody: object, error?: string }
gravityContext(overrides)
Client-side. Collects device + user context. Returns a plain object to include in your request body.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| overrides.sessionId | string | Yes | Your chat/conversation session ID |
| overrides.user.userId | string | Yes | Your authenticated user's ID |
| overrides.device | object | No | Device field overrides |
Returns: { sessionId, user: { id, ... }, device: { ua, timezone, locale, ... } }
Auto-detected fields vary by runtime:
| Field | Browser | Node/Bun/Deno |
|-------|---------|---------------|
| ua | navigator.userAgent | gravity-js/x.x.x (OS arch; Runtime/ver) |
| browser | "web" | "cli" |
| device_type | "mobile" or "desktop" | "server" (CI) or "desktop" |
| timezone | Intl.DateTimeFormat | Intl.DateTimeFormat |
| screen_width/height | screen.width/height | stdout.columns/rows |
| viewport_width/height | innerWidth/Height | stdout.columns/rows |
gravityAds(req, messages, placements, opts?) (low-level)
Standalone function — same as gravity.getAds() but takes all config per-call. Useful when you don't want to instantiate a class.
import { gravityAds } from '@gravity-ai/api';
const { ads } = await gravityAds(req, messages, placements, { production: true });Data Flow
┌──────────────────┐ gravity_context in body ┌──────────────────┐ POST /api/v1/ad ┌─────────┐
│ Client code │ ────────────────────────────▶ │ Your server │ ──────────────────▶ │ Gravity │
│ │ │ │ │ API │
│ gravityContext() │ { sessionId, │ gravity.getAds( │ { messages, │ │
│ auto-detects │ user: {...}, │ req, messages, │ sessionId, │ returns │
│ user + device │ device: {...} } │ placements) │ user: {...}, │ Ad[] │
│ │ │ reads ctx from │ device: {ip,...} } │ │
└──────────────────┘ │ req.body + IP │ └─────────┘
└──────────────────┘Rendering Ads
Terminal (OpenTUI)
For OpenTUI terminal apps, createGravityAdCard() gives you a ready-to-use ad card with impression tracking, click-to-open-browser, and hover affordance:
import { createGravityAdCard } from '@gravity-ai/api/opentui';
const adCard = createGravityAdCard({ renderer, input });
root.add(adCard.panel);
// In your SSE stream consumer:
if (event.type === 'ad') {
adCard.showAd(event.ads[0]); // populates content, fires impression, wires click
}Every visual property is overridable — pass panel, text, cta, hoverPanel style overrides, or mutate adCard.elements directly. For fully custom layouts, use the low-level gravityAdTracking() primitive instead.
Requires @opentui/core as a peer dependency (bun add @opentui/core). See OPENTUI.md for the full API reference, custom layout guide, and theme matching.
React
npm install @gravity-ai/reactimport { GravityAd } from '@gravity-ai/react';
<GravityAd ad={ads[0]} variant="card" />See @gravity-ai/react for component docs, variants, and styling.
Vanilla JS / any framework
Ads are plain JSON objects. Render them however you want:
const ad = ads[0];
if (ad) {
element.innerHTML = `<a href="${ad.clickUrl}">${ad.adText}</a>`;
if (ad.impUrl) new Image().src = ad.impUrl;
}License
MIT
