# Recommendations

`allegro.recommendations` fetches ranked article recommendations for the current reader and reports clicks on them, so you can build a "Read Next" or "More Like This" module on your site.

## How ranking works[​](#how-ranking-works "Direct link to How ranking works")

Recommendations come from a ladder of strategies, tried in order until enough articles are found:

1. **Taste** — articles matched to the reader's own reading history.
2. **Similar** — articles similar to the one they're currently on.
3. **Trending** — articles currently getting the most engagement site-wide.

Articles the reader has already read are excluded at every rung. Each returned item's `strategy` field tells you which rung produced it.

## Basic usage[​](#basic-usage "Direct link to Basic usage")

```js
window.allegro.push(async function (allegro) {
    const { items } = await allegro.recommendations.get({ limit: 6 });

    const list = document.querySelector('#recommendations');

    items.forEach(function (item) {
        const link = document.createElement('a');
        link.href = item.pagePath;
        link.textContent = item.title;
        link.addEventListener('click', function () {
            allegro.recommendations.trackClick(item).catch(function (error) {
                console.error(error);
            });
        });

        list.appendChild(link);
    });
});

```

Call `allegro.recommendations.get()` to fetch a set, render the items, and call `allegro.recommendations.trackClick(item)` when the reader clicks one — pass the same `item` object `get()` gave you, not a copy. See [Click attribution](#click-attribution) for why that matters.

`trackClick()` returns a promise that rejects if the click report fails — a rate limit or a network error mid-navigation are both plausible — so always attach a `.catch()`. An unhandled rejection surfaces as an SDK error in your own site's monitoring, not ours.

## Options[​](#options "Direct link to Options")

`get()` takes an options object; every field is optional:

| Option       | Type     | Default                     | Description                                               |
| ------------ | -------- | --------------------------- | --------------------------------------------------------- |
| `limit`      | `number` | `6` upstream                | How many articles to ask for. 24 is the maximum.          |
| `path`       | `string` | `window.location.pathname`  | Article to anchor the "similar" strategy on.              |
| `maxAgeDays` | `number` | `0` (no cap)                | Excludes articles published more than this many days ago. |
| `device`     | `string` | the SDK's own device cookie | Reader's device id.                                       |

Override `path` when you're rendering recommendations for an article other than the one currently loaded — a widget on an article's own page, for example, that recommends off a different anchor article. Override `device` only if you're managing device identity yourself outside the SDK's cookie; otherwise leave it to the default so click attribution and taste ranking stay tied to the same reader.

## Sorting and comparing scores[​](#sorting-and-comparing-scores "Direct link to Sorting and comparing scores")

Each item carries a `score`, but it's only comparable **within** a strategy, not across strategies. Taste and similar scores are a similarity value times a recency decay; trending scores are engaged hours. Sorting a mixed list of items by `score` produces a meaningless order — mixing a similarity fraction against a count of hours. If you want a single ordering, keep the order `get()` returned, which is already ranked by the ladder above.

## Click attribution[​](#click-attribution "Direct link to Click attribution")

`trackClick(item)` needs to know which request served `item` so the click can be attributed to the right recommendation set. Passing the exact item object `get()` returned always works — the SDK tracks it by identity.

If you pass a copy instead — the result of a JSON round-trip, or an item that went through a state library that serializes it — the SDK falls back to matching by `pagePath` across the 10 most recent `get()` calls on the page. That works as long as the path is unique among those calls, but **throws** if it isn't: two recommendation widgets on the same page could otherwise recommend the same article and cross-attribute a click between them, silently corrupting your click-through data.

Guidance

Pass the item you rendered. Don't reconstruct it from a path or id, and don't let it pass through `JSON.stringify`/`JSON.parse` or a store that serializes props before it reaches `trackClick()`.

## No recommendations available[​](#no-recommendations-available "Direct link to No recommendations available")

When no engine is configured for your tenant, or the engine is unreachable, `get()` doesn't throw — it resolves with an empty `items` array and a `null` `requestId`:

```js
const { requestId, items } = await allegro.recommendations.get();

if (requestId === null) {
    return;
}

```

Check for this and render nothing rather than an empty container, so a missing recommendation set doesn't leave a visible hole in your layout.

## Server-to-server[​](#server-to-server "Direct link to Server-to-server")

Everything above runs from the browser through the SDK. If you need recommendations from your own backend — a build step, a newsletter generator, a batch job — there's a token-authenticated equivalent under `/api/v1`:

| Endpoint                               | Scope                   | Equivalent to                          |
| -------------------------------------- | ----------------------- | -------------------------------------- |
| `GET /api/v1/recommendations`          | `recommendations:read`  | `allegro.recommendations.get()`        |
| `POST /api/v1/recommendations/click`   | `recommendations:write` | `allegro.recommendations.trackClick()` |
| `POST /api/v1/recommendations/preview` | `recommendations:read`  | No SDK equivalent — see below.         |

See [API Authentication](/developer/api/authentication.md) for how to create a key with these scopes, and the generated [REST API reference](/developer/api/token/token-recommendation-index.md) for full request and response schemas.

This API reports failures instead of degrading

The browser SDK answers an empty `items` array when the engine is unreachable — a reader can't act on the difference, and a dead widget is worse than an empty one. The token API takes the opposite position: `GET /recommendations` and `POST /recommendations/click` answer `502 Bad Gateway` instead, because a server-to-server caller can retry or alert on that, and silently swallowing the failure would just hide an outage from you.

### Preview[​](#preview "Direct link to Preview")

`POST /api/v1/recommendations/preview` has no SDK equivalent. It ranks recommendations against a hypothetical reading history you supply, rather than a real visitor's session — useful for testing how the ranking engine responds to a given history, or for building tooling around it. Send up to 50 `reads`, oldest first:

```bash
curl https://acme.allegrocdp.com/api/v1/recommendations/preview \
  -H "Authorization: Bearer <your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "reads": [
      { "path": "/news/first-article", "seconds": 45 },
      { "path": "/news/second-article", "seconds": 120 }
    ],
    "limit": 6
  }'

```

An empty `reads` array previews what a just-arrived visitor with no history would see. The response echoes back each read's contribution to the taste vector (`weight`, `share`, and whether the path was `found` in the catalog) alongside the `items` that history would produce.

Preview depends on the tenant's configured recommendation engine supporting it. A tenant with no engine configured gets `501 Not Implemented` rather than a crash or an empty response — check for that status before assuming the request failed for another reason.

## Related[​](#related "Direct link to Related")

* [`RecommendationsNamespace`](/developer/api-reference/interfaces/RecommendationsNamespace.md) — Full SDK method reference.
* [`RecommendationItem`](/developer/api-reference/interfaces/RecommendationItem.md) — Shape of one recommended article.
* [REST API reference](/developer/api/token/token-recommendation-index.md) — Full server-to-server endpoint documentation.
