Ad Request Integration

Learn how to make ad requests and handle responses in Ring DAS

Ad Request Integration

This guide shows you how to make your first ad request, handle responses, and implement proper error handling across different integration methods.

Making Your First Ad Request

JavaScript SDK (Client-Side)

The simplest way to request ads is using the Ring DAS JavaScript SDK:

Basic Ad Request

<!DOCTYPE html>
<html>
<head>
  <!-- Step 1: Load Ring DAS SDK -->
  <script async src="https://cdn.ringdas.com/sdk/v1/ringdas.js"></script>

  <!-- Step 2: Initialize SDK -->
  <script>
    window.ringDAS = window.ringDAS || [];
    ringDAS.push(function() {
      ringDAS.init({
        networkId: 'YOUR_NETWORK_ID',
        debug: true  // Enable console logging for development
      });
    });
  </script>
</head>
<body>
  <!-- Step 3: Create ad container -->
  <div id="my-ad-slot"></div>

  <!-- Step 4: Request and display ad -->
  <script>
    ringDAS.push(function() {
      ringDAS.displayAd('my-ad-slot', {
        placementId: 'YOUR_PLACEMENT_ID',
        size: '728x90'
      });
    });
  </script>
</body>
</html>

Advanced Request with Targeting

ringDAS.push(function() {
  ringDAS.displayAd('my-ad-slot', {
    placementId: 'YOUR_PLACEMENT_ID',
    size: '728x90',

    // Targeting parameters
    targeting: {
      category: ['technology', 'gadgets'],
      section: 'homepage',
      custom_key: 'custom_value'
    },

    // User context (if available)
    user: {
      aid: 'USER_AID_HASH',  // Logged-in user identifier
      segments: ['tech-enthusiasts', 'premium-subscribers']
    },

    // Callbacks
    onLoad: function(ad) {
      console.log('Ad loaded:', ad);
    },
    onError: function(error) {
      console.error('Ad failed to load:', error);
    }
  });
});

HTTP API (Server-Side)

For server-side rendering or mobile apps, use the HTTP API:

Basic HTTP Request

curl -X POST https://api.ringdas.com/v1/ads/request \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "networkId": "YOUR_NETWORK_ID",
    "placements": [
      {
        "id": "placement-1",
        "placementId": "YOUR_PLACEMENT_ID",
        "size": "728x90"
      }
    ],
    "device": {
      "type": "desktop",
      "ua": "Mozilla/5.0...",
      "ip": "192.168.1.1"
    },
    "page": {
      "url": "https://example.com/article",
      "referrer": "https://google.com"
    }
  }'

Node.js Example

const fetch = require('node-fetch');

