Recommendations API
For programmatic access to content recommendations without UI:
The official Teads SDK sample application includes working examples of this format. Use it for implementation examples and as a reference while you set up this placement.
Example
- Swift
- Objective-C
import TeadsSDK
class RecommendationsManager {
private var recommendationsPlacement: TeadsAdPlacementRecommendations?
func fetchRecommendations() async throws -> [OBRecommendation] {
// Create configuration
let config = TeadsAdPlacementRecommendationsURLConfig(
articleUrl: URL(string: "https://example.com/article/123")!,
widgetId: "SDK_1",
widgetIndex: 0
)
// Create placement
recommendationsPlacement = TeadsAdPlacementRecommendations(config, delegate: nil)
// Fetch recommendations
let loader = try recommendationsPlacement?.loadAd()
let recommendations = try await loader?()
return recommendations ?? []
}
// Custom UI for recommendations
func createCustomRecommendationView(for recommendation: OBRecommendation) -> UIView {
let view = UIView()
// Add image
let imageView = UIImageView()
if let imageUrl = URL(string: recommendation.imageUrl ?? "") {
// Load image asynchronously
URLSession.shared.dataTask(with: imageUrl) { data, _, _ in
if let data = data, let image = UIImage(data: data) {
DispatchQueue.main.async {
imageView.image = image
}
}
}.resume()
}
// Add title
let titleLabel = UILabel()
titleLabel.text = recommendation.content
titleLabel.font = .systemFont(ofSize: 16, weight: .semibold)
titleLabel.numberOfLines = 2
// Add source
let sourceLabel = UILabel()
sourceLabel.text = recommendation.source
sourceLabel.font = .systemFont(ofSize: 12)
sourceLabel.textColor = .secondaryLabel
// Layout views...
// Configure viewability tracking — required for accurate impression reporting
TeadsAdPlacementRecommendations.configureViewabilityPerListing(
for: view,
withRec: recommendation
)
return view
}
}
#import <TeadsSDK/TeadsSDK-Swift.h>
@interface RecommendationsManager : NSObject
@property (nonatomic, strong) TeadsAdPlacementRecommendations *recommendationsPlacement;
@end
@implementation RecommendationsManager
- (void)fetchRecommendationsWithCompletion:(void (^)(NSArray<OBRecommendation *> *recommendations))completion {
// Objective-C has no separate Config type for Recommendations — create the
// placement directly with its Objective-C-friendly convenience initializer
self.recommendationsPlacement = [[TeadsAdPlacementRecommendations alloc]
initWithArticleUrl:[NSURL URLWithString:@"https://example.com/article/123"]
widgetId:@"SDK_1"
widgetIndex:0
externalID:nil
delegate:nil];
// loadAd(completion:) is used instead of the Swift-only async
// `loadAd() throws -> () async throws -> [OBRecommendation]`
[self.recommendationsPlacement loadAdWithCompletion:^(OBRecommendationResponse *response) {
completion(response.recommendations ?: @[]);
}];
}
// Custom UI for recommendations
- (UIView *)createCustomRecommendationViewFor:(OBRecommendation *)recommendation {
UIView *view = [[UIView alloc] init];
// Add image
UIImageView *imageView = [[UIImageView alloc] init];
NSURL *imageUrl = recommendation.image.url;
if (imageUrl) {
// Load image asynchronously
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithURL:imageUrl
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
});
}
}
}];
[task resume];
}
// Add title
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = recommendation.content;
titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
titleLabel.numberOfLines = 2;
// Add source
UILabel *sourceLabel = [[UILabel alloc] init];
sourceLabel.text = recommendation.source;
sourceLabel.font = [UIFont systemFontOfSize:12];
sourceLabel.textColor = [UIColor secondaryLabelColor];
// Layout views...
// Configure viewability tracking — required for accurate impression reporting
[TeadsAdPlacementRecommendations configureViewabilityPerListingFor:view withRec:recommendation];
return view;
}
@end
Viewability Tracking
Call configureViewabilityPerListing(for:withRec:) for each recommendation view as you add it to your layout. Pass the view that displays the recommendation and its corresponding OBRecommendation:
- Swift
- Objective-C
// Call for each recommendation view once it is in the view hierarchy
TeadsAdPlacementRecommendations.configureViewabilityPerListing(
for: recommendationView,
withRec: recommendation
)
// Call for each recommendation view once it is in the view hierarchy
[TeadsAdPlacementRecommendations
configureViewabilityPerListingFor:recommendationView
withRec:recommendation];
Once configured, the SDK tracks the view and automatically reports viewable impressions to Teads — no additional work is required.
AdChoices compliance
Widget AdChoices icon
Your recommendations widget must display a clickable AdChoices icon — a UIButton at least 30×30 pt, positioned above the widget and hidden until recommendations load. When tapped, open the URL returned by TeadsAdPlacementRecommendations.getAboutURL() in Safari or an SFSafariViewController:
- Swift
- Objective-C
import SafariServices
if let url = TeadsAdPlacementRecommendations.getAboutURL() {
let safari = SFSafariViewController(url: url)
present(safari, animated: true)
}
#import <SafariServices/SafariServices.h>
NSURL *url = [TeadsAdPlacementRecommendations getAboutURL];
if (url) {
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:url];
[self presentViewController:safari animated:YES completion:nil];
}
RTB disclosure icon (paid recommendations)
Paid (RTB) recommendations require a per-item disclosure icon overlaid on the thumbnail. Add a UIButton (minimum 30×30 pt) above each recommendation image, hidden by default.
For each recommendation, check shouldDisplayDisclosureIcon(). If it returns true, reveal the button, load its icon from disclosure.imageUrl, and open disclosure.clickUrl on tap; otherwise keep it hidden so it never shows on organic recommendations:
- Swift
- Objective-C
if recommendation.shouldDisplayDisclosureIcon() {
disclosureButton.isHidden = false
// Load recommendation.disclosure?.imageUrl and set it as the button image
// On tap, open recommendation.disclosure?.clickUrl in Safari or an SFSafariViewController
} else {
disclosureButton.isHidden = true
}
if ([recommendation shouldDisplayDisclosureIcon]) {
disclosureButton.hidden = NO;
// Load recommendation.disclosure.imageUrl and set it as the button image
// On tap, open recommendation.disclosure.clickUrl in Safari or an SFSafariViewController
} else {
disclosureButton.hidden = YES;
}
Paid recommendations always appear in responses when test mode is enabled:
- Swift
- Objective-C
TeadsAdPlacementRecommendations.testRTB = true
TeadsAdPlacementRecommendations.testRTB = YES;
Best Practices
Viewability tracking is required for accurate impression reporting.
The widget AdChoices icon and the per-item disclosure icon on paid recommendations are required to ship.
Remember to remove the testRTB = true call before releasing to production.
Ad Lifecycle Listener
Implement TeadsAdPlacementEventsDelegate to observe playback, clicks, and viewability. See Placement Events Listener for the full event reference and error handling guide.
Checklist
- ✅ Ensure Brand Safety is enabled
- ✅ Ensure you comply with privacy legal requirements (GDPR/CCPA/GPP)
- ✅ Comply with app-ads.txt
- ✅ Implement proper error handling
- ✅ Test with the PIDs in Test PIDs and Configurations