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

> ## Agent Instructions
> Fetch /llms.txt for the complete page index before exploring further. Every docs page is available as markdown by appending .md to its URL. Coding agents should start at /for-agents/coding-agents.md.

# Search accounts

> Find Instagram accounts by who they currently appear to be, with account filters and a related-posts scope. POST /v1/instagram/accounts/search.

`POST /v1/instagram/accounts/search` finds whole accounts. Send one `account` block with a semantic `query` about who the account is and structural `filters`. Add an optional `posts` scope that qualifies accounts through their publishing. This page is the whole endpoint: identity queries, filtering, the posts scope, ordering, and how to read what comes back.

<Note>
  To find **content** rather than people, use [Search posts](/docs/instagram/search/posts-search); it returns individual posts. A common workflow: discover content there, qualify its authors here.
</Note>

## Query the identity

`account.query` describes who you're looking for, for example `"sporty stay-at-home moms"`. Klint qualifies each account against its **current observed identity**: the public profile plus recurring or representative published content.

<Tabs>
  <Tab title="curl">
    ```bash theme={null}
    curl -X POST "https://api.tryklint.ai/v1/instagram/accounts/search" \
      -H "Authorization: Bearer $KLINT_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
      "account": {
        "query": "sporty stay-at-home moms",
        "filters": {
          "followers_count": {
            "gte": 5000,
            "lte": 50000
          }
        },
        "posts": {
          "minimum_matching_posts": 3,
          "filters": {
            "posted_at": {
              "gte": "2026-04-01T00:00:00Z",
              "lt": "2026-07-01T00:00:00Z"
            }
          }
        }
      },
      "limit": 10
    }'
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os, requests

    resp = requests.post(
        "https://api.tryklint.ai/v1/instagram/accounts/search",
        headers={"Authorization": f"Bearer {os.environ['KLINT_API_KEY']}"},
        json={
      "account": {
        "query": "sporty stay-at-home moms",
        "filters": {
          "followers_count": {
            "gte": 5000,
            "lte": 50000
          }
        },
        "posts": {
          "minimum_matching_posts": 3,
          "filters": {
            "posted_at": {
              "gte": "2026-04-01T00:00:00Z",
              "lt": "2026-07-01T00:00:00Z"
            }
          }
        }
      },
      "limit": 10
    },
    )
    results = resp.json()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const resp = await fetch("https://api.tryklint.ai/v1/instagram/accounts/search", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.KLINT_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        "account": {
          "query": "sporty stay-at-home moms",
          "filters": {
            "followers_count": {
              "gte": 5000,
              "lte": 50000
            }
          },
          "posts": {
            "minimum_matching_posts": 3,
            "filters": {
              "posted_at": {
                "gte": "2026-04-01T00:00:00Z",
                "lt": "2026-07-01T00:00:00Z"
              }
            }
          }
        },
        "limit": 10
      }),
    });
    const results = await resp.json();
    ```
  </Tab>
</Tabs>

<Accordion title="Full response">
  ```json theme={null}
  {
    "request_id": "req_example_03",
    "data": [
      {
        "id": "ig_account_01j2k9m4q7",
        "handle": "mayamoveswell",
        "display_name": "Maya | Family Wellness",
        "web_url": "https://www.instagram.com/mayamoveswell/",
        "avatar_url": "https://media.tryklint.ai/accounts/ig_account_01j2k9m4q7/avatar",
        "biography": "Sporty stay-at-home mom sharing practical kids gut-health and family-wellness tips. Spokane, WA.",
        "bio_links": [
          {
            "url": "https://mayamoveswell.example/resources",
            "title": "Free family wellness guides"
          }
        ],
        "account_type": "creator",
        "category_label": "Health/beauty",
        "declared_locations": [
          {
            "country_code": "US",
            "region_code": "US-WA",
            "locality": "Spokane"
          }
        ],
        "is_verified": false,
        "is_private": false,
        "followers_count": 24300,
        "following_count": 684,
        "posts_count": 418,
        "publishing_summary": "Shares practical family-wellness education through at-home fitness routines, simple meal preparation, and recurring explanations of children's gut health.",
        "publishing_languages": [
          "en"
        ],
        "niches": [
          "family_wellness",
          "gut_health",
          "home_fitness"
        ],
        "observed_at": "2026-07-15T15:40:00Z"
      }
    ],
    "has_more": false,
    "next_cursor": null
  }
  ```
