Merino

The Merino component provides two clients for interacting with the Merino service:

  • CuratedRecommendationsClient — Fetches personalized content recommendations, powering features like Firefox’s New Tab page.

  • SuggestClient — Fetches search suggestions, powering features like Firefox’s address bar.

Prerequisites

Ensure that Viaduct is initialized during application startup, as it is used for making network requests.

The SuggestClient additionally requires the OHTTP channel to be configured before making requests:

import mozilla.appservices.viaduct.OhttpConfig
import mozilla.appservices.viaduct.configureOhttpChannel

configureOhttpChannel(
    channel = "merino",
    config = OhttpConfig(
        relayUrl = "https://ohttp-merino.mozilla.fastly-edge.com",
        gatewayHost = "ohttp-gateway-merino.services.mozilla.com",
    ),
)
// Configure the OHTTP channel before using SuggestClient
let config = OhttpConfig(
    relayUrl: "https://ohttp-merino.mozilla.fastly-edge.com",
    gatewayHost: "ohttp-gateway-merino.services.mozilla.com"
)
try configureOhttpChannel(channelId: "merino", config: config)

Async

Both clients are synchronous — calling them directly will block the current thread. Consumers should wrap calls in an async implementation.


Curated Recommendations Client

Fetches personalized content recommendations from the Merino Service. Merino Curated Recommendations API Docs

The API for the CuratedRecommendationsClient can be found in the Mozilla Rust components Kotlin API Reference and Swift API Reference.

Importing the Client

import mozilla.appservices.merino.CuratedRecommendationsClient
import mozilla.appservices.merino.CuratedRecommendationsRequest
import mozilla.appservices.merino.CuratedRecommendationsResponse
import mozilla.appservices.merino.CuratedRecommendationsApiException
import MozillaAppServices

Initializing the Curated Recommendations Client

The CuratedRecommendationsClient is initialized using a CuratedRecommendationsConfig object. This includes a required userAgentHeader and an optional baseHost. If baseHost is not provided, the client will default to the production host.

val config = CuratedRecommendationsConfig(
    baseHost = "https://merino.services.mozilla.com"
    userAgentHeader = "Mozilla/5.0"
)

val client = CuratedRecommendationsClient(config)
let config = CuratedRecommendationsConfig(
    baseHost: "https://merino.services.mozilla.com"
    userAgentHeader: "Mozilla/5.0"
)

let client = try CuratedRecommendationsClient(config: config)

Fetching Curated Recommendations

The getCuratedRecommendations() method fetches recommendations based on the provided request parameters.

val request = CuratedRecommendationsRequest(
    locale = Locale.EN_US,
    region = "US",
    count = 4,
    topics = listOf("business"),
    feeds = listOf("sections")
)

try {
    val response: CuratedRecommendationsResponse = client.getCuratedRecommendations(request)
    println("Received recommendations: $response")
} catch (e: CuratedRecommendationsError) {
    println("Error fetching recommendations: ${e.message}")
}
let request = CuratedRecommendationsRequest(
    locale: Locale.en-US,
    region: "US",
    count: 4,
    topics: ["business"],
    feeds: ["sections"]
)

do {
    let response = try client.getCuratedRecommendations(request: request)
    print("Received recommendations: \(response)")
} catch {
    print("Error fetching recommendations: \(error)")
}

Data Models

Curated Recommendations Request Model

The CuratedRecommendationsRequest model defines the parameters required to request curated recommendations.

Request Fields

Field

Type

Description

locale

string

The Firefox installed locale, e.g., en, en-US, de-DE. Determines the language of recommendations.

region

string (optional)

(Optional) The country-level region, e.g., US or IE. Helps return more relevant recommendations. If not provided, it is extracted from locale if it contains two parts (e.g., en-US).

count

integer (optional)

(Optional) The maximum number of recommendations to return. Defaults to 100.

topics

array<string> (optional)

(Optional) A list of preferred curated topics.

feeds

array<string> (optional)

(Optional) A list of additional data feeds. Accepted values: "need_to_know", "fakespot", and "sections".

sections

array<object> (optional)

(Optional) A list of section settings that the user follows or has blocked.

experimentName

string (optional)

(Optional) The Nimbus New Tab experiment name that the user is enrolled in. Used to run backend experiments independently of Firefox releases.

experimentBranch

string (optional)

(Optional) The branch name of the Nimbus experiment that the user is in.

enableInterestPicker

boolean (optional, default: false)

(Optional, defaults to false) If true, the API response will include an interestPicker object with sections for interest bubbles.

Curated Recommendations Response Model

The CuratedRecommendationsResponse model defines the response format containing recommendations.

Response Fields

Field

Type

Description

recommendedAt

integer

The timestamp (in milliseconds) indicating when the recommendations were generated.

data

array<object>

A list of curated recommendation items.

feeds

object (optional)

(Optional) A structured set of multiple curated recommendation lists.

interestPicker

object (optional)

(Optional) Returned if enableInterestPicker is true in the request. Specifies the display order (receivedFeedRank) and a list of sections (referenced by sectionId) for interest bubbles. The text in these bubbles should match the corresponding section title.

Error Handling

The Curated Recommendations component defines the following error hierarchy:

  • CuratedRecommendationsApiError: Base error

    • Network(reason: string): Network error while making a request.

    • Other(code: integer (optional), reason: string): Generic error containing an HTTP status code and message.

Handling Errors in Kotlin and Swift

