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

# Channels and markets

> How Unif scopes a query, and what sits behind a channel.

Two parameters scope almost every request: `channel` — where selling happens — and `market` —
which storefront of that channel you mean.

```json theme={null}
{ "channel": "tiktok_shop", "market": "US" }
```

## Channels

A channel is a commerce surface. `tiktok_shop` is the first; more follow the same model.

`channel` is optional. Omit it and the query spans every channel your workspace has enabled.
Every returned object names its own `channel`, so results stay distinguishable:

```json theme={null}
{ "id": "shp_01k3…", "channel": "tiktok_shop", "market": "US", "name": "GlowLab" }
```

<Note>
  A query only spans channels that are **enabled** for your workspace. Turning on a new channel is
  a deliberate change in the dashboard — enabling one never silently widens queries already
  running in production.
</Note>

## Channels and sources

A channel is the surface you are asking about. Behind it sits a **cascade of sources** — the
providers Unif queries in cost order until one returns a verified result. See
[the waterfall](/docs/concepts/waterfall).

The roster is published, not hidden. The first TikTok Shop source is
[Kalodata](https://www.kalodata.com).

<Info>
  **\[PLACEHOLDER — full source roster]** The complete per-channel list belongs here once it is
  settled.
</Info>

What the API does **not** tell you is which source answered a given row. That is not secrecy — it
is a stability guarantee. Source order, membership and per-field strength are re-tuned over time,
and if your code branched on them, every re-tune would be a breaking change you did not ask for.

<CardGroup cols={2}>
  <Card title="Published" icon="list">
    Which sources are in the roster for a channel, and what the cascade achieves — reported per
    field by [`GET /coverage`](/docs/concepts/coverage).
  </Card>

  <Card title="Not in the response" icon="shuffle">
    Which step answered this particular row. Build against coverage and `meta.refreshed_at`, which
    are stable, rather than source identity, which is not.
  </Card>
</CardGroup>

<Tip>
  If a number needs provenance for a compliance or audit workflow, cite `meta.refreshed_at` on the
  record together with `typical_lag_hours` from `GET /coverage`. Those describe the data you were
  actually given, which is what an auditor is asking about.
</Tip>

## Markets

A market is an ISO 3166-1 alpha-2 code identifying a channel's storefront in one country. At
preview, TikTok Shop covers 16:

`US` · `GB` · `IE` · `ES` · `FR` · `DE` · `IT` · `ID` · `TH` · `VN` · `MY` · `PH` · `SG` · `JP` ·
`BR` · `MX`

Markets matter more than they look. Each has its own catalogue, its own creator population, its
own native currency and its own time zone for day boundaries — so the "same" shop in `US` and `GB`
is two entities with two IDs.

## Read entitlements, do not hard-code them

Markets get added. `GET /v1/channels` is the source of truth:

```bash theme={null}
curl https://api.unif.dev/v1/channels \
  -H "Authorization: Bearer $UNIF_API_KEY"
```

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "channel": "tiktok_shop",
      "name": "TikTok Shop",
      "entity_types": ["shop", "product", "creator", "video", "livestream", "category"],
      "markets": [
        {
          "market": "US",
          "currency": "USD",
          "timezone": "America/Los_Angeles",
          "earliest_period": "2024-01-01",
          "granularities": ["day", "week", "month"]
        },
        {
          "market": "ID",
          "currency": "IDR",
          "timezone": "Asia/Jakarta",
          "earliest_period": "2024-07-01",
          "granularities": ["week", "month"]
        }
      ]
    }
  ]
}
```

Three fields on each market are worth wiring into your own validation:

* **`granularities`** — the period buckets the market supports natively. `ID` above has no daily
  bucket, so daily requests come back with `period.adjusted: true`. See
  [Periods and currency](/docs/concepts/periods).
* **`earliest_period`** — the oldest date with data. Requests before it return an empty series,
  not an error.
* **`timezone`** — the local zone that defines day boundaries for this market.

<Warning>
  Requesting a market your workspace is not entitled to returns `coverage_error`, not an empty
  result. That distinction is deliberate: an empty list is a finding, an entitlement problem is a
  bug.
</Warning>