async function requestAd(placementId) {
  const response = await fetch('https://api.ringdas.com/v1/ads/request', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.RINGDAS_API_KEY}`
    },
    body: JSON.stringify({
      networkId: process.env.RINGDAS_NETWORK_ID,
      placements: [
        {
          id: 'placement-1',
          placementId: placementId,
          size: '728x90'
        }
      ],
      device: {
        type: 'desktop',
        ua: req.headers['user-agent'],
        ip: req.ip
      },
      page: {
        url: req.url,
        referrer: req.headers.referer
      }
    })
  });

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  const data = await response.json();
  return data;
}

// Usage
requestAd('YOUR_PLACEMENT_ID')
  .then(ad => console.log('Ad received:', ad))
  .catch(error => console.error('Error:', error));

Python Example

import requests
import os

def request_ad(placement_id):
    url = "https://api.ringdas.com/v1/ads/request"

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {os.getenv('RINGDAS_API_KEY')}"
    }

    payload = {
        "networkId": os.getenv("RINGDAS_NETWORK_ID"),
        "placements": [
            {
                "id": "placement-1",
                "placementId": placement_id,
                "size": "728x90"
            }
        ],
        "device": {
            "type": "desktop"
        }
    }

    response = requests.post(url, json=payload, headers=headers)
    response.raise_for_status()

    return response.json()

# Usage
try:
    ad_data = request_ad("YOUR_PLACEMENT_ID")
    print("Ad received:", ad_data)
except requests.exceptions.RequestException as e:
    print(f"Error: {e}")

Request Parameters

Required Parameters

ParameterTypeDescriptionExample
networkIdstringYour Ring DAS network identifier"network-12345"
placementsarrayArray of ad placement requestsSee below

Placement Object

Each placement in the placements array should include:

ParameterTypeRequiredDescriptionExample
idstringYesUnique identifier for this request"placement-1"
placementIdstringYesRing DAS placement ID"homepage-banner"
sizestringYesAd dimensions (WIDTHxHEIGHT)"728x90"
allowedFormatsarrayNoAllowed creative formats["image", "html"]
minCpmnumberNoMinimum CPM floor price1.50

Device Context (Recommended)

{
  "device": {
    "type": "desktop",           // desktop, mobile, tablet
    "ua": "Mozilla/5.0...",      // User agent string
    "ip": "192.168.1.1",         // User IP address
    "language": "en-US",         // Browser language
    "platform": "Windows"        // Operating system
  }
}

Page Context (Recommended)

{
  "page": {
    "url": "https://example.com/article/tech-news",
    "referrer": "https://google.com/search?q=...",
    "title": "Latest Tech News",
    "keywords": ["technology", "gadgets", "innovation"],
    "categories": ["tech", "news"]
  }
}

User Context (Optional)

{
  "user": {
    "aid": "a1b2c3d4e5f6...",      // Hashed user email (logged-in)
    "lu": "2019051614375...",       // Browser cookie ID (anonymous)
    "segments": ["tech-readers", "premium-subscribers"],
    "gdprConsent": true,            // GDPR consent status
    "gdprConsentString": "CPX..."   // TCF consent string
  }
}

Targeting Parameters (Optional)

{
  "targeting": {
    "category": ["technology", "business"],
    "section": "homepage",
    "author": "john-doe",
    "tags": ["featured", "trending"],
    "custom": {
      "key1": "value1",
      "key2": ["value2", "value3"]
    }
  }
}

Response Handling

Success Response Structure

{
  "status": "success",
  "requestId": "req_abc123",
  "placements": [
    {
      "id": "placement-1",
      "placementId": "homepage-banner",
      "ad": {
        "adId": "ad_xyz789",
        "dealId": "deal_123",
        "lineItemId": "li_456",
        "creativeId": "cr_789",
        "format": "image",
        "size": "728x90",
        "cpm": 2.50,
        "currency": "USD",

        // Creative content
        "creative": {
          "html": "<a href='...'>...</a>",
          "imageUrl": "https://cdn.ringdas.com/creative/image.jpg",
          "clickUrl": "https://click.ringdas.com/click?...",
          "impressionUrl": "https://pixel.ringdas.com/impression?..."
        },

        // Tracking pixels
        "tracking": {
          "impressionPixels": [
            "https://pixel.ringdas.com/imp?id=ad_xyz789"
          ],
          "clickPixels": [
            "https://pixel.ringdas.com/click?id=ad_xyz789"
          ],
          "viewabilityPixels": [
            "https://pixel.ringdas.com/viewable?id=ad_xyz789"
          ]
        }
      }
    }
  ]
}

Rendering the Ad

Client-Side (JavaScript SDK)

The SDK handles rendering automatically, but you can also render manually:

ringDAS.push(function() {
  ringDAS.requestAd({
    placementId: 'YOUR_PLACEMENT_ID',
    size: '728x90'
  }).then(function(adResponse) {
    if (adResponse.ad) {
      // SDK provides a render helper
      ringDAS.renderAd('my-ad-slot', adResponse.ad);

      // Or render manually
      document.getElementById('my-ad-slot').innerHTML = adResponse.ad.creative.html;

      // Fire impression tracking
      ringDAS.trackImpression(adResponse.ad);
    } else {
      console.log('No ad available');
      // Show fallback content
      document.getElementById('my-ad-slot').innerHTML = '<p>Advertisement</p>';
    }
  });
});

Server-Side (Node.js)

function renderAd(adResponse, containerId) {
  if (!adResponse.placements[0].ad) {
    return `<div id="${containerId}"><!-- No ad available --></div>`;
  }

  const ad = adResponse.placements[0].ad;

  return `
    <div id="${containerId}" class="ringdas-ad">
      ${ad.creative.html}

      <!-- Impression tracking -->
      ${ad.tracking.impressionPixels.map(url =>
        `<img src="${url}" width="1" height="1" style="display:none;" />`
      ).join('')}
    </div>
  `;
}

// Express.js route example
app.get('/article', async (req, res) => {
  const adResponse = await requestAd('article-banner');
  const adHtml = renderAd(adResponse, 'article-ad-slot');

  res.render('article', { adHtml });
});

No Ad Response

When no ad is available, the response includes a noAd reason:

{
  "status": "success",
  "requestId": "req_abc123",
  "placements": [
    {
      "id": "placement-1",
      "placementId": "homepage-banner",
      "noAd": {
        "reason": "NO_ELIGIBLE_ADS",
        "details": "No active campaigns match targeting criteria"
      }
    }
  ]
}

Common noAd reasons:

ReasonDescriptionAction
NO_ELIGIBLE_ADSNo campaigns match targetingCheck campaign targeting settings
BUDGET_EXHAUSTEDAll campaigns out of budgetCheck campaign budgets
BELOW_FLOOR_PRICEAll bids below floor priceAdjust floor pricing or campaign bids
FREQUENCY_CAPUser reached frequency capExpected behavior, no action needed
USER_BLOCKEDUser opted out of adsRespect user preference
INVALID_PLACEMENTPlacement ID not foundVerify placement ID is correct

Error Scenarios

HTTP Error Responses

400 Bad Request

{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Missing required parameter: networkId",
    "details": {
      "field": "networkId",
      "issue": "required field is missing"
    }
  }
}

Common causes:

  • Missing required parameters
  • Invalid parameter format
  • Invalid placement ID or network ID

Solution: Validate request payload before sending

401 Unauthorized

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired API key"
  }
}

Common causes:

  • Missing Authorization header
  • Invalid API key
  • Expired API key

Solution: Verify API key and refresh if needed

403 Forbidden

{
  "error": {
    "code": "FORBIDDEN",
    "message": "API key does not have access to network: network-12345"
  }
}

Common causes:

  • API key doesn't have permission for this network
  • Network is disabled or suspended

Solution: Check API key permissions or contact support

429 Too Many Requests

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Retry after 60 seconds.",
    "retryAfter": 60
  }
}

Common causes:

  • Exceeded rate limit (500,000 requests/day default)
  • Too many concurrent requests

Solution: Implement rate limiting and exponential backoff

500 Internal Server Error

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred",
    "requestId": "req_abc123"
  }
}

Common causes:

  • Temporary server issue
  • Platform outage

Solution: Retry with exponential backoff, contact support if persistent

Error Handling Best Practices

Retry Strategy

async function requestAdWithRetry(placementId, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await requestAd(placementId);
      return response;
    } catch (error) {
      // Don't retry on client errors (4xx except 429)
      if (error.status >= 400 && error.status < 500 && error.status !== 429) {
        throw error;
      }

      // Last attempt failed
      if (attempt === maxRetries) {
        console.error(`Failed after ${maxRetries} attempts:`, error);
        throw error;
      }

      // Exponential backoff: 1s, 2s, 4s
      const delay = Math.pow(2, attempt - 1) * 1000;
      console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms`);
      await sleep(delay);
    }
  }
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Timeout Handling

