---
title: "Search your assets and generations"
method: GET
path: "/v1/search"
tags: ["Search"]
---

# Search your assets and generations

`GET /v1/search`

## Request

- Base URL: `https://nanostudiopro.com/api`
- URL: `https://nanostudiopro.com/api/v1/search`
- Auth: HTTP bearer

Full-text + detection-aware search. The query (`q`) matches against image names, prompts, AI-generated narratives, and every detected item — clothing, poses, colors, textures, actions. Returns image-level matches scored by relevance. Set `include_regions=true` to get the matching bounding boxes overlaid by your client; coords are normalized 0..1 so they scale to any render size. Stale-while-revalidate caching: rapid repeat queries are instant. Read scope.

Four modes, one response shape: `mode=text` (default) is the full-text engine above. `mode=color` ranks your whole library by perceptual (OKLab) proximity to a hex color — add `q` to color-reorder a text search instead. `mode=similar` ranks by likeness to one anchor image (`similar_to` + `similar_type`), with `similar_mode` picking the flavor: `all` (blended, default), `items` (same detected stuff), `scene` (same lighting/mood/setting vocabulary), `story` (same narrative). `mode=browse` asks nothing and returns the catalogue newest-first. Color, similar, and browse results carry `score: 0` (they are ranked browses, not text relevance).

Text mode also reaches INSIDE indexed videos: an instant of footage that matches the words arrives as an ordinary item flagged `is_video`, whose `video_moment` names the exact `time_ms` — one clip is one card (its best-matching moment), ranked in by relevance alongside the photos. Filter to clips alone with `gen_type=video`.

The one endpoint **publishable keys** (`pk_sf_live_`) can call — embeddable in public pages and callable cross-origin from browsers. Every response (errors included) echoes the request's `Origin` back in `Access-Control-Allow-Origin` (`*` when absent) and sends `Vary: Origin`; preflights are answered by the `OPTIONS` operation below. A key's `search_scope` narrows results silently — see the notes on `type`, `project_id`, `image_size`, and `include_regions`. A key with an `origins` allowlist answers `403 forbidden` to browser requests from any other origin.

## Query parameters

- `mode` 'text' | 'color' | 'similar' | 'browse'
- `q` string
- `color` string
- `similar_to` string
- `similar_type` 'asset' | 'generation'
- `similar_mode` 'all' | 'items' | 'scene' | 'story'
- `limit` integer
- `offset` integer
- `type` 'asset' | 'generation'
- `project_id` string
- `gen_type` 'image' | 'video' | 'cutout' | 'collage' | 'uploaded'
- `style` 'photograph' | '3d-render' | 'illustration'
- `has_pose` boolean
- `has_colors` boolean
- `colors` string
- `scanned_after` string, date-time
- `scanned_before` string, date-time
- `sort` 'relevance' | 'date_desc' | 'date_asc'
- `aspect` string
- `include_regions` boolean
- `image_size` 'xs' | 'thumb' | 'preview' | 'large' | 'original': xs=100px · thumb=200px · preview=600px · large=1200px · original=raw upload (full-res)
- `embed_key` string

## Response `200`

Paginated search results.

- object
  - `items` object[]
    - `id` string: Asset or generation ID.
    - `type` 'asset' | 'generation'
    - `name` string, nullable
    - `image_url` string, nullable: Time-limited signed URL (HMAC). Expiry snaps to a ~6-hour signing window, so URLs stay byte-stable — and browser-cacheable — within a window. Re-issue by re-fetching the search.
    - `permanent_url` string, nullable: A link that never expires (`/api/v1/media/{type}/{id}?sig=…`): it redirects to a fresh signed file each time, takes `&size=` like `image_url`, and stops working when the image is deleted. Use it wherever the link is stored, such as a quote or a page. Absent on video moment cards.
    - `image_width` integer, nullable
    - `image_height` integer, nullable
    - `aspect_ratio` string, nullable
    - `category` string, nullable: Asset category (model, pose, background, etc.). Null for generations.
    - `project_id` string, nullable
    - `score` number: Text relevance, 0..1. Always 0 in `color` and `similar` modes (ranked browses — order carries the ranking).
    - `scanned_at` string, date-time
    - `narrative` string, nullable: AI-generated description of the image; present when available. Publishable keys only get it when the key's `search_scope.include_narrative` is true — omitted otherwise.
    - `gen_type` 'image' | 'video' | 'cutout' | 'collage' | 'uploaded': What kind of generation this is. Present on generation hits only — `category` is null for generations, so this is the kind signal.
    - `is_video` boolean: Present (true) only on video hits — a clip's poster row or a matched moment. On a moment, `image_url` is the matched frame and `video_moment` says where in the clip it lives.
    - `video_moment` object: Present only on video hits from `text` mode: the exact instant inside an indexed clip that matched the query. First-page only, ranked in by relevance alongside photos; one clip appears as one item (its best-matching moment).
      - `moment_id` string
      - `time_ms` integer: Milliseconds from the start of the clip.
      - `moments_in_video` integer: How many moments in this clip matched the query.
      - `duration_sec` number, nullable
      - `shot_size` string, nullable
      - `camera_motion` string, nullable
    - `regions` object[]: Present only when `include_regions=true`.
      - `label` string
      - `category` string, nullable
      - `texture` string, nullable
      - `colors` string[]: Hex strings, dominant first.
      - `bbox` object: Normalized 0..1 image-space coordinates. Multiply by your render width/height.
        - `x` number
        - `y` number
        - `w` number
        - `h` number
  - `total` integer: Total matches across all pages.
  - `next_offset` integer, nullable: Pass to the next request as `offset`. `null` when there are no further pages.
  - `did_you_mean` string[]: Up to 3 closest names (trigram similarity). Present whenever suggestions exist.
  - `videos_total` integer: Present when the video branch matched: how many video moments matched across the library.
  - `videos_shown` integer: Present when the video branch matched: how many clip cards joined this page (max 4, first page only). `next_offset` counts photo rows alone.

## Other responses

- `400`: Missing or invalid parameter (`q`, `mode`, `color`, `similar_to`, ...)
- `401`: Missing or invalid token
- `403`: This key is not allowed from this origin.
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/nanostudiopro/apis/nano-studio-pro-api.md) · [All operations](https://skmtc.dev/nanostudiopro/apis/nano-studio-pro-api/llms.txt) · [OpenAPI document](https://skmtc.dev/nanostudiopro/apis/nano-studio-pro-api/revisions/b454211e598e?raw)
