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

# Descoberta do público-alvo

> Encontre contas relevantes no X por perfis, seguidores, comunidades e engajamento com publicações.

Encontre pessoas relevantes no X com seis técnicas, cada uma baseada em um sinal: palavras-chave no perfil, seguidores, participação em comunidades, publicações recentes, verificação ou engajamento com um post. Combine os resultados por ID para montar uma audiência sem duplicatas.

Veja também o [guia de descoberta de público-alvo](https://api.sorsa.io/blog/twitter-audience-discovery).

## Escolha a técnica

| Pergunta                                          | Endpoint                           | Resultado                                 |
| :------------------------------------------------ | :--------------------------------- | :---------------------------------------- |
| Quem se descreve com um cargo ou termo relevante? | `POST /search-users`               | Perfis de usuários                        |
| Quem segue uma conta do meu setor?                | `GET /followers`                   | Até 200 perfis por página                 |
| Quem entrou em uma comunidade do meu tema?        | `POST /community-members`          | Perfis compactos                          |
| Quem está discutindo meu tema?                    | `POST /search-tweets`              | Até 20 publicações por página com autores |
| Quais contas verificadas seguem o alvo?           | `GET /verified-followers`          | Até 200 perfis por página                 |
| Quem amplia a circulação de um post?              | `POST /retweeters`, `POST /quotes` | Perfis ou publicações de citação          |

O tamanho das páginas varia. Continue com `next_cursor`; uma página curta não indica o fim.

## Configuração e paginação compartilhada

Os exemplos usam `https://api.sorsa.io/v3` e o cabeçalho `ApiKey`. Execute os exemplos Python no mesmo script após a configuração abaixo. Instale `requests` com `python -m pip install requests` e defina `SORSA_API_KEY` no ambiente. JavaScript exige execução no servidor com `fetch`, como Node.js 18+.

```python theme={null}
import os
import time
import requests

API_KEY = os.environ["SORSA_API_KEY"]
BASE_URL = "https://api.sorsa.io/v3"

def fetch_pages(method, endpoint, payload, result_key, max_pages=10):
    """Fetch a bounded number of pages; raise on HTTP errors."""
    items = []
    cursor = None
    seen_cursors = set()

    for _ in range(max_pages):
        values = dict(payload)
        if cursor:
            values["next_cursor"] = cursor
        options = {"params": values} if method == "GET" else {"json": values}
        response = requests.request(
            method, f"{BASE_URL}{endpoint}",
            headers={"ApiKey": API_KEY}, timeout=30, **options,
        )
        response.raise_for_status()
        data = response.json()
        items.extend(data.get(result_key) or [])
        cursor = data.get("next_cursor")
        if not cursor:
            break
        if cursor in seen_cursors:
            raise RuntimeError("Pagination returned a repeated cursor")
        seen_cursors.add(cursor)
        time.sleep(0.1)

    return items
```

`max_pages` limita o consumo, mas pode deixar resultados sem ler. Os exemplos param em erros HTTP. Em produção, adicione tentativas limitadas para 429 e falhas transitórias conforme [códigos de erro](https://docs.sorsa.io/pt-BR/error-codes), e coordene workers com a mesma chave dentro do [limite](https://docs.sorsa.io/pt-BR/rate-limits). Veja [autenticação](https://docs.sorsa.io/pt-BR/authentication) e [paginação](https://docs.sorsa.io/pt-BR/pagination).

## Técnica 1: palavras-chave na bio

**Endpoint:** `POST /v3/search-users`

Busque cargos, funções ou interesses. Confira a bio, o nome público e o nome de usuário retornados para decidir a relevância.

```json theme={null}
{
  "query": "Product Manager"
}
```

| Parâmetro     | Tipo   | Obrigatório | Descrição                                   |
| :------------ | :----- | :---------- | :------------------------------------------ |
| `query`       | string | Sim         | Palavra-chave ou frase.                     |
| `next_cursor` | string | Não         | Cursor anterior; omita na primeira chamada. |

### Python

```python theme={null}
def find_users_by_bio(query, max_pages=10):
    return fetch_pages("POST", "/search-users", {"query": query}, "users", max_pages)

bio_results = find_users_by_bio("machine learning engineer")
qualified = [
    u for u in bio_results
    if (u.get("followers_count") or 0) >= 1000
    and (u.get("tweets_count") or 0) >= 100
    and not u.get("protected", False)
]
```

### JavaScript

```javascript theme={null}
const API_KEY = process.env.SORSA_API_KEY;
if (!API_KEY) throw new Error("Set SORSA_API_KEY before running this example");

async function findUsersByBio(query, maxPages = 10) {
  const users = [];
  const seenCursors = new Set();
  let cursor = null;

  for (let i = 0; i < maxPages; i++) {
    const body = { query };
    if (cursor) body.next_cursor = cursor;
    const response = await fetch("https://api.sorsa.io/v3/search-users", {
      method: "POST",
      headers: { ApiKey: API_KEY, "Content-Type": "application/json" },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(30000),
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const data = await response.json();
    users.push(...(data.users || []));
    cursor = data.next_cursor;
    if (!cursor) break;
    if (seenCursors.has(cursor)) throw new Error("Repeated pagination cursor");
    seenCursors.add(cursor);
    await new Promise((resolve) => setTimeout(resolve, 100));
  }
  return users;
}
```

## Técnica 2: seguidores de concorrentes

**Endpoint:** `GET /v3/followers`

Obtenha até 200 perfis por chamada. Envie um entre `username` (sem @), `user_id` (string) e `user_link` (URL completa), com `next_cursor` opcional.

```text theme={null}
GET https://api.sorsa.io/v3/followers?username=competitor_handle
```

```python theme={null}
def get_followers(username, max_pages=10):
    return fetch_pages("GET", "/followers", {"username": username}, "users", max_pages)

followers = get_followers("competitor_handle", max_pages=20)
```

### Sobreposição entre contas de referência

Conte cada usuário uma vez por conta de referência. Substitua os nomes de exemplo:

```python theme={null}
from collections import Counter

competitors = ["competitor_a", "competitor_b", "competitor_c"]
follower_sets = {
    handle: {u["id"] for u in get_followers(handle, max_pages=10)}
    for handle in competitors
}
counts = Counter(uid for ids in follower_sets.values() for uid in ids)
overlap = {uid for uid, count in counts.items() if count >= 2}
```

A sobreposição se refere às páginas consultadas, não necessariamente às listas completas. Veja [seguidores e contas seguidas](https://docs.sorsa.io/pt-BR/followers-and-following).

## Técnica 3: membros de comunidades

> **Confirme a disponibilidade:** esta seção documenta o formato da requisição. Antes de incluir em um novo fluxo, consulte o [suporte](https://docs.sorsa.io/pt-BR/support) e a nota em [listas e comunidades](https://docs.sorsa.io/pt-BR/lists-and-communities).

**Endpoint:** `POST /v3/community-members`

Participação é um sinal de interesse, mas não prova atividade atual nem intenção de compra.

```json theme={null}
{
  "community_link": "1966045657589813686"
}
```

`community_link` aceita o ID numérico como string ou a URL completa.

```python theme={null}
def get_community_members(community_id, max_pages=20):
    return fetch_pages(
        "POST", "/community-members",
        {"community_link": community_id}, "users", max_pages,
    )
```

Os perfis compactos incluem `id`, `username`, `display_name`, `profile_image_url`, `verified` e `protected`. Antes de filtrar por bio ou seguidores, enriqueça os IDs com [User Profile (Batch)](https://docs.sorsa.io/pt-br/api-reference/usu%C3%A1rios/perfis-de-usu%C3%A1rios-em-lote), até 100 por chamada. Veja [listas e comunidades](https://docs.sorsa.io/pt-BR/lists-and-communities).

## Técnica 4: buscar sinais de intenção em publicações

**Endpoint:** `POST /v3/search-tweets`

Busque conversas recentes e extraia autores únicos. Preserve o objeto completo de cada usuário para combiná-lo com os outros resultados.

```python theme={null}
def find_active_voices(query, min_followers=100, max_pages=10):
    tweets = fetch_pages(
        "POST", "/search-tweets",
        {"query": query, "order": "latest"}, "tweets", max_pages,
    )
    voices = {}
    for tweet in tweets:
        user = tweet.get("user")
        if not user or not user.get("id"):
            continue
        if (user.get("followers_count") or 0) < min_followers:
            continue
        if user["id"] not in voices:
            voices[user["id"]] = {
                **user,
                "sample_tweet": (tweet.get("full_text") or "")[:160],
            }
    return list(voices.values())

intent_voices = find_active_voices(
    '("need a CRM" OR "looking for a CRM") lang:en -filter:retweets',
)
```

### Padrões de consulta

Substitua os marcadores entre colchetes pela categoria, conta, ferramenta ou tema. Parênteses aplicam filtros comuns aos dois lados de OR. **Os exemplos abaixo procuram frases em inglês**; adapte os termos e o filtro de idioma ao público desejado.

| Objetivo                     | Consulta                                                                       |
| :--------------------------- | :----------------------------------------------------------------------------- |
| Intenção de compra           | `("need a [category]" OR "looking for [category]") lang:en -filter:retweets`   |
| Insatisfação com concorrente | `"[competitor]" (frustrated OR broken OR "switching from") -from:[competitor]` |
| Intenção de migração         | `("migrating from [tool]" OR "switching from [tool]") lang:en`                 |
| Pedidos de recomendação      | `("any recommendation" OR "anyone use") [topic] lang:en`                       |
| Discussão de problemas       | `("struggling with" OR "how do you handle") [topic] lang:en`                   |

Adicione `since:` e `until:` para delimitar o período. Leia os posts antes de tratar uma correspondência como intenção de compra. Veja [operadores](https://docs.sorsa.io/pt-BR/search-operators) e [busca](https://docs.sorsa.io/pt-BR/search-tweets).

## Técnica 5: seguidores verificados

**Endpoint:** `GET /v3/verified-followers`

Usa os mesmos identificadores e paginação de `/followers`. Verificação é um atributo de segmentação; avalie a relevância separadamente.

```python theme={null}
def get_verified_followers(username, max_pages=10):
    return fetch_pages(
        "GET", "/verified-followers", {"username": username}, "users", max_pages,
    )

verified = get_verified_followers("openai")
verified.sort(key=lambda u: u.get("followers_count") or 0, reverse=True)
```

## Técnica 6: quem repostou ou citou

**Endpoints:** `POST /v3/retweeters` e `POST /v3/quotes`.

`/retweeters` retorna perfis. `/quotes` retorna publicações, com autor em `user` e comentário em `full_text`.

```python theme={null}
def get_retweeters(tweet_link, max_pages=10):
    return fetch_pages(
        "POST", "/retweeters", {"tweet_link": tweet_link}, "users", max_pages,
    )

def get_quoters(tweet_link, max_pages=10):
    quote_tweets = fetch_pages(
        "POST", "/quotes", {"tweet_link": tweet_link}, "tweets", max_pages,
    )
    return list({
        tweet["user"]["id"]: tweet["user"]
        for tweet in quote_tweets if tweet.get("user")
    }.values())
```

`get_quoters` converte citações em perfis únicos. Se precisar analisar comentários, preserve `quote_tweets` e examine `full_text` antes da conversão.

## Combinar técnicas

Una os usuários pelo ID string, preservando o conjunto de fontes de cada conta. Aparecer em mais fontes é uma heurística de priorização, não uma pontuação de confiança.

```python theme={null}
def score_by_source(by_source):
    index = {}
    for source, users in by_source.items():
        for user in users:
            uid = user["id"]
            if uid not in index:
                index[uid] = {"user": dict(user), "sources": set()}
            else:
                # Fill gaps when one source returns a compact profile.
                for field, value in user.items():
                    if index[uid]["user"].get(field) is None and value is not None:
                        index[uid]["user"][field] = value
            index[uid]["sources"].add(source)

    result = [
        {**entry["user"], "source_count": len(entry["sources"]),
         "sources": sorted(entry["sources"])}
        for entry in index.values()
    ]
    return sorted(result, key=lambda u: (-u["source_count"], -(u.get("followers_count") or 0)))

# Uses the results from Techniques 1, 2, and 4 above.
combined = score_by_source({
    "profile_search": bio_results,
    "competitor_followers": followers,
    "topic_discussion": intent_voices,
})
```

Adicione membros de comunidades após enriquecer os perfis compactos. Inclua também as listas de `get_retweeters` e `get_quoters`.

## Filtrar por critérios de qualidade

Defina critérios explícitos para o projeto. Este filtro verifica preenchimento do perfil, idade e contadores básicos. Ele não detecta bots nem comprova atividade recente; para isso, analise publicações recentes.

```python theme={null}
from datetime import datetime, timezone, timedelta

def is_quality_account(user, min_followers=500, min_tweets=100, max_following_ratio=10):
    if user.get("protected", False):
        return False
    followers = user.get("followers_count") or 0
    if followers < min_followers or (user.get("tweets_count") or 0) < min_tweets:
        return False
    if (user.get("followings_count") or 0) > followers * max_following_ratio:
        return False
    if not (user.get("description") or "").strip():
        return False

    created = user.get("created_at")
    if not created:
        return False
    try:
        dt = datetime.fromisoformat(created.replace("Z", "+00:00"))
        if dt.tzinfo is None:
            return False
    except (TypeError, ValueError):
        return False
    return dt <= datetime.now(timezone.utc) - timedelta(days=30)

qualified = [user for user in combined if is_quality_account(user)]
```

Datas de criação ausentes ou inválidas são excluídas neste exemplo. Ajuste essa política e os limites ao seu caso.

## Exportar para CSV

Exporte após deduplicar e filtrar. Converta publicações para seus objetos `user` e enriqueça perfis compactos quando precisar dos campos ausentes.

```python theme={null}
import csv

def export_users_to_csv(users, output_file="audience.csv"):
    fields = [
        "user_id", "username", "display_name", "description",
        "followers_count", "followings_count", "tweets_count",
        "location", "verified", "created_at",
    ]
    with open(output_file, "w", newline="", encoding="utf-8") as file:
        writer = csv.DictWriter(file, fieldnames=fields)
        writer.writeheader()
        for user in users:
            row = {field: user.get(field, "") for field in fields}
            row["user_id"] = user["id"]
            row["description"] = (user.get("description") or "").replace("\n", " ")
            writer.writerow(row)

export_users_to_csv(qualified)
```

Valores ausentes ficam vazios, sem virar zero. Ao importar em uma planilha, defina `user_id` como texto para preservar o ID completo.

## Próximos passos

* [Busca](https://docs.sorsa.io/pt-BR/search-tweets): parâmetros e exemplos.
* [Operadores](https://docs.sorsa.io/pt-BR/search-operators): lógica e filtros.
* [Seguidores](https://docs.sorsa.io/pt-BR/followers-and-following): análise da audiência.
* [Listas e comunidades](https://docs.sorsa.io/pt-BR/lists-and-communities): membros e feeds.
* [Concorrentes](https://docs.sorsa.io/pt-BR/Competitor-Analysis): inteligência competitiva.
* [Monitoramento](https://docs.sorsa.io/pt-BR/real-time-monitoring): polling e deduplicação.
* [Menções](https://docs.sorsa.io/pt-BR/search-mentions): marcas e concorrentes.
* [Otimização](https://docs.sorsa.io/pt-BR/optimizing-api-usage): lotes e orçamento.
* [Referência](https://docs.sorsa.io/pt-BR/api-reference-guide): especificações.
