# Ads

export const searchAdsCurl = `curl https://api.afterlib.com/v1/ads/search \\
  -H "Authorization: Bearer $AFTERLIB_API_KEY" \\
  -H "Content-Type: application/json" \\
  -d '{
    "search": "coffee",
    "limit": 10,
    "sortBy": "performance",
    "sortDirection": "desc"
  }'`;

export const searchAdsJs = `const response = await fetch('https://api.afterlib.com/v1/ads/search', {
  method: 'POST',
  headers: {
    Authorization: \`Bearer \${process.env.AFTERLIB_API_KEY}\`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    search: 'coffee',
    limit: 10,
    sortBy: 'performance',
    sortDirection: 'desc',
  }),
});

const result = await response.json();`;

export const searchAdsTs = `type AdsSearchRequest = {
  search?: string;
  limit?: number;
  cursor?: string;
  sortBy?: 'performance' | 'duplicates' | 'trend' | 'started_at' | 'audience_reach';
  sortDirection?: 'asc' | 'desc';
};

const body: AdsSearchRequest = {
  search: 'coffee',
  limit: 10,
  sortBy: 'performance',
  sortDirection: 'desc',
};

const response = await fetch('https://api.afterlib.com/v1/ads/search', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + process.env.AFTERLIB_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(body),
});

const result = await response.json();`;

export const searchAdsPhp = `$response = file_get_contents('https://api.afterlib.com/v1/ads/search', false, stream_context_create([
  'http' => [
    'method' => 'POST',
    'header' => [
      'Authorization: Bearer ' . getenv('AFTERLIB_API_KEY'),
      'Content-Type: application/json',
    ],
    'content' => json_encode([
      'search' => 'coffee',
      'limit' => 10,
      'sortBy' => 'performance',
      'sortDirection' => 'desc',
    ]),
  ],
]));

$result = json_decode($response, true);`;

export const searchAdsPython = `import json
import os
import urllib.request

request = urllib.request.Request(
    'https://api.afterlib.com/v1/ads/search',
    data=json.dumps({
        'search': 'coffee',
        'limit': 10,
        'sortBy': 'performance',
        'sortDirection': 'desc',
    }).encode('utf-8'),
    method='POST',
    headers={
        'Authorization': f"Bearer {os.environ['AFTERLIB_API_KEY']}",
        'Content-Type': 'application/json',
    },
)

with urllib.request.urlopen(request) as response:
    result = json.loads(response.read().decode('utf-8'))`;

export const filteredAdsCurl = `curl https://api.afterlib.com/v1/ads/search \\
  -H "Authorization: Bearer $AFTERLIB_API_KEY" \\
  -H "Content-Type: application/json" \\
  -d '{
    "search": "coffee",
    "countries": {"include": ["US"], "operator": "or"},
    "technologies": {"include": ["shopify"], "operator": "or"},
    "duplicates": {"gte": 100},
    "dateRange": {"gte": "2025-01-01"},
    "limit": 25
  }'`;

export const filteredAdsJs = `const response = await fetch('https://api.afterlib.com/v1/ads/search', {
  method: 'POST',
  headers: {
    Authorization: \`Bearer \${process.env.AFTERLIB_API_KEY}\`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    search: 'coffee',
    countries: {include: ['US'], operator: 'or'},
    technologies: {include: ['shopify'], operator: 'or'},
    duplicates: {gte: 100},
    dateRange: {gte: '2025-01-01'},
    limit: 25,
  }),
});`;

export const filteredAdsTs = `type RangeFilter = {gte?: number; lte?: number};
type DateRangeFilter = {gte?: string; lte?: string};
type IncludeExcludeFilter = {
  include?: string[];
  exclude?: string[];
  operator?: 'or' | 'and' | 'exact';
};

type AdsSearchRequest = {
  search?: string;
  limit?: number;
  countries?: IncludeExcludeFilter;
  technologies?: IncludeExcludeFilter;
  duplicates?: RangeFilter;
  dateRange?: DateRangeFilter;
};

const body: AdsSearchRequest = {
  search: 'coffee',
  countries: {include: ['US'], operator: 'or'},
  technologies: {include: ['shopify'], operator: 'or'},
  duplicates: {gte: 100},
  dateRange: {gte: '2025-01-01'},
  limit: 25,
};

const response = await fetch('https://api.afterlib.com/v1/ads/search', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + process.env.AFTERLIB_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(body),
});`;

export const filteredAdsPhp = `$response = file_get_contents('https://api.afterlib.com/v1/ads/search', false, stream_context_create([
  'http' => [
    'method' => 'POST',
    'header' => [
      'Authorization: Bearer ' . getenv('AFTERLIB_API_KEY'),
      'Content-Type: application/json',
    ],
    'content' => json_encode([
      'search' => 'coffee',
      'countries' => ['include' => ['US'], 'operator' => 'or'],
      'technologies' => ['include' => ['shopify'], 'operator' => 'or'],
      'duplicates' => ['gte' => 100],
      'dateRange' => ['gte' => '2025-01-01'],
      'limit' => 25,
    ]),
  ],
]));`;

export const filteredAdsPython = `import json
import os
import urllib.request

request = urllib.request.Request(
    'https://api.afterlib.com/v1/ads/search',
    data=json.dumps({
        'search': 'coffee',
        'countries': {'include': ['US'], 'operator': 'or'},
        'technologies': {'include': ['shopify'], 'operator': 'or'},
        'duplicates': {'gte': 100},
        'dateRange': {'gte': '2025-01-01'},
        'limit': 25,
    }).encode('utf-8'),
    method='POST',
    headers={
        'Authorization': f"Bearer {os.environ['AFTERLIB_API_KEY']}",
        'Content-Type': 'application/json',
    },
)`;

