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

# Rank API

> Score and rank batches of ad creative before spending media budget.

`POST /api/v1/rank` scores and ranks a batch of creatives against one audience. It handles
image-only ranking, full ad packages with copy context, platform framing, saved or inline
audiences, and pairwise refinement for near ties.

<Info>
  This page covers behavior and interpretation. Field types, schemas, and response shapes live in
  the [Rank API reference](/api-reference/rank), generated from the OpenAPI 3.1 contract.
</Info>

## Choosing a goal

`goal` determines what dimension the assets are scored on. It defaults to `purchase-intent`,
which is the most developed head.

| Goal                                                 | Best for                  |
| ---------------------------------------------------- | ------------------------- |
| `purchase-intent` <Tooltip tip="Default">★</Tooltip> | Product ads, DTC creative |
| `click-through-rate`                                 | Ads optimized for clicks  |
| `conversion-potential`                               | Landing pages, lead gen   |
| `trustworthiness`                                    | Any ad — perceived trust  |
| `scroll-stopping`                                    | Feed-based social ads     |
| `engagement`                                         | Social content, community |
| `brand-recognition`                                  | Brand awareness           |
| `emotional-resonance`                                | Storytelling creative     |
| `open-rate`                                          | Email headers             |
| `logo-professionalism`                               | Logo evaluation           |
| `logo-versatility`                                   | Logo evaluation           |
| `product-page-conversion`                            | Product detail pages      |
| `landing-page-conversion`                            | Landing pages             |

<Warning>
  Scores from different goals are not comparable to each other, and scores are only comparable
  **within one batch**. Do not build a cross-batch leaderboard out of raw scores.
</Warning>

## Framing the asset

`asset_type` changes the system prompt so the simulated persona evaluates the asset in the right
mental context. It defaults to `Social Media Post`.

| Asset type                                             | Context            |
| ------------------------------------------------------ | ------------------ |
| `Social Media Post` <Tooltip tip="Default">★</Tooltip> | Feed-based content |
| `Advertisement`                                        | Paid ads           |
| `Product Photo`                                        | Product imagery    |
| `Email Header`                                         | Email marketing    |
| `Landing Page Hero`                                    | Hero sections      |
| `Logo`                                                 | Brand marks        |
| `Website Banner`                                       | Display ads        |
| `Illustration`                                         | Custom art         |

You can set `asset_type` at the top level and override it per asset.

Optionally add delivery context with `platform` (`meta`, `instagram`, `facebook`, `tiktok`,
`linkedin`, `google`), or `campaign_type` when `platform` is omitted.

## Copy context

Attaching `copy_context` to an asset switches it from image-only scoring to full ad package
scoring, and triggers an image-only ablation alongside it. The response gains
`image_only_score` and `copy_lift`.

Preferred keys are `pageName`, `adBody`, `adHeadline`, `adCaption`, and `adDescription`. Common
aliases are normalized automatically:

| Alias                         | Normalized to   |
| ----------------------------- | --------------- |
| `businessName`, `displayName` | `pageName`      |
| `primaryText`, `body`, `copy` | `adBody`        |
| `headline`                    | `adHeadline`    |
| `caption`                     | `adCaption`     |
| `description`                 | `adDescription` |

<Note>
  An asset with `copy_context` uses **two** scoring evaluations when the ablation succeeds — one
  for `full-ad-package`, one for `image-only`. Check `score_layer` on each ranked asset to see
  which layer produced the final score.
</Note>

## Batch uploads and refinement

Send up to 20 assets per request. Kettio evaluates up to 4 concurrently inside the request and
returns a single sorted `ranked` array.

By default (`refine_close_pairs: true`) Kettio checks successful assets for close pairs. When
two scores fall within the refinement threshold, a pairwise panel can run and adjust the final
order if it disagrees with the initial SSR ranking.

| Field                           | What it tells you                                                |
| ------------------------------- | ---------------------------------------------------------------- |
| `score_before_refine`           | The raw SSR score before any close-pair adjustment               |
| `panel_outcome`                 | Winner, loser, whether the order flipped, votes, and consistency |
| `summary.close_pair_refinement` | Whether refinement ran, how many pairs, and the raw results      |

Set `refine_close_pairs: false` when you want raw SSR-only ordering — for example, when you are
running a controlled experiment and need the SSR scores frozen.

<Tip>
  Refinement is automatic and threshold-driven. When you already know which specific matchups
  matter, use the [Pairwise API](/guides/pairwise-api) instead — it runs a full five-voter panel
  on exactly the pairs you name.
</Tip>

## Handling partial failures

A batch does not fail as a unit. Assets that could not be fetched or scored appear in `errors`,
and the rest still rank. Always read `summary.assets_failed` before acting on `ranked` — a
"winner" chosen from a batch where half the assets failed is not a winner.

```javascript theme={null}
const { ranked, errors, summary } = await rankResponse.json();

if (summary.assets_failed > 0) {
  console.warn(`${summary.assets_failed} assets failed`, errors);
}

const [winner] = ranked;
```

## Related

<CardGroup cols={2}>
  <Card title="Audiences" icon="users" href="/concepts/audiences">
    Inline vs saved audiences, and the demographics that shape the persona.
  </Card>

  <Card title="Limits and billing" icon="gauge" href="/concepts/limits-and-billing">
    Rate limits and credit accounting, both measured in scoring evaluations.
  </Card>
</CardGroup>