async function requestAdWithTimeout(placementId, timeoutMs = 1000) {
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Request timeout')), timeoutMs);
  });

  const requestPromise = requestAd(placementId);

  try {
    return await Promise.race([requestPromise, timeoutPromise]);
  } catch (error) {
    if (error.message === 'Request timeout') {
      console.warn('Ad request timed out, showing fallback');
      return null; // Show fallback ad
    }
    throw error;
  }
}

Fallback Handling

async function displayAdWithFallback(containerId, placementId) {
  try {
    const adResponse = await requestAdWithTimeout(placementId, 500);

    if (adResponse && adResponse.placements[0].ad) {
      renderAd(containerId, adResponse.placements[0].ad);
    } else {
      renderFallbackAd(containerId);
    }
  } catch (error) {
    console.error('Ad request failed:', error);
    renderFallbackAd(containerId);
  }
}

function renderFallbackAd(containerId) {
  document.getElementById(containerId).innerHTML = `
    <div class="fallback-ad">
      <a href="/advertise">
        <img src="/images/house-ad.jpg" alt="Advertise with us" />
      </a>
    </div>
  `;
}

Code Examples

Multiple Ad Requests (Batch)

Request multiple placements in a single API call for better performance:

async function requestMultipleAds(placements) {
  const response = await fetch('https://api.ringdas.com/v1/ads/request', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      networkId: NETWORK_ID,
      placements: [
        { id: 'header', placementId: 'header-banner', size: '728x90' },
        { id: 'sidebar', placementId: 'sidebar-box', size: '300x250' },
        { id: 'footer', placementId: 'footer-banner', size: '728x90' }
      ],
      device: getDeviceContext(),
      page: getPageContext()
    })
  });

  return response.json();
}