export const getAdCurl = `curl https://api.afterlib.com/v1/ads/018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1 \\
  -H "Authorization: Bearer $AFTERLIB_API_KEY"`;

export const getAdJs = `const response = await fetch(
  'https://api.afterlib.com/v1/ads/018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1',
  {headers: {Authorization: \`Bearer \${process.env.AFTERLIB_API_KEY}\`}},
);

const ad = await response.json();`;

export const getAdTs = `type GetAdRequest = {
  adId: string;
};

const request: GetAdRequest = {
  adId: '018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1',
};

const response = await fetch('https://api.afterlib.com/v1/ads/018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1', {
  headers: {
    Authorization: 'Bearer ' + process.env.AFTERLIB_API_KEY,
  },
});

const ad = await response.json();`;

export const getAdPhp = `$response = file_get_contents('https://api.afterlib.com/v1/ads/018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1', false, stream_context_create([
  'http' => [
    'header' => 'Authorization: Bearer ' . getenv('AFTERLIB_API_KEY'),
  ],
]));

$ad = json_decode($response, true);`;

export const getAdPython = `import json
import os
import urllib.request

request = urllib.request.Request(
    'https://api.afterlib.com/v1/ads/018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1',
    headers={'Authorization': f"Bearer {os.environ['AFTERLIB_API_KEY']}"},
)

with urllib.request.urlopen(request) as response:
    ad = json.loads(response.read().decode('utf-8'))`;

## Search ads

Search ads with optional text, range, date, country, technology, and sorting filters. Returned ad records include `appUrl`, a direct link to open that ad in the AfterLib web app.

| Parameter | Location | Type | Default | Allowed values | Description |
| --- | --- | --- | --- | --- | --- |
| `search` | JSON body | string | none | up to 200 chars | Keyword search across public ad fields. |
| `limit` | JSON body | number | `20` | `1` to `100` | Maximum ads returned. |
| `cursor` | JSON body | string | none | value from previous response | Fetch the next cursor page. |
| `sortBy` | JSON body | string | `performance` | `performance`, `duplicates`, `trend`, `started_at`, `audience_reach`, `duplicates_change_p_all`, `audience_reach_change_p_all` | Sort field. |
| `sortDirection` | JSON body | string | `desc` | `asc`, `desc` | Sort direction. |
| `textSearch` | JSON body | string or object | none | text string or field include/exclude object | Match text in specific public fields. |
| `duplicates`, `performance`, `audienceReach` | JSON body | object | none | `{ "gte": number, "lte": number }` | Numeric range filters. |
| `dateRange` | JSON body | object | none | `{ "gte": "YYYY-MM-DD", "lte": "YYYY-MM-DD" }` | Active date range filter. |
| `countries`, `technologies`, `publisherPlatform` | JSON body | object | none | `{ "include": [], "exclude": [], "operator": "or" }` | Include/exclude filters. |
| `displayFormat`, `ctaType` | JSON body | string array | none | supported public values | Filter by ad format or call to action. |

<ApiExampleTabs curl={searchAdsCurl} javascript={searchAdsJs} typescript={searchAdsTs} php={searchAdsPhp} python={searchAdsPython} />

Response:

```json
{
	"items": [
		{
			"id": "018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1",
			"appUrl": "https://app.afterlib.com/ads/018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1",
			"headline": "Bring home cleaner skin",
			"body": "A concise public ad response with creative metadata, media, page, and website context.",
			"startedAt": "2025-08-26T09:04:49.640Z",
			"lastSeenAt": "2025-09-03T09:04:49.640Z",
			"duplicates": 1228,
			"performancePublic": 100,
			"trendPublic": 0,
			"countries": ["US"],
			"displayFormat": "video",
			"ctaType": "SHOP_NOW",
			"media": [
				{
					"type": "THUMB",
					"url": "creative-thumb.jpg",
					"mediaFormat": "jpg",
					"urls": {"preview": "https://box.afterlib.com/example-preview"}
				}
			],
			"page": {"pageName": "Drift Wellness Company", "adsAmount": 4},
			"website": {"siteDomain": "seed-store.afterlib.local"}
		}
	],
	"cursor": null,
	"hasMore": false,
	"totalHits": 1
}
```

## Search recipe: filtered winners

Use include filters and ranges together when you want a narrower set of ads that match a market and threshold.

<ApiExampleTabs curl={filteredAdsCurl} javascript={filteredAdsJs} typescript={filteredAdsTs} php={filteredAdsPhp} python={filteredAdsPython} />

## Fetch one ad

Fetch one ad by its public UUID.

| Parameter | Location | Type | Default | Allowed values | Description |
| --- | --- | --- | --- | --- | --- |
| `adId` | path | UUID string | required | public ad UUID | The ad to fetch. |

<ApiExampleTabs curl={getAdCurl} javascript={getAdJs} typescript={getAdTs} php={getAdPhp} python={getAdPython} />

Response:

```json
{
	"id": "018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1",
	"appUrl": "https://app.afterlib.com/ads/018f2f8e-8f32-7c11-a4a6-33c73a4d7ad1",
	"headline": "Bring home cleaner skin",
	"duplicates": 1228,
	"performancePublic": 100,
	"countries": ["US"],
	"page": {"pageName": "Drift Wellness Company", "adsAmount": 4},
	"website": {"siteDomain": "seed-store.afterlib.local"}
}
```

`POST /v1/ads/search` and `GET /v1/ads/{adId}` can charge credits for returned unique ads. Save `X-Afterlib-Request-Id` when debugging unexpected results or credit charges.