Skip to Content
HTTP API & Mobile
API Reference

HTTP API v1 & Mobile Integration

NotPixel exposes a language-agnostic JSON HTTP API at https://api.notpixel.ai. You can integrate directly from Python, Go, Java, PHP, Ruby, Rust, or orchestrate Mobile apps (React Native, iOS Swift, Android Kotlin, Flutter) through your backend.

OpenAPI 3.1.0 Specification: Download or import /openapi.json (also served live at https://api.notpixel.ai/openapi.json) to generate typed clients with openapi-generator, oapi-codegen, or Postman/Insomnia.


Base URL & Conventions

  • Base URL: https://api.notpixel.ai
  • Content-Type: application/json
  • Idempotency:
    • Pass a stable requestId per ad opportunity on POST /api/v1/pick so network retries return the existing auction decision (_meta.deduplicated: true) without double-charging.
    • POST /api/v1/click is automatically idempotent per impressionId.
    • POST /api/v1/convert is idempotent per (impressionId, externalId).
    • POST /api/v1/feedback is idempotent per eventId.

Endpoints Reference

1. Select a Contextual Ad (POST /api/v1/pick)

Returns an eligible contextual ad and the identifiers needed to display it and track events.

Send a non-zero, normalized 384-dimensional vector generated locally with Xenova/bge-small-en-v1.5, using mean pooling and L2 normalization. The local SDK entry points generate this vector automatically. For remote processing, send input instead of embedding, as shown below. Different embedding models are not compatible.

Placements

Create a placement under the verified domain or app in Publisher → Settings → Domains & apps. Pass its public code as placement on /api/v1/pick. The code identifies the property on backend requests without context.origin. The browser confirms from that same verified origin; native clients use an attested app session. Use a separate requestId for each position on a page and reuse it for retries of that opportunity. Preview events are excluded from the placement report.

Remote context processing

If you do not run a local model, send sanitized context and let NotPixel process it using its Cloudflare inference infrastructure:

{ "publisherId": "pub_10565", "requestId": "req_message_001", "input": "How can I optimize PostgreSQL queries?", "context": { "lang": "en" } }

Send exactly one of input or embedding; sending both or neither is invalid. There is no REST mode parameter. Remote processing requires an eligible active publisher and is subject to included usage limits. Context is sanitized again and limited to 256 characters before inference; it is not stored in NotPixel’s request logs or database. The request body limit is 16 KiB for either format.

Remote quota exhaustion returns 429 remote_embedding_quota_exceeded with Retry-After. Unavailable processing or inference failure returns 503. Continue your application’s LLM answer without an ad when processing is unavailable.

SDK or direct REST integration

With a NotPixel SDK, local is the default; choose remote explicitly with embeddingMode (Python: embedding_mode). The installation assistant selects the correct packages and initialization. NotPixel manages the model and precision.

For direct REST calls using local processing, your application generates the vector before calling /api/v1/pick. Run the model in your application or on your own backend. In that local path, NotPixel receives the vector instead of the text. In the remote path, send input and NotPixel generates the vector for you.

The required contract is:

  • Model: Xenova/bge-small-en-v1.5.
  • Output: exactly 384 finite numeric values, with at least one non-zero value.
  • Processing: mean pooling over tokens using the attention mask, followed by L2 normalization.
  • Use the quantized q8 artifact, as in the example below.

Matching dimensions alone do not make vectors compatible. Do not substitute a different 384-dimensional model or use CLS pooling. This model is designed for English; setting context.lang does not translate text or switch embedding models.

Sanitize email and phone information locally and limit the context to 256 characters to follow the SDK’s preprocessing. Do not include prompt text or personal data in optional context metadata.

Generate the vector and call REST without the SDK

This Node.js example uses Transformers.js directly. Install it with npm install @huggingface/transformers@3.8.1 and save the example as pick-ad.mjs. The first use downloads model files; reuse the extractor for subsequent requests.

import { randomUUID } from 'node:crypto'; import { pipeline } from '@huggingface/transformers'; const extractor = await pipeline( 'feature-extraction', 'Xenova/bge-small-en-v1.5', { dtype: 'q8' }, ); // Relevant context, already free of email/phone information. const contextText = 'How can I optimize PostgreSQL queries?'.slice(0, 256); const output = await extractor(contextText, { pooling: 'mean', normalize: true, }); const embedding = Array.from(output.data); if (embedding.length !== 384 || !embedding.every(Number.isFinite) || !embedding.some(value => value !== 0)) { throw new Error('Invalid context embedding'); } // Keep this ID and payload when retrying the same ad opportunity. const requestId = `req_${randomUUID()}`; const response = await fetch('https://api.notpixel.ai/api/v1/pick', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ publisherId: 'YOUR_PUBLISHER_ID', requestId, embedding, context: { lang: 'en' }, }), }); if (!response.ok) throw new Error(`NotPixel HTTP ${response.status}`); const result = await response.json(); console.log(result.ad); // An eligible ad, or null when no ad is available.

The REST response contains the ad decision and tracking identifiers. Your application calls its chosen LLM provider separately and displays the returned ad alongside that provider’s answer. Never send an ad request if local embedding fails; your application can still deliver the LLM answer.

Request body shape

The array below is abbreviated for readability. A real request must include all 384 generated values.

{ "publisherId": "pub_10565", "requestId": "req_chat_msg_9f8a7b6c", "embedding": [/* all 384 numeric values from your local model */], "context": { "lang": "en" } }

Response (200 OK)

{ "requestId": "req_chat_msg_9f8a7b6c", "impressionId": "550e8400-e29b-41d4-a716-446655440000", "ad": { "id": "660e8400-e29b-41d4-a716-446655440111", "contentRevision": 1, "category": "general", "headline": "Neon Serverless Postgres", "body": "Branch your database like code and scale to zero automatically.", "disclosure": "Sponsored", "cta": { "label": "Start Free", "url": "https://neon.tech?utm_source=notpixel" } }, "_meta": { "priceCents": 1, "priceMilliCents": 650, "auctionMode": "live" } }

When no ad matches the minimum relevance or floor price, the API returns 200 OK with "ad": null and _meta.noFillReason (no_candidates, budget_exhausted, below_min_relevance, below_floor_price, targeting_mismatch).


2. Validate Cached Ad (POST /api/v1/ads/validate)

Validates whether a cached ad and contentRevision are still active and approved before displaying from a local cache.

{ "adId": "660e8400-e29b-41d4-a716-446655440111", "contentRevision": 1, "impressionId": "550e8400-e29b-41d4-a716-446655440000", "publisherId": "pub_10565" }

Response (200 OK): { "valid": true }


3. Acknowledge Impression (POST /api/v1/events)

Acknowledges that an auctioned impression from /api/v1/pick was rendered to the user.

/api/v1/events only acknowledges impressions already created by /api/v1/pick. Unauctioned impressionId or requestId values return 404 Impression not found.

{ "impressionId": "550e8400-e29b-41d4-a716-446655440000" }

Response (200 OK): { "ok": true }


4. Track Click (POST /api/v1/click)

Records a click on an auctioned impression. Repeated calls with the same impressionId are deduplicated and return the original clickId with "duplicate": true.

{ "impressionId": "550e8400-e29b-41d4-a716-446655440000", "metadata": { "placement": "chat_footer", "platform": "ios" } }

Response (200 OK):

{ "ok": true, "clickId": "770e8400-e29b-41d4-a716-446655440222" }

5. Track Conversion (POST /api/v1/convert)

Records a post-click or post-impression conversion. Provide externalId (for example, your order or signup ID) for automatic idempotency.

{ "impressionId": "550e8400-e29b-41d4-a716-446655440000", "type": "signup", "valueCents": 2900, "externalId": "usr_signup_99412", "metadata": { "plan": "pro" } }

Response (200 OK):

{ "ok": true, "conversionId": "880e8400-e29b-41d4-a716-446655440333", "duplicate": false }

6. Submit User Feedback (POST /api/v1/feedback)

Records when a user hides an ad (action: "hide") or reports a policy violation (action: "report").

{ "eventId": "990e8400-e29b-41d4-a716-446655440444", "impressionId": "550e8400-e29b-41d4-a716-446655440000", "action": "report", "reason": "misleading", "details": "Landing page price does not match offer" }

Response (201 Created or 200 OK on replay):

{ "ok": true, "eventId": "990e8400-e29b-41d4-a716-446655440444" }

Python SDK

Python 0.3.0 includes Ads / AsyncAds with offer, streaming, batch, prefetch and tracking. See the Python SDK guide for a complete integration. Direct HTTP remains available when you want to manage transport yourself.

Backend Examples (Python SDK, FastAPI HTTP, Go, curl)

# pip install notpixel from notpixel import AsyncAds # Generates 384-dimensional embeddings locally (BAAI/bge-small-en-v1.5). ads = AsyncAds(publisher_id="pub_10565", cache=True) async def handle_chat(prompt: str) -> dict: # Runs your LLM and contextual ad selection in parallel (best-effort) result = await ads.respond( input=prompt, llm=lambda: call_your_llm(prompt), ) return { "text": result.text, "ad": result.ad.to_dict() if result.ad else None, }

Mobile Integration (React Native, iOS, Android, Flutter)

For React Native and Expo, use the React Native SDK. It forwards calls to your chosen AI provider and looks up ads in parallel. Context processing is local by default, with explicit remote processing available. Native components handle presentation and tracking.

Mobile apps can also consume ads selected by their existing Node.js or Python backend. Use the tracking endpoints below for custom renderers. Keep permanent AI provider credentials on the integrator’s server.

The snippets below illustrate custom HTTP renderers; the React Native SDK provides visibility-aware tracking and feedback out of the box.

import Ads, { type Ad } from '@notpixel/react-native/local'; import { AdCard } from '@notpixel/react-native/react'; const ads = new Ads({ publisherId: 'YOUR_PUBLISHER_ID' }); // Render an ad received from your existing backend with native tracking. export function MobileAd({ ad, impressionId }: { ad: Ad; impressionId: string; }) { return <AdCard ad={ad} impressionId={impressionId} client={ads} />; }
Last updated on
Was this helpful?