</Accordion>

These rules decide who comes back:

* **Every material trait must qualify.** For "sporty stay-at-home moms," an account must qualify as sporty *and* as a stay-at-home mom. Strength on one trait never compensates for absence of another.
* **One direct self-description is enough for what it states.** A biography saying "stay-at-home mom" establishes that trait without repetition.
* **Behavioral traits need recurrence.** An account is "sporty" through recurring fitness content, not one gym post. A single incidental post never redefines an account.
* **Evidence can live anywhere.** Relationship language in a caption ("my kids") counts like biography text. You don't predict where an account disclosed something; Klint looks across profile and content.
* **Current contradictions block; missing history doesn't.** Klint qualifies against what it observed. Known current evidence that contradicts a trait prevents qualification; incomplete coverage alone doesn't.

### Writing a query that qualifies

Identity queries are stricter than content queries: every material trait must independently qualify. Ask for the one or two traits your workflow actually needs.

| Query                                         | What must be true                                                                                                                     |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `"sporty stay-at-home moms"`                  | Recurring fitness/active content (behavioral) *and* affirmative stay-at-home-mom evidence (stated in profile or authored content)     |
| `"veterinarians who post grooming tutorials"` | Affirmative evidence of being a veterinarian; recurring vet-adjacent content alone establishes a topic, not a profession              |
| `"micro creators in Spokane"`                 | Don't. Put size in `followers_count` and place in `declared_locations` filters; keep the query for identity that needs interpretation |

Two habits improve results:

* **Move hard constraints into filters.** Follower ranges, languages, account type, and locations are exact, so filters on them are exact.
* **Don't stack rare traits casually.** `"left-handed vegan triathlete dads"` mostly proves that what Klint has observed can't evidence four independent traits at once.

## Filter the accounts

Filters are hard constraints: Klint returns an account only if it satisfies **every** filter. Every filter value is an **operator object**; there is no bare-value shorthand. `"account_type": "creator"` is rejected; `"account_type": { "in": ["creator"] }` is correct.

### Operators

| Operator                    | Applies to        | Meaning                                                   | Example                                                    |
| --------------------------- | ----------------- | --------------------------------------------------------- | ---------------------------------------------------------- |
| `in`                        | singular fields   | The field's one value is any of the supplied alternatives | `"account_type": { "in": ["creator", "business"] }`        |
| `contains_any`              | collection fields | The collection contains at least one supplied value       | `"niches": { "contains_any": ["pets"] }`                   |
| `contains_all`              | collection fields | The collection contains every supplied value              | `"publishing_languages": { "contains_all": ["en", "es"] }` |
| `gt` / `gte` / `lt` / `lte` | ordered scalars   | The value satisfies the numeric bounds                    | `"followers_count": { "gte": 10000, "lte": 50000 }`        |
| direct boolean              | boolean fields    | The observed value equals the supplied boolean            | `"is_verified": true`                                      |

Rules that follow from the shapes:

* A field takes exactly one operator. `contains_any` and `contains_all` never combine on the same field.
* Membership arrays are non-empty and hold unique values.
* Ranges need at least one bound and allow at most one lower and one upper bound. Klint rejects contradictory bounds.

<Info>
  **Different fields AND together.** No cross-field OR, no negation, no missing-value operator, no expression language. The grammar is deliberately closed and positive-only.
</Info>

<Info>
  **`null` never matches.** A missing or hidden value satisfies no filter: no range, no membership, no boolean in either direction. "Unknown" is not expressible in the launch grammar.
</Info>

Values normalize the same way as post filters: languages use BCP 47 tags with basic range matching (`"en"` matches `en-US`), and classification IDs (`niches`) are exact catalog IDs, never display labels. Klint rejects an unrecognized ID with suggestions rather than fuzzy-resolving it.

### Account filter fields

| Field                  | Shape                           | Matches when                                                                  |
| ---------------------- | ------------------------------- | ----------------------------------------------------------------------------- |
| `id`                   | `in`                            | The account's Klint ID is one of the supplied IDs                             |
| `handle`               | `in`                            | The current bare handle is one of the supplied handles                        |
| `followers_count`      | range                           | The current observed follower count is within bounds                          |
| `is_verified`          | boolean                         | Verification state was observed and equals the value                          |
| `account_type`         | `in`                            | The directly established type is one of `personal`, `creator`, `business`     |
| `declared_locations`   | `contains_any`                  | One declared location matches **every** component of one supplied alternative |
| `publishing_languages` | `contains_any` / `contains_all` | The account's publishing languages contain any / all requested tags           |
| `niches`               | `contains_any` / `contains_all` | The account's niches contain any / all requested IDs                          |