fun fetchCuratedRecommendations() {
    try {
        val response = client.getCuratedRecommendations(request)
    } catch (e: CuratedRecommendationsError.Network) {
        // Log and retry after 5 minutes
        Log.w("Network error when fetching Curated Recommendations: ${e.reason}")
        scheduleRetry(300)
    } catch (e: CuratedRecommendationsError.Other) {
        when (e.code) {
            400 -> Log.e("Bad Request: ${e.reason}")
            422 -> Log.e("Validation Error: ${e.reason}")
            in 500..599 -> Log.e("Server Error: ${e.reason}")
            else -> Log.e("Unexpected Error: ${e.reason}")
        }
    }
}
func fetchCuratedRecommendations() {
    do {
        let response = try client.getCuratedRecommendations(request)
    } catch CuratedRecommendationsError.Network(let reason) {
        // Log and retry after 5 minutes
        print("Network error when fetching Curated Recommendations: \(reason)")
        scheduleRetry(seconds: 300)
    } catch CuratedRecommendationsError.Other(let code, let reason) {
        switch code {
        case 400:
            print("Bad Request: \(reason)")
        case 422:
            print("Validation Error: \(reason)")
        case 500...599:
            print("Server Error: \(reason)")
        default:
            print("Unexpected Error: \(reason)")
        }
    }
}

Suggest Client

Fetches search suggestions from the Merino suggest endpoint. Merino Suggest API Docs

The API for the SuggestClient can be found in the Mozilla Rust components Kotlin API Reference and Swift API Reference.

Importing the Client

import mozilla.appservices.merino.SuggestClient
import mozilla.appservices.merino.SuggestConfig
import mozilla.appservices.merino.SuggestOptions
import mozilla.appservices.merino.MerinoSuggestApiException
import MozillaAppServices

Initializing the Suggest Client

The SuggestClient is initialized using a SuggestConfig object with an optional baseHost. If not provided, it defaults to the production host.

val config = SuggestConfig(
    baseHost = null // defaults to https://merino.services.mozilla.com
)

val client = SuggestClient(config)
let config = SuggestConfig(baseHost: nil) // defaults to https://merino.services.mozilla.com

let client = try SuggestClient(config: config)

Fetching Suggestions

The getSuggestions() method returns a raw JSON string containing the Merino suggest response. Callers are responsible for deserializing the response.

val options = SuggestOptions(
    providers = listOf("wikipedia", "adm"),
    source = "urlbar",
    country = "US",
    region = "CA",
    city = "San Francisco",
    clientVariants = null,
    requestType = null,
    acceptLanguage = "en-US"
)

try {
    val json: String = client.getSuggestions(query = "firefox", options = options)
    // parse json as needed
} catch (e: MerinoSuggestApiException) {
    println("Error fetching suggestions: ${e.message}")
}
let options = SuggestOptions(
    providers: ["wikipedia", "adm"],
    source: "urlbar",
    country: "US",
    region: "CA",
    city: "San Francisco",
    clientVariants: nil,
    requestType: nil,
    acceptLanguage: "en-US"
)

do {
    let json: String = try client.getSuggestions(query: "firefox", options: options)
    // parse json as needed
} catch {
    print("Error fetching suggestions: \(error)")
}

Data Models

SuggestConfig

Field

Type

Description

baseHost

string (optional)

The base host for the Merino endpoint. Defaults to https://merino.services.mozilla.com.

SuggestOptions

All fields are optional — omitted fields are not sent to Merino.

Field

Type

Description

providers

array<string> (optional)

List of suggestion providers to query (e.g. ["wikipedia", "adm"]). An empty list is treated the same as omitting the field.

source

string (optional)

Identifier of which part of Firefox the request comes from (e.g. "urlbar", "newtab").

country

string (optional)

ISO 3166-1 country code (e.g. "US").

region

string (optional)

Subdivision code(s) (e.g. "CA").

city

string (optional)

City name (e.g. "San Francisco").

clientVariants

array<string> (optional)

List of active experiments or rollouts affecting the client’s Suggest experience. An empty list is treated the same as omitting the field.

requestType

string (optional)

For the AccuWeather provider: "location" for location completion or "weather" for weather suggestions. Defaults to weather if omitted.

acceptLanguage

string (optional)

The Accept-Language header value to forward to Merino (e.g. "en-US").

Response

getSuggestions() returns the raw JSON response body as a string. The response follows the Merino suggest API schema.

Error Handling

The Suggest component defines the following error hierarchy:

  • MerinoSuggestApiError: Base error

    • Network(reason: string): A network-level failure (e.g. no connectivity, OHTTP not configured).

    • Other(code: integer (optional), reason: string): An HTTP error or unexpected failure, with an optional status code.

Handling Errors in Kotlin and Swift

fun fetchSuggestions() {
    try {
        val json = client.getSuggestions(query = "firefox", options = options)
    } catch (e: MerinoSuggestApiException.Network) {
        // Log and retry
        Log.w("Network error when fetching suggestions: ${e.reason}")
    } catch (e: MerinoSuggestApiException.Other) {
        when (e.code) {
            400 -> Log.e("Bad Request: ${e.reason}")
            422 -> Log.e("Validation Error: ${e.reason}")
            in 500..599 -> Log.e("Server Error: ${e.reason}")
            else -> Log.e("Unexpected Error: ${e.reason}")
        }
    }
}
func fetchSuggestions() {
    do {
        let json = try client.getSuggestions(query: "firefox", options: options)
    } catch MerinoSuggestApiError.network(let reason) {
        // Log and retry
        print("Network error when fetching suggestions: \(reason)")
    } catch MerinoSuggestApiError.other(let code, let reason) {
        switch code {
        case 400:
            print("Bad Request: \(reason)")
        case 422:
            print("Validation Error: \(reason)")
        case 500...599:
            print("Server Error: \(reason)")
        default:
            print("Unexpected Error: \(reason)")
        }
    }
}