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
| Parameter | Type | Description | Example |
|---|---|---|---|
networkId | string | Your Ring DAS network identifier | "network-12345" |
placements | array | Array of ad placement requests | See below |
Placement Object
Each placement in the placements array should include:
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
id | string | Yes | Unique identifier for this request | "placement-1" |
placementId | string | Yes | Ring DAS placement ID | "homepage-banner" |
size | string | Yes | Ad dimensions (WIDTHxHEIGHT) | "728x90" |
allowedFormats | array | No | Allowed creative formats | ["image", "html"] |
minCpm | number | No | Minimum CPM floor price | 1.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:
| Reason | Description | Action |
|---|---|---|
NO_ELIGIBLE_ADS | No campaigns match targeting | Check campaign targeting settings |
BUDGET_EXHAUSTED | All campaigns out of budget | Check campaign budgets |
BELOW_FLOOR_PRICE | All bids below floor price | Adjust floor pricing or campaign bids |
FREQUENCY_CAP | User reached frequency cap | Expected behavior, no action needed |
USER_BLOCKED | User opted out of ads | Respect user preference |
INVALID_PLACEMENT | Placement ID not found | Verify 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
Authorizationheader - 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" \
--compressedTesting
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
Set up conversion tracking and analytics with DAS Pixel
Validate and test your Ring DAS implementation
Complete API documentation for ad requests
Related Topics
- Integration Overview - Choose your integration approach
- Authentication - API authentication methods
- Rate Limits & Quotas - Platform limits
- Troubleshooting - Common ad delivery issues
Updated about 2 months ago