Each `declared_locations` alternative is an object with a required `country_code` (ISO 3166-1) and optional `region_code` and `locality`. All supplied components must match the **same** declared location. You cannot combine one location's country with another's city.

<Warning>
  Locality matching may launch restricted to ISO country and region codes, with `locality` as response context only. Canonical normalization (`St. Louis` vs `Saint Louis`) is unresolved design.
</Warning>

Not filterable at launch: `following_count`, `posts_count`, `category_label`, biography text, privacy state (Klint never returns private accounts), growth, or any audience/demographic inference.

### The posts scope

`account.posts` qualifies accounts through their publishing: `minimum_matching_posts` sets how many **distinct** posts must each satisfy the complete `account.posts.filters` predicate. Those filters use the [post filter fields](/docs/instagram/search/posts-search#post-filter-fields).

```json theme={null}
{
  "account": {
    "filters": {
      "followers_count": {
        "gte": 5000,
        "lte": 50000
      },
      "niches": {
        "contains_any": ["pets"]
      }
    },
    "posts": {
      "minimum_matching_posts": 1,
      "filters": {
        "posted_at": {
          "gte": "2026-05-01T00:00:00Z",
          "lt": "2026-07-01T00:00:00Z"
        },
        "views": {
          "gte": 200000
        }
      }
    }
  },
  "sort": {
    "by": "followers_count",
    "order": "desc"
  },
  "limit": 10
}
```

<Accordion title="Full response">
  ```json theme={null}
  {
    "request_id": "req_example_04",
    "data": [
      {
        "id": "ig_account_2j1x",
        "handle": "dogmomdaily",
        "display_name": "Dog Mom Daily",
        "web_url": "https://www.instagram.com/dogmomdaily/",
        "avatar_url": "https://media.tryklint.ai/accounts/ig_account_2j1x/avatar",
        "biography": "Golden retriever mom sharing real-life grooming and shedding hacks 🐾",
        "bio_links": [
          {
            "url": "https://dogmomdaily.example/favorites",
            "title": "Our favorite grooming tools"
          }
        ],
        "account_type": "creator",
        "category_label": "Blogger",
        "declared_locations": [],
        "is_verified": false,
        "is_private": false,
        "followers_count": 21400,
        "following_count": 512,
        "posts_count": 267,
        "publishing_summary": "Documents at-home dog care through grooming-product demonstrations, shedding-management routines, and day-in-the-life content with a golden retriever.",
        "publishing_languages": [
          "en"
        ],
        "niches": [
          "pets",
          "dog_care"
        ],
        "observed_at": "2026-07-15T09:20:00Z"
      }
    ],
    "has_more": false,
    "next_cursor": null
  }
  ```
</Accordion>

Rules worth internalizing:

* **The whole predicate must hold on one post.** An account with one post carrying the right hashtags and a *different* post clearing the views bar does not qualify; facts from different posts never combine.
* **A carousel counts once**, no matter how many media items it contains.
* The default minimum is `1`; the provisional launch maximum is `3`. <Tooltip tip="A 5-post benchmark is pending before the OpenAPI freeze — this bound may rise.">Why 3?</Tooltip>

<Note>
  Whether `account.posts` counts only posts the account authored, or also posts where it accepted Instagram Collab coauthorship, is an open contract decision.
</Note>

## What gets rejected

A **non-matching** filter is a valid request that returns `data: []`. An **invalid** filter never executes; it returns a `422 validation-failed` problem object that names the exact `pointer`, `reason`, and correction.

The clearest case is a classification ID that isn't in the catalog. Klint never fuzzy-resolves it to a nearby ID and never lets it evaluate silently to an empty result. It rejects with reason `INVALID` and, when close IDs exist, a `candidates` list to correct from:

```json theme={null}
{ "account": { "filters": { "niches": { "contains_any": ["dog_grooming_deluxe"] } } } }
```

```json theme={null}
{
  "type": "https://api.tryklint.ai/problems/validation-failed",
  "status": 422,
  "request_id": "req_example_47",
  "detail": "One request field needs correction.",
  "errors": [
    {
      "pointer": "#/account/filters/niches/contains_any/0",
      "reason": "INVALID",
      "detail": "Unknown niche ID 'dog_grooming_deluxe'. Classification values are exact catalog IDs.",
      "candidates": [
        {
          "id": "dog_care",
          "label": "Dog care"
        },
        {
          "id": "pets",
          "label": "Pets"
        }
      ]
    }
  ]
}
```

The same grammar rejects, with `422 validation-failed`:

* **Bare values**: `"account_type": "creator"` instead of `{ "in": ["creator"] }` (reason `INVALID`).
* **Unknown fields**: `following_count` or `category_label` are not filterable keys (reason `UNKNOWN`).
* **Contradictory ranges**: `"followers_count": { "gte": 50000, "lte": 10000 }` (reason `INVALID`).
* **Negation attempts**: there is no `not_in`, `exclude`, or `none` (reason `UNKNOWN`).

[Search posts](/docs/instagram/search/posts-search#what-gets-rejected) shows each of these with its full response body.

## Ordering and pagination

### Ordering

A query-bearing search returns relevance order; Klint rejects `sort` sent with a `query` (`422`). A filters-only account search accepts an optional `sort` object, `{ "by": "followers_count", "order": "desc" }`, over a closed list of stable scalar fields. A stable resource ID is the final tie-breaker. Ranking a relevance window by a metric would misrepresent a retrieval window as the whole of Instagram. Metric ordering therefore requires a structural (filters-only) population whose eligibility is exact.

### Pagination

An initial search pins an immutable result sequence: Klint freezes the request, a snapshot of what it tracks, and the ranking together. Each response returns one page plus an opaque cursor. Continuation sends only that cursor plus an optional same-or-lower `limit`. The rules:

* **The sequence never recomputes.** An account whose identity changes after your search never shifts mid-pagination. Run a new search to see newer state.
* **`has_more` speaks about the pinned session**, not about whether Instagram holds other matches.
* **Pages can run short.** Klint suppresses, rather than backfills, an account that goes private after the snapshot.
* **Cursors expire.** After a short lifetime, continuation returns `410 expired-cursor` rather than recomputing against live data:

```json theme={null}
{
  "type": "https://api.tryklint.ai/problems/expired-cursor",
  "status": 410,
  "request_id": "req_example_52",
  "detail": "This cursor's pinned search session has ended. Start a new search to see current results."
}
```

Provisional page bounds: default 20, maximum 100 per page.

## Reading the results

Search and [Get account](/docs/api-reference/instagram/get-account) return the identical full account representation; there is no reduced "search card." A few semantics keep it trustworthy:

* **`id` is durable; `handle` is not.** The `id` survives handle changes, display-name changes, and profile rewrites. Never key on `handle`.
* **Two kinds of truth on one object.** Current profile facts (`biography`, `account_type`, `category_label`, `declared_locations`, `is_verified`, counters) come from what Instagram displays now. Publishing interpretation (`publishing_summary`, `publishing_languages`, `niches`) comes from a bounded set of recent posts. They have different sources and different freshness; don't cross-read them. `category_label` (Instagram's self-presentation text) and `niches` (Klint catalog IDs) routinely disagree, and neither derives from the other.
* **`declared_locations` is declared, never inferred.** Klint never reads it from geotags, travel content, or audience. `[]` means a complete check found no declared base.
* **Zero is real.** `following_count: 0` is an observed count; unavailable counts are `null`.
* **Private accounts leave search.** Search never returns a confirmed-private account; GET may still show its public profile facts with every publishing field cleared to `null`.

Posts embed their author as a fixed seven-field summary (`id`, `handle`, `display_name`, `web_url`, `avatar_url`, `followers_count`, `is_verified`). It's relationship context, not analytical evidence. Dereference `id` with Get account when freshness or the fuller picture matters.

## Continue from here

<Columns cols={2}>
  <Card title="Search posts" icon="magnifying-glass" href="/docs/instagram/search/posts-search">
    Find individual posts by content, format, and metrics.
  </Card>

  <Card title="API reference" icon="code" href="/docs/api-reference/instagram/search-accounts">
    The full request and response schema.
  </Card>
</Columns>
