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

# Lists

> Saved sets of entities you come back to.

A list is a durable, named set of entities of one type — a competitor set, a creator shortlist, a
product watchlist. Lists exist so that a set you care about has an identity of its own, instead of
being reassembled from a spreadsheet every time.

## Creating one

```bash theme={null}
curl -X POST https://api.unif.dev/v1/lists \
  -H "Authorization: Bearer $UNIF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Competitor shops — US skincare",
    "entity_type": "shop",
    "item_ids": [
      "shp_01k3m9x7v2q8r4t6y0b1n5d7fa",
      "shp_01k3m9x7v2q8r4t6y0b1n5d7fb"
    ]
  }'
```

A list holds one `entity_type`. Mixing shops and creators in one set makes the metrics
incomparable, so the API does not allow it — create two lists and relate them on your side.

## Reading it back

`GET /lists/{id}/items` returns the **full record** for every member, measured over whatever
period you ask for. This is the main reason to use lists: refreshing a tracked set is one call.

```bash theme={null}
curl "https://api.unif.dev/v1/lists/lst_01k3m9x7v2q8r4t6y0b1n5d7fa/items?period=last_7d&currency=USD" \
  -H "Authorization: Bearer $UNIF_API_KEY"
```

Change `period` and you get the same set measured over a different window — which makes
week-over-week comparison a matter of two calls rather than a stored history you have to maintain.

```python theme={null}
this_week = get_items(list_id, period="last_7d")
baseline  = get_items(list_id, period="last_30d")
```

## Adding and removing

```bash theme={null}
curl -X POST https://api.unif.dev/v1/lists/lst_01k3m9x7v2q8r4t6y0b1n5d7fa/items \
  -H "Authorization: Bearer $UNIF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "item_ids": ["shp_01k3m9x7v2q8r4t6y0b1n5d7fc"] }'
```

Adds are idempotent — IDs already present are ignored, so you can push your full set each sync
without diffing first. A list holds up to 1,000 items.

<Tip>
  Build a list straight from a search: page through the results, collect the `id` values, and post
  them. That turns a one-off query into a set you can track.
</Tip>

```python theme={null}
ids = [row["id"] for row in iter_shops(search_body)][:1000]

requests.post(
    f"https://api.unif.dev/v1/lists/{list_id}/items",
    headers=auth, json={"item_ids": ids}, timeout=30,
)
```

## What lists unlock

<CardGroup cols={2}>
  <Card title="Trackers" icon="bell" href="/docs/workflows/trackers">
    Point a tracker at a list and Unif re-checks every member on a schedule, posting a webhook
    when one crosses a threshold.
  </Card>

  <Card title="Cheap refreshes" icon="arrows-rotate" href="/docs/workflows/enrich">
    Re-reading a list by ID skips resolution entirely, so a daily refresh costs less than
    re-enriching from URLs.
  </Card>
</CardGroup>

## Lists are not queries

A list is a fixed set of IDs. It does not re-run a search, and it does not gain members when new
entities start matching the criteria that produced it.

When you want "everything currently matching these filters", that is a
[tracker with a `search` source](/docs/workflows/trackers#tracking-a-search) — it re-runs the
query each cycle and tells you what entered or left.
