Documentation Index
Fetch the complete documentation index at: https://docs.adcontextprotocol.org/llms.txt
Use this file to discover all available pages before exploring further.
Discover available advertising products based on campaign requirements using natural language briefs or structured filters.
Authentication: Optional (returns limited results without credentials)
Response Time: ~60 seconds (AI inference with back-end systems)
Request Schema: /schemas/v2/media-buy/get-products-request.json
Response Schema: /schemas/v2/media-buy/get-products-response.json
Quick Start
Discover products with a natural language brief:
import { testAgent } from '@adcp/client/testing';
import { GetProductsResponseSchema } from '@adcp/client';
const result = await testAgent.getProducts({
brief: 'Premium athletic footwear with innovative cushioning',
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
}
});
if (!result.success) {
throw new Error(`Request failed: ${result.error}`);
}
// Validate response against schema
const validated = GetProductsResponseSchema.parse(result.data);
console.log(`Found ${validated.products.length} products`);
// Access validated product fields
for (const product of validated.products) {
console.log(`- ${product.name} (${product.delivery_type})`);
console.log(` Formats: ${product.format_ids.map(f => f.id).join(', ')}`);
}
Using Structured Filters
You can also use structured filters instead of (or in addition to) a brief:
import { testAgent } from '@adcp/client/testing';
const result = await testAgent.getProducts({
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
},
filters: {
format_types: ['video'],
delivery_type: 'guaranteed',
standard_formats_only: true
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} guaranteed video products`);
}
Request Parameters
| Parameter | Type | Required | Description |
|---|
brief | string | No | Natural language description of campaign requirements |
brand_manifest | BrandManifest | string | No | Brand information (inline object or URL). See Brand Manifest |
filters | Filters | No | Structured filters (see below) |
Filters Object
| Parameter | Type | Description |
|---|
delivery_type | string | Filter by "guaranteed" or "non_guaranteed" |
is_fixed_price | boolean | Filter for fixed price vs auction products |
format_types | string[] | Filter by format types (e.g., ["video", "display"]) |
format_ids | FormatID[] | Filter by specific format IDs |
standard_formats_only | boolean | Only return products accepting IAB standard formats |
min_exposures | integer | Minimum exposures needed for measurement validity |
start_date | string | Campaign start date in ISO 8601 format (YYYY-MM-DD) for availability checks |
end_date | string | Campaign end date in ISO 8601 format (YYYY-MM-DD) for availability checks |
budget_range | object | Budget range to filter appropriate products (see Budget Range Object below) |
countries | string[] | Filter by target countries using ISO 3166-1 alpha-2 codes (e.g., ["US", "CA", "GB"]) |
channels | string[] | Filter by advertising channels (e.g., ["display", "video", "dooh", "ctv", "audio", "native"]) |
Budget Range Object
| Parameter | Type | Required | Description |
|---|
currency | string | Yes | ISO 4217 currency code (e.g., "USD", "EUR", "GBP") |
min | number | No* | Minimum budget amount |
max | number | No* | Maximum budget amount |
*At least one of min or max must be specified.
Response
Returns an array of products, each containing:
| Field | Type | Description |
|---|
product_id | string | Unique product identifier |
name | string | Human-readable product name |
description | string | Detailed product description |
publisher_properties | PublisherProperty[] | Array of publisher entries, each with publisher_domain and either property_ids or property_tags |
format_ids | FormatID[] | Supported creative format IDs |
delivery_type | string | "guaranteed" or "non_guaranteed" |
delivery_measurement | DeliveryMeasurement | How delivery is measured (impressions, views, etc.) |
pricing_options | PricingOption[] | Available pricing models (CPM, CPCV, etc.) |
brief_relevance | string | Why this product matches the brief (when brief provided) |
See schema for complete field list: get-products-response.json
Common Scenarios
Standard Catalog Discovery
import { testAgent } from '@adcp/client/testing';
// No brief = standard catalog products for maximum reach
const result = await testAgent.getProducts({
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
},
filters: {
delivery_type: 'non_guaranteed'
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} standard catalog products`);
}
import { testAgent } from '@adcp/client/testing';
// Find products supporting both video and display
const result = await testAgent.getProducts({
brief: 'Brand awareness campaign with video and display',
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
},
filters: {
format_types: ['video', 'display']
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} products supporting video and display`);
}
Budget and Date Filtering
import { testAgent } from '@adcp/client/testing';
// Find products within budget and date range for specific countries and channels
const result = await testAgent.getProducts({
brief: 'Q2 campaign for athletic footwear in North America',
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
},
filters: {
start_date: '2025-04-01',
end_date: '2025-06-30',
budget_range: {
min: 50000,
max: 100000,
currency: 'USD'
},
countries: ['US', 'CA'],
channels: ['display', 'video', 'ctv'],
delivery_type: 'guaranteed'
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} products for Q2 within budget`);
}
Property Tag Resolution
import { testAgent } from '@adcp/client/testing';
// Get products with property tags
const result = await testAgent.getProducts({
brief: 'Sports content',
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
}
});
if (result.success && result.data) {
// Products with property_tags in publisher_properties need resolution via list_authorized_properties
const productsWithTags = result.data.products.filter(p =>
p.publisher_properties?.some(pub => pub.property_tags && pub.property_tags.length > 0)
);
console.log(`${productsWithTags.length} products use property tags (large networks)`);
}
Guaranteed Delivery Products
import { testAgent } from '@adcp/client/testing';
// Find guaranteed delivery products for measurement
const result = await testAgent.getProducts({
brief: 'Guaranteed delivery for lift study',
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
},
filters: {
delivery_type: 'guaranteed',
min_exposures: 100000
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} guaranteed products with 100k+ exposures`);
}
import { testAgent } from '@adcp/client/testing';
// Find products that only accept IAB standard formats
const result = await testAgent.getProducts({
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
},
filters: {
standard_formats_only: true
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} products with standard formats only`);
}
Error Handling
| Error Code | Description | Resolution |
|---|
AUTH_REQUIRED | Authentication needed for full catalog | Provide credentials via auth header |
INVALID_REQUEST | Brief too long or malformed filters | Check request parameters |
POLICY_VIOLATION | Category blocked for advertiser | See policy response message for details |
Authentication Comparison
See the difference between authenticated and unauthenticated access:
import { testAgent, testAgentNoAuth } from '@adcp/client/testing';
// WITH authentication - full catalog with pricing
const fullCatalog = await testAgent.getProducts({
brief: 'Premium CTV inventory for brand awareness',
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
}
});
if (!fullCatalog.success) {
throw new Error(`Failed to get products: ${fullCatalog.error}`);
}
console.log(`With auth: ${fullCatalog.data.products.length} products`);
console.log(`First product pricing: ${fullCatalog.data.products[0].pricing_options.length} options`);
// WITHOUT authentication - limited public catalog
const publicCatalog = await testAgentNoAuth.getProducts({
brief: 'Premium CTV inventory for brand awareness',
brand_manifest: {
name: 'Nike',
url: 'https://nike.com'
}
});
if (!publicCatalog.success) {
throw new Error(`Failed to get products: ${publicCatalog.error}`);
}
console.log(`Without auth: ${publicCatalog.data.products.length} products`);
console.log(`First product pricing: ${publicCatalog.data.products[0].pricing_options?.length || 0} options`);
Key Differences:
- Product Count: Authenticated access returns more products, including private/custom offerings
- Pricing Information: Only authenticated requests receive detailed pricing options (CPM, CPCV, etc.)
- Targeting Details: Custom targeting capabilities may be restricted to authenticated users
- Rate Limits: Unauthenticated requests have lower rate limits
Authentication Behavior
- Without credentials: Returns limited catalog (standard catalog products), no pricing, no custom offerings
- With credentials: Returns complete catalog with pricing and custom products
See Authentication Guide for details.
Asynchronous Operations
Most product searches complete immediately, but some scenarios require asynchronous processing. When this happens, you’ll receive a status other than completed and can track progress through webhooks or polling.
When Search Runs Asynchronously
Product search may require async processing in these situations:
- Complex searches: Searching across multiple inventory sources or custom curation
- Needs clarification: Your brief is vague and the system needs more information
- Custom products: Bespoke product packages that require human review
Async Status Flow
POST /api/mcp/call_tool
{
"name": "get_products",
"arguments": {
"brief": "CTV inventory for sports audience",
"brand_manifest": { "name": "Nike", "url": "https://nike.com" }
}
}
Response (200 OK):
{
"status": "completed",
"message": "Found 3 products matching your requirements",
"data": {
"products": [...]
}
}
Needs Clarification
When the brief is unclear, the system asks for more details:Response (200 OK):
{
"status": "input-required",
"message": "I need a bit more information. What's your budget range and campaign duration?",
"task_id": "task_789",
"context_id": "ctx_123",
"data": {
"reason": "CLARIFICATION_NEEDED",
"partial_results": [],
"suggestions": ["$50K-$100K", "1 month", "Q1 2024"]
}
}
Continue the conversation with the same context_id:POST /api/mcp/continue
{
"context_id": "ctx_123",
"message": "Budget is $75K for a 3-week campaign in March"
}
Response (200 OK):
{
"status": "completed",
"message": "Perfect! Found 5 products within your budget",
"data": {
"products": [...]
}
}
Complex Search (With Webhook)
For searches requiring deep inventory analysis, configure a webhook:POST /api/mcp/call_tool
{
"name": "get_products",
"arguments": {
"brief": "Premium inventory across all formats for luxury automotive brand",
"brand_manifest": { "name": "Porsche", "url": "https://porsche.com" },
"pushNotificationConfig": {
"url": "https://buyer.com/webhooks/adcp/get_products",
"authentication": {
"schemes": ["Bearer"],
"credentials": "secret_token_32_chars"
}
}
}
}
Response (200 OK):
{
"status": "working",
"message": "Searching premium inventory across display, video, and audio",
"task_id": "task_456",
"context_id": "ctx_123",
"data": {
"percentage": 10,
"current_step": "searching_inventory"
}
}
// Later, webhook POST to https://buyer.com/webhooks/adcp/get_products
{
"task_id": "task_456",
"task_type": "get_products",
"status": "completed",
"timestamp": "2025-01-22T10:30:00Z",
"message": "Found 12 premium products across all formats",
"result": {
"products": [...]
}
}
POST /api/a2a
{
"message": {
"role": "user",
"parts": [{
"kind": "data",
"data": {
"skill": "get_products",
"parameters": {
"brief": "CTV inventory for sports audience",
"brand_manifest": { "name": "Nike", "url": "https://nike.com" }
}
}
}]
}
}
Response (200 OK):
{
"id": "task_123",
"contextId": "ctx_456",
"artifact": {
"kind": "data",
"data": {
"products": [...]
}
},
"status": {
"state": "completed",
"message": {
"role": "agent",
"parts": [{ "text": "Found 3 products matching your requirements" }]
}
}
}
Needs Clarification
Real-time updates via SSE when clarification is needed:// Initial response
{
"id": "task_789",
"contextId": "ctx_123",
"status": {
"state": "input-required",
"message": {
"role": "agent",
"parts": [
{ "text": "I need a bit more information. What's your budget range and campaign duration?" },
{
"data": {
"reason": "CLARIFICATION_NEEDED",
"suggestions": ["$50K-$100K", "1 month", "Q1 2024"]
}
}
]
}
}
}
// Send follow-up
POST /api/a2a
{
"contextId": "ctx_123",
"message": {
"role": "user",
"parts": [{ "text": "Budget is $75K for a 3-week campaign in March" }]
}
}
// SSE update: task completed
{
"id": "task_789",
"contextId": "ctx_123",
"artifact": {
"kind": "data",
"data": { "products": [...] }
},
"status": {
"state": "completed",
"message": {
"role": "agent",
"parts": [{ "text": "Perfect! Found 5 products within your budget" }]
}
}
}
Complex Search (With Webhook)
Configure push notifications for long searches:POST /api/a2a
{
"message": {
"role": "user",
"parts": [{
"kind": "data",
"data": {
"skill": "get_products",
"parameters": {
"brief": "Premium inventory across all formats for luxury automotive brand",
"brand_manifest": { "name": "Porsche", "url": "https://porsche.com" }
}
}
}]
},
"pushNotificationConfig": {
"url": "https://buyer.com/webhooks/a2a/get_products",
"authentication": {
"schemes": ["bearer"],
"credentials": "secret_token_32_chars"
}
}
}
Response (200 OK):
{
"id": "task_456",
"contextId": "ctx_789",
"status": {
"state": "working",
"message": {
"role": "agent",
"parts": [
{ "text": "Searching premium inventory across display, video, and audio" },
{
"data": {
"percentage": 10,
"current_step": "searching_inventory"
}
}
]
}
}
}
// Later, webhook POST to https://buyer.com/webhooks/a2a/get_products
{
"id": "task_456",
"contextId": "ctx_789",
"artifact": {
"kind": "data",
"data": {
"products": [...]
}
},
"status": {
"state": "completed",
"message": {
"role": "agent",
"parts": [
{ "text": "Found 12 premium products across all formats" },
{
"data": {
"products": [...]
}
}
]
},
"timestamp": "2025-01-22T10:30:00Z"
}
}
Status Overview
| Status | When It Happens | What You Do |
|---|
completed | Search finished successfully | Process the product results |
input-required | Need clarification on the brief | Answer the question and continue |
working | Searching across multiple sources | Wait for webhook or poll for updates |
submitted | Custom curation queued | Wait for webhook notification |
failed | Search couldn’t complete | Check error message, adjust brief |
Note: For the complete status list see Core Concepts - Task Status System.
Most searches complete immediately. Async processing is only needed for complex scenarios or when the system needs your input.
Next Steps
After discovering products:
- Review Options: Compare products, pricing, and targeting capabilities
- Create Media Buy: Use
create_media_buy to execute campaign
- Prepare Creatives: Use
list_creative_formats to see format requirements
- Upload Assets: Use
sync_creatives to provide creative assets
Learn More