Request Deduplication
Automatic deduplication of parallel requests for the same input. This prevents redundant API calls when multiple parts of your application request the same ad simultaneously.
How It Works
When multiple getAd() calls are made for the same input before the first one completes, they all share the same API request:
const ads = new Ads({ publisherId: 'pub-xxx' });
// Fire 3 parallel requests for the same input
const [ad1, ad2, ad3] = await Promise.all([
ads.getAd({ input: 'database optimization' }),
ads.getAd({ input: 'database optimization' }),
ads.getAd({ input: 'database optimization' }),
]);
// Only 1 API call was made!
// All 3 return the same ad
console.log(ad1?.id === ad2?.id); // true
console.log(ad2?.id === ad3?.id); // trueDeduplication is automatic and requires no configuration. It works with or without caching enabled.
Benefits
| Benefit | Description |
|---|---|
| Reduced API calls | Parallel requests share a single network call |
| Lower latency | Subsequent requests get the result immediately |
| Cost savings | Fewer requests = lower API costs |
| Rate limit protection | Helps avoid hitting rate limits |
Different Inputs
Requests with different inputs are not deduplicated:
// These make 3 separate API calls
const [ad1, ad2, ad3] = await Promise.all([
ads.getAd({ input: 'query 1' }),
ads.getAd({ input: 'query 2' }),
ads.getAd({ input: 'query 3' }),
]);
// Each gets a different ad
console.log(ad1?.id !== ad2?.id); // trueDeduplication Key
Requests are deduplicated based on:
- Publisher ID
- Input text (or text hash in signals mode)
- Privacy mode
// These are deduplicated (same key)
ads.getAd({ input: 'query' });
ads.getAd({ input: 'query' });
// These are NOT deduplicated (different inputs)
ads.getAd({ input: 'query 1' });
ads.getAd({ input: 'query 2' });
// These are NOT deduplicated (different publishers)
ads.getAd({ input: 'query', publisherId: 'pub-a' });
ads.getAd({ input: 'query', publisherId: 'pub-b' });With Caching
Deduplication works alongside caching:
- Cache miss + first request: Creates in-flight request
- Cache miss + parallel request: Shares in-flight request
- Cache hit: Returns immediately from cache (no in-flight request needed)
const ads = new Ads({ publisherId: 'pub-xxx', cache: true });
// First batch: 1 API call (deduplication)
await Promise.all([
ads.getAd({ input: 'query' }),
ads.getAd({ input: 'query' }),
]);
// Second batch: 0 API calls (cache hit)
await Promise.all([
ads.getAd({ input: 'query' }),
ads.getAd({ input: 'query' }),
]);Error Handling
If a deduplicated request fails, all waiting requests receive the same error:
const results = await Promise.allSettled([
ads.getAd({ input: 'query' }),
ads.getAd({ input: 'query' }),
ads.getAd({ input: 'query' }),
]);
// If the single API call fails, all 3 are rejected
results.forEach(r => {
if (r.status === 'rejected') {
console.log('All failed with:', r.reason);
}
});Cleanup
In-flight requests are automatically cleaned up:
- On success: Removed after result is returned
- On error: Removed after error is thrown
This ensures subsequent requests make fresh API calls.
Batch Requests
adsBatch() also benefits from deduplication:
// If inputs have duplicates, only unique inputs are fetched
await ads.adsBatch({
inputs: ['q1', 'q1', 'q2', 'q2', 'q3'],
});
// Makes 3 API calls, not 5Implementation Note
Deduplication uses an in-memory Map of Promises. When a request starts:
- Check if there’s an in-flight request for this key
- If yes, return the existing Promise
- If no, create the request and store the Promise
- When complete, remove from the map