> ## Documentation Index
> Fetch the complete documentation index at: https://docs.upriver.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Score audience fit

> Match an audience persona to advertisers and inspect the score, audience characteristics, and supporting evidence.

Use audience matching to assess how well a supplied persona fits the advertisers
in your catalog. Each match includes a fit score from 0 to 1, reasons, and the
audience characteristics behind the match.

For campaign planning, you can prepare a catalog containing your brand, submit
each candidate persona separately, and collect the returned scores to help
prioritize audiences. For sponsorship selection, submit a persona against a
catalog of advertisers to find relevant brands.

> [!NOTE]
>
> Audience matching is in beta. Scores describe relevance under the reported
> scoring strategy; they are not purchase probabilities or proof of buying intent.
> The API accepts one persona per request, not a contact list, and does not
> generate new personas or sync audiences to ad platforms.

## Before you start

You need an Upriver API key with access and quota for catalog intake and matching.
Use the same API user for catalog setup and matching: catalogs belong to the user
who creates them. The examples use `curl` and `jq`.

Set `UPRIVER_API_KEY` in your environment. If you already have a prepared catalog,
set `CATALOG_ID` to its ID and skip to [Match a persona](#match-a-persona).

## Prepare your advertiser catalog

### 1. Create a catalog

A catalog is the set of advertisers you want to match against. Save the returned
`catalog_id` for subsequent requests.

```bash theme={null}
export CATALOG_ID="$(curl --fail-with-body --silent --show-error \
  --request POST 'https://api.upriver.ai/v1/catalogs' \
  --header "X-API-Key: $UPRIVER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{"name":"Campaign audience research"}' | jq -er '.catalog_id')"
```

### 2. Add advertisers

Choose a stable `ref` for each advertiser. The match response uses this identifier
to connect results back to your own records. You can submit up to 200 entries per
request; inspect each entry's status in the response before continuing.

```bash theme={null}
curl --fail-with-body --silent --show-error \
  --request POST "https://api.upriver.ai/v1/catalogs/$CATALOG_ID/entries" \
  --header "X-API-Key: $UPRIVER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{"entries":[{"ref":"running-brand","brand":{"url":"https://www.nike.com"}}]}'
```

### 3. Research the entries

Hydration researches the advertisers and prepares the characteristics used for
matching. Adding an entry alone does not mean it is ready to match.

```bash theme={null}
curl --fail-with-body --silent --show-error \
  --request POST "https://api.upriver.ai/v1/catalogs/$CATALOG_ID/hydrate" \
  --header "X-API-Key: $UPRIVER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{"refs":["running-brand"]}'
```

A `202` response means the request was accepted. Check `selected` and `skipped`;
`mode` indicates whether processing started immediately (`inline`) or was queued
(`queued`). Check progress periodically:

```bash theme={null}
curl --fail-with-body --silent --show-error \
  "https://api.upriver.ai/v1/catalogs/$CATALOG_ID/hydrate" \
  --header "X-API-Key: $UPRIVER_API_KEY"
```

Wait for `succeeded` before expecting the prepared entry to participate fully.
`pending` and `running` mean work is unfinished; `partial` means only some research
is available. For `failed` or `stale`, retry hydration for the affected ref.
To retry a `partial` entry, include its ref and `"force": true` in the hydration
request. Avoid repeatedly restarting entries that are still running.

## Match a persona

Send a description of the audience's stated needs, interests, and situation.
Include relevant constraints and objections. Use information you actually have;
do not invent individual preferences from a broad segment label.

```bash theme={null}
curl --fail-with-body --silent --show-error \
  --request POST "https://api.upriver.ai/v1/catalogs/$CATALOG_ID/match" \
  --header "X-API-Key: $UPRIVER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "persona": {
      "summary": "Recreational runners training for their first half marathon who want comfortable shoes for longer runs."
    },
    "settings": {"limit": 10}
  }'
```

Provide exactly one of `persona.summary` (free text), `persona.profile`
(structured fields), or `persona.facets` (characteristics you have already typed).
See the [Match Persona reference](https://docs.upriver.ai/audience/match-persona) for all request fields,
settings, and the complete response schema.

## Read the score and audience breakdown

A **facet** is a short statement about a persona or an advertiser's audience,
such as a need, interest, or situation. The response separates the supplied
persona's interpretation from the matches it supports.

| Field | How to use it |
| - | - |
| `interpretation.facets` | Inspect the needs, interests, situations, identities, triggers, barriers, or intent read from your persona. Each has text and confidence. |
| `matches[].ref` | Join the result to your catalog entry. |
| `matches[].score` | Read fit from 0 to 1 under `strategy_version`; higher means stronger assessed fit. |
| `matches[].reasons` | Explain why the advertiser fits. |
| `matches[].matched_offer` | When present, inspect the selected product or service, its evidence, and relevance score. |
| `matches[].contributions` | Inspect the related persona and product facets and supporting evidence. When an offer is selected, these explain how it was found; they do not add up to its relevance score. |
| `settings_applied` and `strategy_version` | Keep these with your scores so you can identify changes in scoring conditions. |
| `completeness` and `relevance_status` | Check whether results are complete and whether relevance assessment ran or used a fallback. |

This illustrative excerpt shows the fields to use for a score column and its
explanation. Values are examples, not measured results; other response fields
are omitted.

```json theme={null}
{
  "interpretation": {
    "facets": [
      {"kind":"need","text":"Comfortable shoes for longer runs","confidence":0.9},
      {"kind":"situation","text":"Training for a first half marathon","confidence":0.9}
    ]
  },
  "matches": [
    {
      "ref":"running-brand",
      "score":0.82,
      "reasons":["Offers running shoes relevant to the stated training needs."]
    }
  ]
}
```

### Compare candidate audiences

Submit each persona against the same prepared catalog with the same settings.
Store its description, advertiser ref, score, reasons, strategy, and response
status together. In a spreadsheet or a workflow such as Clay, these can become
columns you use to review and prioritize candidate segments. This requires your
own integration; it is not a built-in Clay audience-list scoring operation.

Check status before comparing results. `completeness` can be `complete`, `truncated`,
or `unavailable`. A fallback or a different `strategy_version` can change what a
score means, so do not silently mix these results into one ranking.

The endpoint returns qualifying matches rather than a score for every catalog
entry. A missing advertiser or empty `matches` array is **not a zero score**:
check research readiness, response status, exclusions, and score thresholds.
Validate any filtering threshold on examples representative of your use case.

## Get facets without running a match

To inspect an advertiser's stored characteristics and evidence, request:

```bash theme={null}
curl --fail-with-body --silent --show-error \
  "https://api.upriver.ai/v1/catalogs/$CATALOG_ID/entries/running-brand/facets" \
  --header "X-API-Key: $UPRIVER_API_KEY"
```

This returns `facets` with `kind`, `text`, `confidence`, `evidence_grade`,
`sensitive`, brand/product `scope`, and supporting `evidence`. It reads existing
research; it does not create new research or score a persona. The default limit
is 200, with a maximum of 500. Check `truncated` to see whether more facets exist
than the response contains.

`POST /v1/audience_dimensions` serves a different starting point: a brand URL
becomes researched audience facts categorized as behaviors, motivations,
lifestyles, concerns, or interests. Matching facets can draw on that research,
but also on brand, product, and persona research. Use
[Personas](/api-reference/audience/personas-v3.md) when you need to discover audience
personas before assessing their fit. There is no separate endpoint that only
interprets a supplied persona; its facets come back in the match response.

## Limits and troubleshooting

Matching is limited to 60 requests per 60-second window per API user, in addition
to account quota. The default result limit is 10; you can request 1–50 advertisers.

| Result | What to check |
| - | - |
| `200` with no matches | Check hydration status, `completeness`, and `relevance_status`, then the persona, exclusions, and thresholds. Do not record an absent match as zero. |
| `400` | Check the validation message and request identifiers. |
| `401` / `403` | Check the API key and account access. |
| `404` | Check the catalog ID and use credentials for its owning API user. |
| `422` | Supply exactly one persona format and valid field values. Simplify an uninterpretable persona description. |
| `429` | Back off before retrying and check account limits. |
| `500` / `503` / `504` | Retry with backoff; contact support if failures persist. |

For example, an unknown or inaccessible catalog returns `404` with:

```json theme={null}
{"detail":"Catalog not found."}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.