> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.spyglass.so/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.spyglass.so/_mcp/server.

# Migrating to v2

> What changed from v1, endpoint by endpoint.

v2 lives at `https://api.spyglass.so/v2`. Same keys, same credits, same
error envelope. v1 still responds at `/api/v1`, but these docs cover v2 only.

## Endpoint map

| v1                                           | v2                                         |
| -------------------------------------------- | ------------------------------------------ |
| `POST /brands/search` with `type: "BRAND"`   | `POST /brands`                             |
| `POST /brands/search` with `type: "CREATOR"` | `POST /creators`                           |
| `GET /brands/{id}`                           | `GET /account/{id}`                        |
| `POST /brands/{id}/media`                    | `POST /media` with `accountId` in the body |
| `POST /brands/index`                         | `POST /account/index`                      |
| `GET /brands/{id}/index-status`              | `GET /account/{id}/index-status`           |
| `GET /brands/{id}/insights`                  | `GET /account/{id}/insights`               |
| `GET /brands/{id}/campaigns`                 | `GET /account/{id}/campaigns`              |
| `GET /brands/{id}/collabs`                   | `GET /account/{id}/collabs`                |
| `GET /brands/{id}/landing-pages`             | `GET /account/{id}/landing-pages`          |
| `POST /comments`                             | `POST /comments` (unchanged)               |
| —                                            | `GET /taxonomies` (new)                    |

## Search

* **No more `mode` or `type`.** The endpoint decides brand or creator, and the
  inputs decide how to search. Precedence is `ids` > `igUsername` /
  `tiktokUsername` > `names` > `query`.
* **`brandNames` is now `names`. `brandIds` is now `ids`.**
* **Username search.** Pass `igUsername` or `tiktokUsername` to find accounts
  whose handle contains that text.
* **Different filters.** `/brands` doesn't accept `gender`, `ageRange`,
  `ethnicity` or `language`; `/creators` does. Both accept `location`. Sending them to
  `/brands` is a `400`.
* **Different output.** Every row has a `type`. Creators return profile fields
  (`gender`, `ageRange`, `ethnicity`, `location`, `language`,
  `tiktokEngagementRate`) and omit `facebookUsername`, `likes` and `category`.
* **`count` is now `limit`.** The request field that caps rows is `limit`, same
  as `/media`. Responses still report `count`, the number of rows returned.
* `sortBy` is no longer rejected when you search by `query`; it is ignored.

## Account lookup

`GET /account/{id}` returns either a brand or a creator. Branch on `type`.

## Media

* `/media` takes an optional `accountId` in the body. Omit it to search the whole
  corpus, in which case `query` is required.
* v1's `semanticSearchQuery` is `query` in v2 (same creative search semantics).
* **Filters are flat.** v1's `filters` object is gone: put `channel`, `format`,
  `status`, `sortBy`, `limit` and the rest at the top level of the body. The
  nested ranges are flattened too:

| v1 `filters.…`                    | v2                                 |
| --------------------------------- | ---------------------------------- |
| `dateRange.preset`                | `datePreset`                       |
| `dateRange.startDate` / `endDate` | `startDate` / `endDate`            |
| `daysRun.min` / `max`             | `minDaysRun` / `maxDaysRun`        |
| `viewCount.min` / `max`           | `minViewCount` / `maxViewCount`    |
| `landingPage.value` / `operator`  | `landingPage` / `landingPageMatch` |

* `userRequest` is no longer accepted.
* Unknown fields return `400 invalid_request`.
* `taxonomy` on `/media` and `/brands` takes path strings from
  `GET /taxonomies` (for example `Apparel > Footwear > Sneakers`). That
  lookup is free.