// Render all ads
requestMultipleAds().then(data => {
  data.placements.forEach(placement => {
    if (placement.ad) {
      renderAd(placement.id, placement.ad);
    }
  });
});

React Integration

import React, { useEffect, useState } from 'react';

function AdPlacement({ placementId, size }) {
  const [ad, setAd] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function loadAd() {
      try {
        setLoading(true);
        const response = await requestAd(placementId);

        if (response.placements[0].ad) {
          setAd(response.placements[0].ad);
        }
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    loadAd();
  }, [placementId]);

  if (loading) return <div>Loading ad...</div>;
  if (error) return <div>Error loading ad</div>;
  if (!ad) return <div>No ad available</div>;

  return (
    <div
      className="ad-container"
      dangerouslySetInnerHTML={{ __html: ad.creative.html }}
    />
  );
}

// Usage
<AdPlacement placementId="homepage-banner" size="728x90" />

Mobile App (Swift/iOS)

import Foundation

class RingDASAdManager {
    static let shared = RingDASAdManager()

    private let networkId: String
    private let apiUrl = "https://api.ringdas.com/v1"
    private let apiKey: String

    init() {
        // Load from configuration
        self.networkId = Configuration.ringDASNetworkId
        self.apiKey = Configuration.ringDASApiKey
    }

    func requestAd(placementId: String, completion: @escaping (Result<Ad, Error>) -> Void) {
        let url = URL(string: "\(apiUrl)/ads/request")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")

        let body: [String: Any] = [
            "networkId": networkId,
            "placements": [
                [
                    "id": "placement-1",
                    "placementId": placementId,
                    "size": "320x50"
                ]
            ],
            "device": [
                "type": "mobile",
                "os": "iOS",
                "osVersion": UIDevice.current.systemVersion,
                "model": UIDevice.current.model
            ]
        ]

        request.httpBody = try? JSONSerialization.data(withJSONObject: body)

        URLSession.shared.dataTask(with: request) { data, response, error in
            if let error = error {
                completion(.failure(error))
                return
            }

            guard let data = data else {
                completion(.failure(NSError(domain: "RingDAS", code: -1)))
                return
            }

            do {
                let adResponse = try JSONDecoder().decode(AdResponse.self, from: data)
                if let ad = adResponse.placements.first?.ad {
                    completion(.success(ad))
                } else {
                    completion(.failure(NSError(domain: "RingDAS", code: -2)))
                }
            } catch {
                completion(.failure(error))
            }
        }.resume()
    }
}

// Usage
RingDASAdManager.shared.requestAd(placementId: "banner-placement") { result in
    switch result {
    case .success(let ad):
        DispatchQueue.main.async {
            self.displayAd(ad)
        }
    case .failure(let error):
        print("Failed to load ad: \(error)")
    }
}

Performance Optimization

Prefetching Ads

Request ads before they're needed:

// Prefetch ads on page load
window.addEventListener('DOMContentLoaded', () => {
  ringDAS.push(function() {
    ringDAS.prefetchAd('homepage-banner');
    ringDAS.prefetchAd('sidebar-box');
  });
});

// Display prefetched ad when needed
document.getElementById('show-ad-button').addEventListener('click', () => {
  ringDAS.displayPrefetchedAd('my-ad-slot', 'homepage-banner');
});

Lazy Loading

Load ads only when they enter viewport:

ringDAS.push(function() {
  ringDAS.init({
    networkId: 'YOUR_NETWORK_ID',
    lazyLoad: true,
    lazyLoadMargin: '200px'  // Load 200px before entering viewport
  });
});

Response Compression

Enable gzip compression for smaller payloads:

curl -X POST https://api.ringdas.com/v1/ads/request \
  -H "Content-Type: application/json" \
  -H "Accept-Encoding: gzip, deflate" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  --compressed

Testing

Debug Mode

Enable debug logging:

ringDAS.push(function() {
  ringDAS.init({
    networkId: 'YOUR_NETWORK_ID',
    debug: true  // Logs all requests and responses
  });
});

Test Ad Requests

Use test placement IDs for development:

const PLACEMENT_IDS = {
  production: 'prod-placement-123',
  staging: 'staging-placement-456',
  development: 'test-placement-789'
};

const placementId = PLACEMENT_IDS[process.env.NODE_ENV] || PLACEMENT_IDS.development;

Next Steps

Event Tracking Setup

Set up conversion tracking and analytics with DAS Pixel

Testing Your Integration

Validate and test your Ring DAS implementation

API Reference

Complete API documentation for ad requests

Related Topics