API

Integre o QueryWin aos seus próprios fluxos

O QueryWin encontra as buscas que já geram impressões para o seu site, mas ainda não têm uma página escrita para elas, gera o rascunho do artigo que falta e prepara seu produto para ser enviado a diretórios, plataformas de lançamento, comunidades e aos sites que as respostas de IA já citam. Esta API entrega esses dois fluxos aos seus scripts e assistentes de IA. A publicação e o envio continuam acontecendo com suas próprias credenciais e contas: o QueryWin nunca se conecta ao seu CMS nem envia nada por conta própria.

Base URLhttps://www.querywin.com/apiCriar chave de APIEspecificação OpenAPI (JSON) →

Início rápido

Três passos. Tudo o que vem a seguir é uma chamada REST comum com um único cabeçalho.

1

Criar uma chave

No painel do QueryWin, em “API”. A chave em texto puro aparece só uma vez, na criação. Conceda apenas as permissões necessárias: uma chave sem nenhuma opção marcada é somente leitura.

2

Enviar como token Bearer

Inclua o cabeçalho Authorization: Bearer qw_live_… em todas as requisições. X-API-Key também funciona, para ferramentas que só permitem definir um cabeçalho.

3

Ver o que escrever e obter o rascunho

A lista de temas é gratuita e não consome créditos. Gerar uma estrutura ou um rascunho desconta créditos e exige a permissão spend.

bash
# 1. Sites em que esta chave pode atuar
curl "https://www.querywin.com/api/v1/sites" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"

# 2. O que escrever esta semana (grátis, sem créditos)
curl "https://www.querywin.com/api/v1/topics?siteId=YOUR_SITE_ID" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
bash
# 3. Obter o rascunho (Markdown + JSON-LD com os dados preenchidos)
curl "https://www.querywin.com/api/v1/topics/article?siteId=YOUR_SITE_ID&key=standard%20wardrobe%20depth" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"

# 4. Seu script publica no seu próprio blog e depois informa a URL
curl -X POST https://www.querywin.com/api/v1/topics/published \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "siteId": "YOUR_SITE_ID",
    "key": "standard wardrobe depth",
    "url": "https://example.com/blog/standard-wardrobe-depth"
  }'

Toda resposta vem encapsulada: {"success": true, "data": …}. Os erros são códigos legíveis por máquina, nunca frases: o QueryWin é multilíngue, e uma frase em inglês fixa no código acabaria aparecendo assim mesmo em uma página em português.

O fluxo de conteúdo

Cinco passos, e só dois deles têm custo. O passo 4 é seu: o QueryWin entrega o Markdown e os dados estruturados, e quem publica é você.

PassoEndpointCusto
Encontrar buscas com impressões, mas sem páginaGET /v1/topicsGrátis
Gerar uma estrutura embasada em evidênciasPOST /v1/topics/outlineCréditos
Transformar em um rascunho pronto para publicarPOST /v1/topics/articleCréditos
Publicar no seu próprio blogseu próprio CMS—
Informar a URL ao QueryWinPOST /v1/topics/publishedGrátis

O QueryWin nunca se conecta ao seu CMS, nunca guarda as credenciais do seu site e nunca clica em “Publicar” por você. Esta API entrega o conteúdo; a escrita no CMS acontece na sua máquina, com suas próprias credenciais. Esse é o limite, não uma brecha nele.

O fluxo de divulgação

Sete passos, e só um deles tem custo. O passo 6 é seu: o QueryWin entrega o conteúdo de envio de cada canal, e quem envia é você (ou seu agente, com suas próprias contas). Depois, o próprio QueryWin verifica de novo cada listagem publicada.

PassoEndpointCusto
Listar seus produtos e o quanto cada perfil está completoGET /v1/productsGrátis
Listar onde enviar, por ordem de afinidade, incluindo os sites que as respostas de IA citamGET /v1/channels?productId=Grátis
Criar uma campanha com os canais escolhidosPOST /v1/campaignsGrátis
Obter o conteúdo preparado de uma tarefaGET /v1/tasks/{id}Grátis
Reescrever para esse canalPOST /v1/tasks/{id}/materialsCréditos
Enviarsuas próprias contas—
Informar o envio e, depois, a publicação com a URL da listagemPOST /v1/tasks/{id}/statusGrátis

published é o que você informou; verified é o que o QueryWin encontrou ao verificar a listagem de novo, cerca de 72 horas depois: um link para o seu produto em diretórios e listas de ferramentas de IA, uma menção nos demais canais. São campos separados. E um site citado pelas respostas de IA (citedByAi) é um site que vale a pena procurar; não é garantia de que ele vai listar você.

Permissões (scopes)

Cada chave carrega as permissões concedidas na criação. GET /v1/usage informa quais são, para que você nunca precise descobri-las ao receber um 403.

read

Sempre ativo

Todos os endpoints GET: sites, lacunas de conteúdo, estruturas, rascunhos, produtos, canais, campanhas, tarefas e seus conteúdos, uso.

publish

Desativado por padrão

Criar e atualizar perfis de produto, importar imagens e registrar o que aconteceu: a URL de um artigo publicado (também enviada ao Bing, Yandex, Seznam e Naver, mas não ao Google), uma nova campanha, uma tarefa marcada como enviada ou publicada. É gratuito, mas cada chamada gera um registro com consequências: as campanhas contam no limite do seu plano, e uma listagem publicada passa por nova verificação.

spend

Desativado por padrão

Gerar estruturas e rascunhos e reescrever conteúdos de envio para um canal. Essas ações descontam créditos. Conceda esta permissão apenas se quiser que quem faz a chamada (um script ou um assistente de IA) gaste por conta própria.

Não há hierarquia: publish não inclui spend, e spend não inclui publish. São tipos diferentes de risco. Uma chamada para a qual sua chave não tem permissão retorna 403 com missing_scope_<name> e as permissões que você tem.

Consumo de créditos

Dois endpoints descontam créditos: POST /v1/topics/outline e POST /v1/topics/article. Três travas de segurança protegem esses endpoints.

TravaO que ela impede
confirmSpendUm script que continua gastando créditos depois de um aumento de preço.
Limite diárioUm loop descontrolado que esgota o saldo durante a noite. Retorna 429 com o horário em que o limite é renovado.
Fingerprint da entradaCobrança dupla pelo mesmo tema. Entradas idênticas retornam o resultado em cache sem custo, então é seguro repetir uma requisição que excedeu o tempo limite.

confirmSpend é um teto de autorização, não um valor exato. Envie um valor maior ou igual ao preço atual e será cobrado apenas o custo real, muitas vezes zero quando o resultado está em cache. Se o preço um dia passar do seu teto, a chamada falha com confirm_spend_too_low em vez de cobrar mais sem avisar.

Convenções

Três regras que valem para todos os endpoints.

Envelope de resposta

json
{ "success": true,  "data": { "...": "..." } }
{ "success": false, "error": "missing_scope_spend" }

Erros são códigos, nunca frases. Leia os códigos, mas não os exiba crus: eles existem para serem convertidos nos seus próprios textos.

Resultado de negócio não é erro

Uma chamada de geração que não conseguiu produzir um resultado válido retorna HTTP 200 com data.ok = false e um código data.failure, para que você consiga diferenciá-la de uma falha de autenticação ou de uma conexão interrompida. Nada é cobrado nesses casos.

json
{
  "success": true,
  "data": { "ok": false, "failure": "no_outline", "creditsSpent": 0 }
}

Limite de requisições

120 requisições por minuto por chave. Acima disso, você recebe 429 com o horário em que o limite é renovado. Esse limite é separado do limite diário de geração descrito acima.

MCP para agentes de IA

Os dois fluxos estão disponíveis via Model Context Protocol, autenticados com a mesma chave e o mesmo cabeçalho. Adicione o servidor ao Claude Code, ao Cursor, ao n8n ou a qualquer outra ferramenta compatível com MCP via HTTP.

MCPhttps://www.querywin.com/api/mcp
bash
claude mcp add --transport http querywin https://www.querywin.com/api/mcp \
  --header "Authorization: Bearer $QUERYWIN_API_KEY"

Dezenove ferramentas. Perfis de produto: create_product, get_product, update_product, import_product_image. Conteúdo: list_sites, get_usage, list_content_gaps, get_outline, get_article_draft, generate_outline, generate_article_draft, mark_published. Divulgação: list_products, list_channels, create_campaign, list_tasks, get_task, write_task_materials, report_task_status. Pergunte ao seu assistente o que escrever esta semana ou onde enviar seu produto em seguida, e ele vai descobrir sozinho.

prompt
Usando o servidor MCP do QueryWin, encontre as três maiores lacunas de conteúdo do meu site,
mostre as buscas por trás de cada uma e diga quanto custaria gerar o rascunho da primeira.

As ferramentas são filtradas por permissão. Com uma chave somente leitura, as ferramentas de geração nem aparecem na lista de ferramentas do assistente: um agente não consegue chamar uma ferramenta que não vê. Dê a cada agente sua própria chave, para poder revogá-la sem mexer nas suas outras integrações.

A autenticação usa token Bearer, o que a especificação MCP permite (nela, a autorização é opcional). Clientes que permitem definir um cabeçalho (Claude Code, Cursor, n8n) se conectam diretamente. Hosts que exigem uma tela de consentimento OAuth podem não conseguir se conectar.

Account

GET/v1/sites

Listar os sites em que esta chave pode atuar

Comece por aqui. Todos os outros endpoints recebem o siteId retornado por esta chamada. Sem siteId, eles usam o primeiro site conectado: tudo bem para contas com um único site, mas um bug em potencial para todas as outras.

Resposta200

CampoTipoDescrição
successenumValores: true
dataSiteList
└sitesarray<Site>
└siteIdstring
└domainstring
└gscPropertystringA propriedade do Search Console, exatamente como está cadastrada: sc-domain:example.com ou https://example.com/.
└syncStatusenumValores: pendingsyncingdonefailed
└syncedThroughaceita nullstringO Search Console tem um atraso de 2 a 3 dias. Todas as métricas deste site se referem a dados até esta data — informe isso sempre que exibir esses números.
Erros possíveis
401Chave ausente, mal formatada, revogada ou expirada

Exemplo

bash
curl "https://www.querywin.com/api/v1/sites" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "sites": [
      {
        "siteId": "string",
        "domain": "example.com",
        "gscProperty": "string",
        "syncStatus": "pending",
        "syncedThrough": "2026-09-01T00:00:00.000Z"
      }
    ]
  }
}
GET/v1/usage

Preços atuais, gerações gratuitas, saldo de créditos e limites diários

Leia antes de gerar qualquer coisa. É a mesma fonte de verdade que a interface web usa para decidir o que mostrar em cada botão: a disponibilidade é decidida no servidor, não na base da tentativa e erro.

Resposta200

CampoTipoDescrição
successenumValores: true
dataUsage
└scopesarray<enum>O que esta chave pode fazer. Leia uma vez na inicialização, em vez de descobrir suas permissões esbarrando em um erro 403.Valores: readpublishspend
└creditsobject
└balanceinteger
└outlineStepUsage
└availablebooleanÉ false quando o mecanismo de geração não está configurado. Nesse caso, não chame o endpoint POST.
└pricePerOutlineintegerCréditos por estrutura (presente apenas em outline).
└pricePerArticleintegerCréditos por rascunho (presente apenas em article).
└pricePerTaskintegerCréditos por reescrita para um canal (presente apenas em materials).
└freeRemainingintegerGerações gratuitas restantes nesta conta, contadas por tópico distinto (estruturas, rascunhos) ou por tarefa distinta (conteúdos de envio) — não por cliques no botão. As gerações gratuitas também exigem confirmSpend.
└dailyDailyLimitUma proteção contra scripts descontrolados, contada no banco de dados em todos os pontos de acesso (o uso pela interface web também conta). Zera à meia-noite, no horário local, e não em uma janela deslizante.
└usedinteger
└limitinteger
└remaininginteger
└resetAtstring
└articleStepUsage
└availablebooleanÉ false quando o mecanismo de geração não está configurado. Nesse caso, não chame o endpoint POST.
└pricePerOutlineintegerCréditos por estrutura (presente apenas em outline).
└pricePerArticleintegerCréditos por rascunho (presente apenas em article).
└pricePerTaskintegerCréditos por reescrita para um canal (presente apenas em materials).
└freeRemainingintegerGerações gratuitas restantes nesta conta, contadas por tópico distinto (estruturas, rascunhos) ou por tarefa distinta (conteúdos de envio) — não por cliques no botão. As gerações gratuitas também exigem confirmSpend.
└dailyDailyLimitUma proteção contra scripts descontrolados, contada no banco de dados em todos os pontos de acesso (o uso pela interface web também conta). Zera à meia-noite, no horário local, e não em uma janela deslizante.
└usedinteger
└limitinteger
└remaininginteger
└resetAtstring
└materialsStepUsage
└availablebooleanÉ false quando o mecanismo de geração não está configurado. Nesse caso, não chame o endpoint POST.
└pricePerOutlineintegerCréditos por estrutura (presente apenas em outline).
└pricePerArticleintegerCréditos por rascunho (presente apenas em article).
└pricePerTaskintegerCréditos por reescrita para um canal (presente apenas em materials).
└freeRemainingintegerGerações gratuitas restantes nesta conta, contadas por tópico distinto (estruturas, rascunhos) ou por tarefa distinta (conteúdos de envio) — não por cliques no botão. As gerações gratuitas também exigem confirmSpend.
└dailyDailyLimitUma proteção contra scripts descontrolados, contada no banco de dados em todos os pontos de acesso (o uso pela interface web também conta). Zera à meia-noite, no horário local, e não em uma janela deslizante.
└usedinteger
└limitinteger
└remaininginteger
└resetAtstring
└distributionDistributionQuotaOs limites de divulgação do plano e o uso atual. Um limite null significa sem limite.
└planstring
└limitsobject
└channelsaceita nullintegerCanais distintos em que um produto pode ter tarefas, contados ao longo de channelsPeriod.
└channelsPeriodenummonth (planos pagos): contado por mês do calendário (UTC), então cada mês libera um novo lote de canais. total (plano gratuito): contado ao longo de toda a vida do produto.Valores: monthtotal
└tasksPerMonthaceita nullintegerTarefas que podem ser criadas por mês do calendário (UTC), somando todos os produtos.
└activeCampaignsaceita nullintegerCampanhas que podem ficar ativas ao mesmo tempo.
└usageobject
└activeCampaignsinteger
└tasksThisMonthinteger
└channelsaceita nullintegerApenas quando a requisição indicou um produto.
Erros possíveis
401Falha na autenticação

Exemplo

bash
curl "https://www.querywin.com/api/v1/usage" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "scopes": [
      "read"
    ],
    "credits": {
      "balance": 0
    },
    "outline": {
      "available": true,
      "pricePerOutline": 0,
      "pricePerArticle": 0,
      "pricePerTask": 0,
      "freeRemaining": 0,
      "daily": {
        "used": 0,
        "limit": 0,
        "remaining": 0,
        "resetAt": "2026-09-01T00:00:00.000Z"
      }
    },
    "article": {
      "available": true,
      "pricePerOutline": 0,
      "pricePerArticle": 0,
      "pricePerTask": 0,
      "freeRemaining": 0,
      "daily": {
        "used": 0,
        "limit": 0,
        "remaining": 0,
        "resetAt": "2026-09-01T00:00:00.000Z"
      }
    },
    "materials": {
      "available": true,
      "pricePerOutline": 0,
      "pricePerArticle": 0,
      "pricePerTask": 0,
      "freeRemaining": 0,
      "daily": {
        "used": 0,
        "limit": 0,
        "remaining": 0,
        "resetAt": "2026-09-01T00:00:00.000Z"
      }
    },
    "distribution": {
      "plan": "string",
      "limits": {
        "channels": 0,
        "channelsPeriod": "month",
        "tasksPerMonth": 0,
        "activeCampaigns": 0
      },
      "usage": {
        "activeCampaigns": 0,
        "tasksThisMonth": 0,
        "channels": 0
      }
    }
  }
}

Topics

GET/v1/topics

Buscas com impressões, mas sem nenhuma página dedicada

Gratuito, sem cobrança externa (a própria API exige um plano pago). Calculado do zero a cada chamada a partir dos seus próprios dados do Search Console, sem nenhum banco de palavras-chave externo — e é justamente isso que importa: “você já tem impressões e nenhuma página para isso” não é algo que uma ferramenta de palavras-chave consiga dizer.

Os resultados são ordenados por oportunidade. Os tópicos que o usuário descartou na interface ficam de fora.

Parâmetros de consulta

CampoTipoDescrição
siteIdstringObtido em GET /v1/sites. Por padrão, o primeiro site conectado.

Resposta200

CampoTipoDescrição
successenumValores: true
dataTopicList
└siteIdaceita nullstring
└topicsarray<Topic>
└keystringO identificador do cluster — envie-o de volta como key em todos os outros endpoints de tópicos. É o texto normalizado da busca representativa, por isso pode conter espaços, barras e caracteres não latinos. Envie-o sempre na query string ou no corpo, nunca no caminho da URL.
└titlestringA busca com mais impressões do cluster, sem alterações. Não é um título gerado — esse vem com a estrutura.
└shapeenumcomparison significa que este cluster bateu com a sua lista de concorrentes. O artigo precisa comparar, não explicar o concorrente — caso contrário, você estará escrevendo conteúdo para ele.Valores: comparisonroundupguide
└intentstring
└membersarray<TopicMember>
└textstringA busca, como foi digitada.
└impressionsinteger
└clicksinteger
└positionaceita nullnumber
└landingUrlaceita nullstringA página que o Search Console registra hoje para esta busca, se houver.
└impressionsintegerValor medido, obtido do Search Console.
└clicksintegerValor medido, obtido do Search Console.
└positionaceita nullnumberValor medido: posição média no cluster, ponderada pelas impressões.
└competitorboolean
└scorenumber
└rankinteger
└upsideClicksintegerÉ uma estimativa, não uma medição: cliques extras por mês se uma página dedicada chegasse à posição 3. Este campo é propositalmente separado de clicks e impressions, e precisa continuar visualmente separado onde quer que você o exiba. Apresentar uma projeção como dado medido é a falha típica desta categoria de produto.
└statusenumValores: newdismissedplannedpublished
└outlineAtaceita nullstring
└articleAtaceita nullstringNão deduza este campo a partir de outlineAt. Ter uma estrutura não significa que exista um rascunho — são duas etapas pagas distintas.
└totalQueriesintegerQuantas buscas distintas esses tópicos cobrem no total.
Erros possíveis
401Falha na autenticação
404site_not_found — o siteId não pertence a esta conta

Exemplo

bash
curl "https://www.querywin.com/api/v1/topics" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "siteId": "string",
    "topics": [
      {
        "key": "string",
        "title": "string",
        "shape": "comparison",
        "intent": "string",
        "members": [
          {
            "text": null,
            "impressions": null,
            "clicks": null,
            "position": null,
            "landingUrl": null
          }
        ],
        "impressions": 0,
        "clicks": 0,
        "position": 0,
        "competitor": true,
        "score": 0,
        "rank": 0,
        "upsideClicks": 0,
        "status": "new",
        "outlineAt": "2026-09-01T00:00:00.000Z",
        "articleAt": "2026-09-01T00:00:00.000Z"
      }
    ],
    "totalQueries": 0
  }
}
GET/v1/topics/article

Ler um rascunho já gerado

Nunca dispara uma geração e nunca custa nada. article é null enquanto não houver nenhum rascunho.

Parâmetros de consulta

CampoTipoDescrição
keyobrigatóriostringO identificador do cluster, obtido em GET /v1/topics.
siteIdstringObtido em GET /v1/sites. Por padrão, o primeiro site conectado.

Resposta200

CampoTipoDescrição
successenumValores: true
dataArticleResult
└articleaceita nullArticle
└titlestring
└descriptionstringA meta description.
└markdownstringO corpo a publicar.
└jsonLdstringOs dados estruturados deste artigo, já preenchidos. JSON válido — coloque-o dentro de uma tag script do tipo application/ld+json na página publicada.
└wordCountinteger
└warningsarray<ArticleWarning>Trechos que soam como escritos por IA. O rascunho continua utilizável — esses avisos são reportados em vez de reescritos em silêncio. Registre-os nos seus logs. Em um fluxo automatizado, este é o único momento em que alguém poderia perceber.
└kindenumValores: banned_phrasebanned_wordem_dash_overuseno_short_sentence
└hitstringO trecho problemático.
└articleAtaceita nullstring
└modelaceita nullstring
Erros possíveis
400key_required
401Falha na autenticação

Exemplo

bash
curl "https://www.querywin.com/api/v1/topics/article?key=..." \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "article": {
      "title": "string",
      "description": "string",
      "markdown": "string",
      "jsonLd": "string",
      "wordCount": 0,
      "warnings": [
        {
          "kind": "banned_phrase",
          "hit": "string"
        }
      ]
    },
    "articleAt": "2026-09-01T00:00:00.000Z",
    "model": "string"
  }
}
POST/v1/topics/article

Transformar a estrutura em um rascunho pronto para publicar (consome créditos)

A chamada mais cara do produto. É síncrona e pode levar de um a dois minutos.

Antes, é preciso existir uma estrutura — sem ela, você recebe failure: "no_outline". É a estrutura que traz as evidências (as buscas reais por trás do cluster, as páginas que a IA cita hoje, a deduplicação em relação às páginas que você já tem). Pular essa etapa reduziria esta chamada a uma ferramenta de escrita com IA sem nada por trás.

article.markdown é o corpo a publicar. article.jsonLd traz os dados estruturados já preenchidos com o conteúdo deste artigo. **Registre os article.warnings nos seus logs, não os descarte** — cada aviso aponta um trecho específico que soa como escrito por IA, e em um fluxo automatizado ninguém relê o rascunho antes de ele ir ao ar.

Rascunhos reprovados na validação estrutural (seções faltando, perguntas obrigatórias sem resposta, links inventados, JSON-LD inválido) são descartados e não são cobrados.

Corpo da requisição

CampoTipoDescrição
keyobrigatóriostringO identificador do cluster, obtido em GET /v1/topics.
siteIdstringPor padrão, o primeiro site conectado.
confirmSpendobrigatóriointegerUm teto de autorização em créditos, não um valor exato. Envie um valor maior ou igual ao preço atual informado por GET /v1/usage; você paga o custo real, que muitas vezes é zero quando há resultado em cache. Se algum dia o preço passar do seu teto, a chamada é recusada em vez de cobrar mais sem avisar. Obrigatório mesmo que ainda restem gerações gratuitas — elas acabam, e não deve ser nesse momento que seu script descobre que este endpoint custa dinheiro. (min 0)

Resposta200

CampoTipoDescrição
successenumValores: true
dataArticleOutcome
└okboolean
└failureenumPresente apenas quando ok é false. O status HTTP continua 200.Valores: topic_not_foundno_outlineengine_unavailableengine_failedno_valid_article
└articleaceita nullArticle
└titlestring
└descriptionstringA meta description.
└markdownstringO corpo a publicar.
└jsonLdstringOs dados estruturados deste artigo, já preenchidos. JSON válido — coloque-o dentro de uma tag script do tipo application/ld+json na página publicada.
└wordCountinteger
└warningsarray<ArticleWarning>Trechos que soam como escritos por IA. O rascunho continua utilizável — esses avisos são reportados em vez de reescritos em silêncio. Registre-os nos seus logs. Em um fluxo automatizado, este é o único momento em que alguém poderia perceber.
└kindenumValores: banned_phrasebanned_wordem_dash_overuseno_short_sentence
└hitstringO trecho problemático.
└generatedbooleanÉ false quando um resultado em cache foi retornado — nada foi cobrado.
└creditsSpentinteger
└freeUsedboolean
└rejectedarray<string>Falhas estruturais que fizeram o rascunho ser descartado (e não cobrado): missing_sections, missing_faq, invented_link, invalid_json_ld, comparison_without_contrast, body_too_short.
Erros possíveis
400key_required, confirm_spend_required ou confirm_spend_too_low (o corpo traz o price atual)
401Falha na autenticação
402insufficient_credits — o corpo traz requiredCredits, currentBalance, shortfall
403missing_scope_spend — esta chave não recebeu a permissão spend
429rate_limited ou daily_limit_reached (o corpo traz resetAt)

Exemplo

bash
curl -X POST https://www.querywin.com/api/v1/topics/article \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "siteId": "clx1site000",
    "key": "standard wardrobe depth",
    "confirmSpend": 80
  }'
json — resposta
{
  "success": true,
  "data": {
    "ok": true,
    "failure": "topic_not_found",
    "article": {
      "title": "string",
      "description": "string",
      "markdown": "string",
      "jsonLd": "string",
      "wordCount": 0,
      "warnings": [
        {
          "kind": "banned_phrase",
          "hit": "string"
        }
      ]
    },
    "generated": true,
    "creditsSpent": 0,
    "freeUsed": true,
    "rejected": [
      "string"
    ]
  }
}
GET/v1/topics/outline

Ler uma estrutura já gerada

Nunca dispara uma geração e nunca custa nada. outline é null enquanto não houver nenhuma estrutura.

Parâmetros de consulta

CampoTipoDescrição
keyobrigatóriostringO identificador do cluster, obtido em GET /v1/topics.
siteIdstringObtido em GET /v1/sites. Por padrão, o primeiro site conectado.

Resposta200

CampoTipoDescrição
successenumValores: true
dataOutlineResult
└outlineaceita nullOutline
└titlestring
└slugstring
└anglestringO argumento que esta página deve defender.
└sectionsarray<object>
└headingstring
└pointsarray<string>
└faqarray<object>Perguntas que a página precisa responder. São esses os ganchos que as respostas de IA citam.
└questionstring
└answerstring
└schemaTypestringO tipo de JSON-LD adequado para esta página.
└internalLinksarray<string>Páginas do seu próprio site que vale a pena linkar. Escolhidas entre URLs reais, nunca inventadas.
└outlineAtaceita nullstring
└modelaceita nullstring
Erros possíveis
400key_required
401Falha na autenticação

Exemplo

bash
curl "https://www.querywin.com/api/v1/topics/outline?key=..." \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "outline": {
      "title": "string",
      "slug": "string",
      "angle": "string",
      "sections": [
        {
          "heading": "string",
          "points": []
        }
      ],
      "faq": [
        {
          "question": "string",
          "answer": "string"
        }
      ],
      "schemaType": "string",
      "internalLinks": [
        "string"
      ]
    },
    "outlineAt": "2026-09-01T00:00:00.000Z",
    "model": "string"
  }
}
POST/v1/topics/outline

Gerar uma estrutura (consome créditos)

A chamada é síncrona e leva cerca de 10 a 20 segundos.

Entradas idênticas retornam a estrutura em cache sem nova cobrança — a assinatura usada no cache considera o cluster do tópico e a classificação dele em relação aos concorrentes, então é seguro repetir uma requisição HTTP que falhou.

Retorna HTTP 200 com ok: false e um código em failure para resultados de negócio (mecanismo de geração indisponível, saída reprovada na validação). Já a falta de créditos retorna um 402 de verdade.

Corpo da requisição

CampoTipoDescrição
keyobrigatóriostringO identificador do cluster, obtido em GET /v1/topics.
siteIdstringPor padrão, o primeiro site conectado.
confirmSpendobrigatóriointegerUm teto de autorização em créditos, não um valor exato. Envie um valor maior ou igual ao preço atual informado por GET /v1/usage; você paga o custo real, que muitas vezes é zero quando há resultado em cache. Se algum dia o preço passar do seu teto, a chamada é recusada em vez de cobrar mais sem avisar. Obrigatório mesmo que ainda restem gerações gratuitas — elas acabam, e não deve ser nesse momento que seu script descobre que este endpoint custa dinheiro. (min 0)

Resposta200

CampoTipoDescrição
successenumValores: true
dataOutlineOutcome
└okboolean
└failureenumPresente apenas quando ok é false. O status HTTP continua 200 — é um resultado, não um erro.Valores: topic_not_foundengine_unavailableengine_failedno_valid_outline
└outlineaceita nullOutline
└titlestring
└slugstring
└anglestringO argumento que esta página deve defender.
└sectionsarray<object>
└headingstring
└pointsarray<string>
└faqarray<object>Perguntas que a página precisa responder. São esses os ganchos que as respostas de IA citam.
└questionstring
└answerstring
└schemaTypestringO tipo de JSON-LD adequado para esta página.
└internalLinksarray<string>Páginas do seu próprio site que vale a pena linkar. Escolhidas entre URLs reais, nunca inventadas.
└generatedbooleanÉ false quando um resultado em cache foi retornado — nada foi cobrado.
└creditsSpentinteger
└freeUsedboolean
└rejectedarray<string>Regras de validação que a saída do modelo violou. Vale registrar como sinal de qualidade.
Erros possíveis
400key_required, confirm_spend_required ou confirm_spend_too_low (o corpo traz o price atual)
401Falha na autenticação
402insufficient_credits — o corpo traz requiredCredits, currentBalance, shortfall
403missing_scope_spend — esta chave não recebeu a permissão spend
429rate_limited ou daily_limit_reached (o corpo traz resetAt)

Exemplo

bash
curl -X POST https://www.querywin.com/api/v1/topics/outline \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "siteId": "clx1site000",
    "key": "standard wardrobe depth",
    "confirmSpend": 80
  }'
json — resposta
{
  "success": true,
  "data": {
    "ok": true,
    "failure": "topic_not_found",
    "outline": {
      "title": "string",
      "slug": "string",
      "angle": "string",
      "sections": [
        {
          "heading": "string",
          "points": []
        }
      ],
      "faq": [
        {
          "question": "string",
          "answer": "string"
        }
      ],
      "schemaType": "string",
      "internalLinks": [
        "string"
      ]
    },
    "generated": true,
    "creditsSpent": 0,
    "freeUsed": true,
    "rejected": [
      "string"
    ]
  }
}
POST/v1/topics/published

Informar onde você publicou

Fecha o ciclo. Marca o tópico como publicado, armazena a URL e a envia ao IndexNow em seu nome.

O IndexNow cobre Bing, Yandex, Seznam e Naver — não o Google. O Google não tem um endpoint equivalente de indexação instantânea; ele encontra a página pelo seu sitemap.

O envio ao IndexNow nunca faz a requisição falhar: seu artigo já está publicado, e é esse o fato que esta chamada registra. Consulte o campo indexnow para ver o que de fato aconteceu. O envio exige que o arquivo de chave do IndexNow esteja verificado para o site (configure isso uma vez no painel).

Registrar a URL também é o que permite ao QueryWin medir de novo as buscas que este artigo mira, depois que ele tiver tido tempo de surtir efeito.

Corpo da requisição

CampoTipoDescrição
keyobrigatóriostring
siteIdstring
urlobrigatóriostringOnde você publicou. Somente http/https. A página não é acessada neste momento.

Resposta200

CampoTipoDescrição
successenumValores: true
dataPublishedResult
└clusterKeystring
└statusenumValores: published
└publishedUrlstring
└publishedAtstring
└indexnowobjectBing, Yandex, Seznam e Naver. Não inclui o Google.
└pushedboolean
└outcomestringskipped geralmente significa que o arquivo de chave ainda não foi verificado.
└enginesstring
Erros possíveis
400key_required, url_required ou invalid_url (somente http/https)
401Falha na autenticação
403missing_scope_publish — esta chave não recebeu a permissão publish
404site_not_found

Exemplo

bash
curl -X POST https://www.querywin.com/api/v1/topics/published \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "siteId": "clx1site000",
    "key": "standard wardrobe depth",
    "url": "https://example.com/blog/standard-wardrobe-depth"
  }'
json — resposta
{
  "success": true,
  "data": {
    "clusterKey": "string",
    "status": "published",
    "publishedUrl": "string",
    "publishedAt": "2026-09-01T00:00:00.000Z",
    "indexnow": {
      "pushed": true,
      "outcome": "string",
      "engines": "string"
    }
  }
}

Distribution

GET/v1/campaigns

Campanhas e sua cota de divulgação

As campanhas (sem as arquivadas, a menos que includeArchived=true), mais os limites de divulgação do plano e o uso atual. Grátis.

Parâmetros de consulta

CampoTipoDescrição
productIdstringApenas as campanhas deste produto.
includeArchivedboolean

Resposta200

CampoTipoDescrição
successenumValores: true
dataCampaignList
└campaignsarray<Campaign>
└campaignIdstring
└productIdstring
└namestring
└statusenumcompleted é calculado: todas as tarefas estão publicadas, verificadas, com falha ou ignoradas.Valores: activecompletedarchived
└quotaintegerCom quantos canais a campanha foi criada.
└startsAtstring
└endsAtaceita nullstring
└countsobject
└totalinteger
└submittedintegersubmitted + published + verified.
└liveintegerpublished + verified.
└blockedinteger
└doneinteger
└byStatusobject
└createdAtstring
└quotaDistributionQuotaOs limites de divulgação do plano e o uso atual. Um limite null significa sem limite.
└planstring
└limitsobject
└channelsaceita nullintegerCanais distintos em que um produto pode ter tarefas, contados ao longo de channelsPeriod.
└channelsPeriodenummonth (planos pagos): contado por mês do calendário (UTC), então cada mês libera um novo lote de canais. total (plano gratuito): contado ao longo de toda a vida do produto.Valores: monthtotal
└tasksPerMonthaceita nullintegerTarefas que podem ser criadas por mês do calendário (UTC), somando todos os produtos.
└activeCampaignsaceita nullintegerCampanhas que podem ficar ativas ao mesmo tempo.
└usageobject
└activeCampaignsinteger
└tasksThisMonthinteger
└channelsaceita nullintegerApenas quando a requisição indicou um produto.
Erros possíveis
401Falha na autenticação

Exemplo

bash
curl "https://www.querywin.com/api/v1/campaigns" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "campaigns": [
      {
        "campaignId": "string",
        "productId": "string",
        "name": "string",
        "status": "active",
        "quota": 0,
        "startsAt": "2026-09-01T00:00:00.000Z",
        "endsAt": "2026-09-01T00:00:00.000Z",
        "counts": {
          "total": 0,
          "submitted": 0,
          "live": 0,
          "blocked": 0,
          "done": 0,
          "byStatus": null
        },
        "createdAt": "2026-09-01T00:00:00.000Z"
      }
    ],
    "quota": {
      "plan": "string",
      "limits": {
        "channels": 0,
        "channelsPeriod": "month",
        "tasksPerMonth": 0,
        "activeCampaigns": 0
      },
      "usage": {
        "activeCampaigns": 0,
        "tasksThisMonth": 0,
        "channels": 0
      }
    }
  }
}
POST/v1/campaigns

Criar uma campanha a partir de uma lista explícita de canais

Uma tarefa por ID de canal, com os conteúdos de envio preparados na hora a partir do perfil do produto, sem custo. Passe os canais que foram de fato escolhidos — uma campanha é o registro de onde você decidiu enviar, não um filtro que o servidor expande.

Canais em que o produto já tem uma tarefa aberta ou enviada são pulados e listados em skipped, com um motivo (already_open, already_submitted, not_found, broken, inactive, other_product, locked). Se não sobrar nenhum, a chamada retorna HTTP 200 com ok: false e failure: "no_valid_targets". Ultrapassar a cota de divulgação do plano resulta em recusa: **409 quota_exceeded**, com dimension, limit, used e requested. Nada é criado, nem mesmo a parte que caberia na cota.

Corpo da requisição

CampoTipoDescrição
productIdobrigatóriostring
nameobrigatóriostring
targetIdsobrigatórioarray<string>IDs de canais obtidos em GET /v1/channels. Apenas os canais escolhidos.
endsAtstringPrazo opcional exibido no painel. Nada é encerrado automaticamente.

Resposta200

CampoTipoDescrição
successenumValores: true
dataCreateCampaignOutcome
└okboolean
└failureenumPresente quando ok é false.Valores: no_valid_targets
└campaignCampaign
└campaignIdstring
└productIdstring
└namestring
└statusenumcompleted é calculado: todas as tarefas estão publicadas, verificadas, com falha ou ignoradas.Valores: activecompletedarchived
└quotaintegerCom quantos canais a campanha foi criada.
└startsAtstring
└endsAtaceita nullstring
└countsobject
└totalinteger
└submittedintegersubmitted + published + verified.
└liveintegerpublished + verified.
└blockedinteger
└doneinteger
└byStatusobject
└createdAtstring
└tasksarray<Task>
└taskIdstring
└campaignIdstring
└productIdstring
└statusenumpublished é o que você informou. verified é o que o QueryWin viu na página da listagem (um link, no caso de diretórios e listas de ferramentas de IA; uma menção, nos demais). Mantenha os dois separados ao reportar.Valores: plannedpreparedin_progressblockedsubmittedpublishedverifiedfailedskipped
└blockedReasonaceita nullenumValores: logincaptchapaymentmissing_materialothernull
└missingarray<string>Conteúdos obrigatórios que faltam no perfil. Complete o perfil no painel; a tarefa se prepara de novo sozinha.
└listingUrlaceita nullstring
└markedByenumQuem fez a última mudança de status: uma pessoa (ou esta API), a extensão do navegador ou o próprio QueryWin.Valores: userdevicesystem
└hasGeneratedbooleanExiste uma reescrita específica para este canal.
└reviewDueAtaceita nullstringQuando voltar a conferir depois do envio (submittedAt + os dias de análise do canal).
└submittedAtaceita nullstring
└publishedAtaceita nullstring
└verifiedAtaceita nullstring
└nextarray<string>Status que você pode definir a partir do atual, via POST /v1/tasks/{id}/status.
└targetobject
└targetIdstring
└namestring
└urlstring
└submitUrlstring
└kindstring
└sourceenumValores: seeduserrivals
└languagestring
└requiresBacklinkboolean
└checkaceita nullobjectA verificação que o QueryWin faz na listagem depois da publicação. É null até a tarefa ser publicada.
└kindenumO que é procurado: um link para o produto (diretórios, listas de ferramentas de IA) ou uma menção à marca (todos os demais).Valores: link_livemention_seen
└stateaceita nullenumconfirmed = encontrado. unconfirmed = não encontrado em uma rodada de verificações (status inalterado; confira a URL). lost = estava lá e sumiu (tarefa marcada como falha).Valores: confirmedunconfirmedlostnull
└checkedAtaceita nullstring
└dueAtaceita nullstring
└updatedAtstring
└skippedarray<object>
└targetIdstring
└reasonenumother_product = um candidato citado pela IA que pertence a outro produto; inactive = um canal desativado ou ignorado; locked = fora da janela do catálogo de canais do plano gratuito (os primeiros window canais por relevância, como retornados por GET /v1/channels; seus próprios canais, os favoritos e os citados pela IA não têm limite).Valores: not_foundbrokeninactiveother_productalready_openalready_submittedlocked
Erros possíveis
400product_id_required, name_required / name_too_long (80 caracteres), target_ids_required / too_many_targets (100 canais) ou invalid_date
401Falha na autenticação
403missing_scope_publish — esta chave não recebeu a permissão publish
404product_not_found — o productId não pertence a esta conta
409quota_exceeded — o corpo traz dimension (active_campaigns / tasks_per_month / channels), limit, used, requested; para channels, também period (month / total, igual a DistributionQuota.limits.channelsPeriod)

Exemplo

bash
curl -X POST https://www.querywin.com/api/v1/campaigns \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "clx1prod000",
    "name": "Directories, September",
    "targetIds": [
      "clx1tgt001",
      "clx1tgt002"
    ]
  }'
json — resposta
{
  "success": true,
  "data": {
    "ok": true,
    "failure": "no_valid_targets",
    "campaign": {
      "campaignId": "string",
      "productId": "string",
      "name": "string",
      "status": "active",
      "quota": 0,
      "startsAt": "2026-09-01T00:00:00.000Z",
      "endsAt": "2026-09-01T00:00:00.000Z",
      "counts": {
        "total": 0,
        "submitted": 0,
        "live": 0,
        "blocked": 0,
        "done": 0,
        "byStatus": null
      },
      "createdAt": "2026-09-01T00:00:00.000Z"
    },
    "tasks": [
      {
        "taskId": "string",
        "campaignId": "string",
        "productId": "string",
        "status": "planned",
        "blockedReason": "login",
        "missing": [
          "string"
        ],
        "listingUrl": "string",
        "markedBy": "user",
        "hasGenerated": true,
        "reviewDueAt": "2026-09-01T00:00:00.000Z",
        "submittedAt": "2026-09-01T00:00:00.000Z",
        "publishedAt": "2026-09-01T00:00:00.000Z",
        "verifiedAt": "2026-09-01T00:00:00.000Z",
        "next": [
          "string"
        ],
        "target": {
          "targetId": "string",
          "name": "string",
          "url": "string",
          "submitUrl": "string",
          "kind": "string",
          "source": "seed",
          "language": "string",
          "requiresBacklink": true
        },
        "check": {
          "kind": "link_live",
          "state": "confirmed",
          "checkedAt": "2026-09-01T00:00:00.000Z",
          "dueAt": "2026-09-01T00:00:00.000Z"
        },
        "updatedAt": "2026-09-01T00:00:00.000Z"
      }
    ],
    "skipped": [
      {
        "targetId": "string",
        "reason": "not_found"
      }
    ]
  }
}
GET/v1/campaigns/{id}

Uma campanha e suas tarefas

A campanha e todas as tarefas dela. Grátis.

Resposta200

CampoTipoDescrição
successenumValores: true
dataCampaignDetail
└campaignCampaign
└campaignIdstring
└productIdstring
└namestring
└statusenumcompleted é calculado: todas as tarefas estão publicadas, verificadas, com falha ou ignoradas.Valores: activecompletedarchived
└quotaintegerCom quantos canais a campanha foi criada.
└startsAtstring
└endsAtaceita nullstring
└countsobject
└totalinteger
└submittedintegersubmitted + published + verified.
└liveintegerpublished + verified.
└blockedinteger
└doneinteger
└byStatusobject
└createdAtstring
└tasksarray<Task>
└taskIdstring
└campaignIdstring
└productIdstring
└statusenumpublished é o que você informou. verified é o que o QueryWin viu na página da listagem (um link, no caso de diretórios e listas de ferramentas de IA; uma menção, nos demais). Mantenha os dois separados ao reportar.Valores: plannedpreparedin_progressblockedsubmittedpublishedverifiedfailedskipped
└blockedReasonaceita nullenumValores: logincaptchapaymentmissing_materialothernull
└missingarray<string>Conteúdos obrigatórios que faltam no perfil. Complete o perfil no painel; a tarefa se prepara de novo sozinha.
└listingUrlaceita nullstring
└markedByenumQuem fez a última mudança de status: uma pessoa (ou esta API), a extensão do navegador ou o próprio QueryWin.Valores: userdevicesystem
└hasGeneratedbooleanExiste uma reescrita específica para este canal.
└reviewDueAtaceita nullstringQuando voltar a conferir depois do envio (submittedAt + os dias de análise do canal).
└submittedAtaceita nullstring
└publishedAtaceita nullstring
└verifiedAtaceita nullstring
└nextarray<string>Status que você pode definir a partir do atual, via POST /v1/tasks/{id}/status.
└targetobject
└targetIdstring
└namestring
└urlstring
└submitUrlstring
└kindstring
└sourceenumValores: seeduserrivals
└languagestring
└requiresBacklinkboolean
└checkaceita nullobjectA verificação que o QueryWin faz na listagem depois da publicação. É null até a tarefa ser publicada.
└kindenumO que é procurado: um link para o produto (diretórios, listas de ferramentas de IA) ou uma menção à marca (todos os demais).Valores: link_livemention_seen
└stateaceita nullenumconfirmed = encontrado. unconfirmed = não encontrado em uma rodada de verificações (status inalterado; confira a URL). lost = estava lá e sumiu (tarefa marcada como falha).Valores: confirmedunconfirmedlostnull
└checkedAtaceita nullstring
└dueAtaceita nullstring
└updatedAtstring
Erros possíveis
401Falha na autenticação
404campaign_not_found

Exemplo

bash
curl "https://www.querywin.com/api/v1/campaigns/{id}" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "campaign": {
      "campaignId": "string",
      "productId": "string",
      "name": "string",
      "status": "active",
      "quota": 0,
      "startsAt": "2026-09-01T00:00:00.000Z",
      "endsAt": "2026-09-01T00:00:00.000Z",
      "counts": {
        "total": 0,
        "submitted": 0,
        "live": 0,
        "blocked": 0,
        "done": 0,
        "byStatus": null
      },
      "createdAt": "2026-09-01T00:00:00.000Z"
    },
    "tasks": [
      {
        "taskId": "string",
        "campaignId": "string",
        "productId": "string",
        "status": "planned",
        "blockedReason": "login",
        "missing": [
          "string"
        ],
        "listingUrl": "string",
        "markedBy": "user",
        "hasGenerated": true,
        "reviewDueAt": "2026-09-01T00:00:00.000Z",
        "submittedAt": "2026-09-01T00:00:00.000Z",
        "publishedAt": "2026-09-01T00:00:00.000Z",
        "verifiedAt": "2026-09-01T00:00:00.000Z",
        "next": [
          "string"
        ],
        "target": {
          "targetId": "string",
          "name": "string",
          "url": "string",
          "submitUrl": "string",
          "kind": "string",
          "source": "seed",
          "language": "string",
          "requiresBacklink": true
        },
        "check": {
          "kind": "link_live",
          "state": "confirmed",
          "checkedAt": "2026-09-01T00:00:00.000Z",
          "dueAt": "2026-09-01T00:00:00.000Z"
        },
        "updatedAt": "2026-09-01T00:00:00.000Z"
      }
    ]
  }
}
GET/v1/channels

Onde um produto pode ser enviado, por ordem de relevância

O catálogo de canais (diretórios, plataformas de lançamento, listas de ferramentas de IA, comunidades, plataformas de publicação), os canais que você adicionou e — quando productId é informado — os sites que as respostas de IA já citam para as buscas desse produto (source: "rivals"). Com productId, a lista vem ordenada por relevância e cada canal traz taskStatus (não nulo quando o produto já tem uma tarefa nesse canal). Grátis.

**citedByAi significa que respostas de IA citaram esse site ao responder às suas buscas. Não significa que o site vai listar seu produto** — pedir isso é justamente o papel da tarefa.

Parâmetros de consulta

CampoTipoDescrição
productIdstringOrdena por relevância para este produto, inclui taskStatus e adiciona os candidatos citados pela IA.
kindstringTipo de canal.Valores: directorylaunchai_directorycommunitycontentother
sourcestringseed = o catálogo, user = adicionado por você, rivals = sites citados pela IA para este produto.Valores: seeduserrivals
pricingstringCusto do envio.Valores: freeconditionalpaidunknown
submitMethodstringComo é feito o envio: preencher um formulário, publicar em uma comunidade ou mandar um e-mail de apresentação.Valores: formpostemail
qstringBusca por nome, domínio e temas.
hideSubmittedbooleanDeixa de fora os canais em que este produto já tem uma tarefa aberta ou enviada. Exige productId.
pageintegerPadrão: 1.
pageSizeintegerPadrão: 30.

Resposta200

CampoTipoDescrição
successenumValores: true
dataChannelList
└productIdaceita nullstring
└itemsarray<Channel>
└targetIdstringPasse estes valores como targetIds para POST /v1/campaigns.
└namestring
└urlstring
└submitUrlstringO formulário de envio ou a página de publicação; para sites citados pela IA, a página que as respostas de IA mais citam.
└kindenumValores: directorylaunchai_directorycommunitycontentother
└submitMethodenumform = preencher o formulário de envio, post = publicar você mesmo na comunidade, email = mandar um e-mail de apresentação aos editores (o endereço é lido na página quando você a abre; ele não é armazenado).Valores: formpostemail
└sourceenumseed = o catálogo, user = adicionado por você, rivals = um site que as respostas de IA citam para as buscas deste produto.Valores: seeduserrivals
└pricingTypeenumValores: freeconditionalpaidunknown
└priceNoteaceita nullstring
└languagestringen, zh, multi ou, para sites citados pela IA, um código de idioma detectado a partir das buscas.
└topicsarray<string>
└requiresAccountboolean
└requiresBacklinkboolean
└reviewDaysaceita nullintegerTempo típico de análise. A tarefa lembra você de voltar a conferir depois desse prazo.
└siteRankaceita nullintegerPosição global na lista pública Tranco (quanto menor, mais visitado). null = fora do primeiro milhão. Não é o Domain Rating.
└hasFormSpecbooleanOs campos do formulário deste canal estão cadastrados, então os conteúdos são cortados exatamente nos limites dele.
└relevanceaceita nullintegerPontuação de relevância para o produto indicado na requisição. Serve só para ordenar.
└taskStatusaceita nullstringA tarefa aberta ou concluída do produto neste canal, se houver. null = nenhuma ainda.
└citedByAiaceita nullobjectApenas para source: "rivals". Respostas de IA citaram este site para as buscas listadas. Isso não é uma promessa de que o site vai listar seu produto — pedir isso é justamente o papel da tarefa.
└queriesintegerPara quantas buscas distintas ele foi citado.
└samplesintegerQuantas amostras de respostas de IA o citaram.
└searchesarray<string>
└pagesarray<string>As páginas citadas, das mais citadas para as menos citadas. Vazio em amostras antigas, que só registravam o domínio.
└totalinteger
└pageinteger
└pageSizeinteger
└limitedbooleanPlano gratuito: são retornados apenas os primeiros window canais na ordem padrão (maior relevância para productId), e os parâmetros de filtro, busca e ordenação são recusados com 403 library_locked. total continua sendo o tamanho completo do catálogo.
└windowaceita nullintegerTamanho da janela do plano gratuito: 20 canais, mais 20 para cada pessoa que se cadastrou com seu link de indicação (e mais 20 se o seu próprio cadastro veio de um link de indicação). null quando a lista não é limitada.
Erros possíveis
401Falha na autenticação

Exemplo

bash
curl "https://www.querywin.com/api/v1/channels" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "productId": "string",
    "items": [
      {
        "targetId": "string",
        "name": "string",
        "url": "string",
        "submitUrl": "string",
        "kind": "directory",
        "submitMethod": "form",
        "source": "seed",
        "pricingType": "free",
        "priceNote": "string",
        "language": "string",
        "topics": [
          "string"
        ],
        "requiresAccount": true,
        "requiresBacklink": true,
        "reviewDays": 0,
        "siteRank": 0,
        "hasFormSpec": true,
        "relevance": 0,
        "taskStatus": "string",
        "citedByAi": {
          "queries": 0,
          "samples": 0,
          "searches": [],
          "pages": []
        }
      }
    ],
    "total": 0,
    "page": 0,
    "pageSize": 0,
    "limited": true,
    "window": 0
  }
}
GET/v1/products

Seus produtos e o quanto cada perfil está preenchido

Ponto de partida do fluxo de divulgação: todos os outros endpoints de divulgação recebem um productId. completeness e missing vêm do perfil do produto salvo: um campo vazio ali é um campo vazio em todos os envios, e um campo obrigatório faltando trava a tarefa como blocked / missing_material até o perfil ser completado. Leia o perfil com GET /v1/products/{id} e preencha-o com PATCH /v1/products/{id}. Grátis.

Resposta200

CampoTipoDescrição
successenumValores: true
dataProductList
└productsarray<Product>
└productIdstring
└namestring
└urlstring
└domainstring
└primaryLanguageenumValores: enzhjakodefresptitrunlpltrarthviid
└topicsarray<string>
└completenessintegerPreenchimento do perfil, de 0 a 100. Preencha os campos faltantes no painel ou com PATCH /v1/products/{id}.
└missingarray<string>Campos do perfil que estão vazios. Cada um é uma lacuna em todos os envios.
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughaceita nullstring
Erros possíveis
401Falha na autenticação

Exemplo

bash
curl "https://www.querywin.com/api/v1/products" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "products": [
      {
        "productId": "string",
        "name": "string",
        "url": "string",
        "domain": "string",
        "primaryLanguage": "en",
        "topics": [
          "string"
        ],
        "completeness": 0,
        "missing": [
          "string"
        ],
        "sites": [
          {
            "siteId": null,
            "domain": null,
            "syncedThrough": null
          }
        ]
      }
    ]
  }
}
POST/v1/products

Criar um produto

Exige a permissão publish e um plano pago. Cria um produto a partir da URL pública da página inicial e de um nome opcional; ocupa uma vaga da sua cota de produtos, sem consumir créditos. Retorna o perfil completo salvo, que você preenche depois com PATCH /v1/products/{id}. Não rastreia o site nem aciona IA. Um domínio já cadastrado retorna 409 product_exists com o productId existente; reutilize-o se a resposta tiver se perdido. Os valores padrão do produto são editáveis, não fatos verificados sobre o site.

Corpo da requisição

CampoTipoDescrição
urlobrigatóriostring
namestring

Resposta201

CampoTipoDescrição
successboolean
dataProductDetail
└namestring
└urlstring
└primaryLanguageenumValores: enzhjakodefresptitrunlpltrarthviid
└businessTypeenumValores: saastoolecommercecontentserviceother
└launchStatusenumValores: livebeta
└pricingModelenumValores: freefreemiumpaidtrial
└taglineaceita nullobject
└shortDescaceita nullobject
└longDescaceita nullobject
└firstCommentaceita nullobject
└topicsarray<string>
└promoCodeaceita nullstring
└videoUrlaceita nullstring
└demoUrlaceita nullstring
└linksaceita nullobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
└socialsaceita nullobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
└contactNameaceita nullstring
└contactEmailaceita nullstring
└galleryarray<string>
└productIdstring
└domainstring
└thumbnailUrlaceita nullstring
└completenessinteger (0–100)
└missingarray<string>
└createdAtstring
└updatedAtstring
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughaceita nullstring
Erros possíveis
400invalid_product_input, invalid_url ou url_not_public
401Falha na autenticação
403plan_required ou, em operações de escrita, missing_scope_publish
409product_exists (com o productId existente) ou product_limit_reached
429rate_limited — 120 requisições por minuto por chave

Exemplo

bash
curl -X POST https://www.querywin.com/api/v1/products \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "name": "Example"
  }'
json — resposta
{
  "success": true,
  "data": {
    "name": "string",
    "url": "string",
    "primaryLanguage": "en",
    "businessType": "saas",
    "launchStatus": "live",
    "pricingModel": "free",
    "tagline": null,
    "shortDesc": null,
    "longDesc": null,
    "firstComment": null,
    "topics": [
      "string"
    ],
    "promoCode": "string",
    "videoUrl": "string",
    "demoUrl": "string",
    "links": {
      "webApp": "string",
      "appStore": "string",
      "playStore": "string",
      "chromeExtension": "string",
      "macos": "string",
      "windows": "string"
    },
    "socials": {
      "x": "string",
      "linkedin": "string",
      "github": "string",
      "instagram": "string",
      "youtube": "string",
      "facebook": "string"
    },
    "contactName": "string",
    "contactEmail": "string",
    "gallery": [
      "string"
    ],
    "productId": "string",
    "domain": "string",
    "thumbnailUrl": "string",
    "completeness": 0,
    "missing": [
      "string"
    ],
    "createdAt": "2026-09-01T00:00:00.000Z",
    "updatedAt": "2026-09-01T00:00:00.000Z",
    "sites": [
      {
        "siteId": "string",
        "domain": "string",
        "syncedThrough": "string"
      }
    ]
  }
}
PATCH/v1/products/{id}

Preencher ou alterar o perfil de um produto

Exige a permissão publish. Salva na hora apenas os campos enviados; objetos (incluindo os textos localizados) e arrays substituem o campo inteiro. Leia o perfil antes para preservar os outros idiomas. Não consome créditos nem gera nada com IA. Os conteúdos de envio pendentes são atualizados de forma assíncrona. Use só fatos conhecidos; não invente informações do produto que estejam faltando.

Corpo da requisição

CampoTipoDescrição
namestring
urlstring
primaryLanguageenumValores: enzhjakodefresptitrunlpltrarthviid
businessTypeenumValores: saastoolecommercecontentserviceother
launchStatusenumValores: livebeta
pricingModelenumValores: freefreemiumpaidtrial
taglineobject
shortDescobject
longDescobject
firstCommentobject
topicsarray<string>
promoCodestring
videoUrlstring
demoUrlstring
linksobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
socialsobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
contactNamestring
contactEmailstring
galleryarray<string>

Resposta200

CampoTipoDescrição
successboolean
dataProductDetail
└namestring
└urlstring
└primaryLanguageenumValores: enzhjakodefresptitrunlpltrarthviid
└businessTypeenumValores: saastoolecommercecontentserviceother
└launchStatusenumValores: livebeta
└pricingModelenumValores: freefreemiumpaidtrial
└taglineaceita nullobject
└shortDescaceita nullobject
└longDescaceita nullobject
└firstCommentaceita nullobject
└topicsarray<string>
└promoCodeaceita nullstring
└videoUrlaceita nullstring
└demoUrlaceita nullstring
└linksaceita nullobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
└socialsaceita nullobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
└contactNameaceita nullstring
└contactEmailaceita nullstring
└galleryarray<string>
└productIdstring
└domainstring
└thumbnailUrlaceita nullstring
└completenessinteger (0–100)
└missingarray<string>
└createdAtstring
└updatedAtstring
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughaceita nullstring
Erros possíveis
400invalid_product_input, invalid_url, url_not_public, invalid_email ou invalid_gallery
401Falha na autenticação
403plan_required ou, em operações de escrita, missing_scope_publish
404product_not_found
409product_exists (com o productId existente)
429rate_limited — 120 requisições por minuto por chave

Exemplo

bash
curl -X PATCH https://www.querywin.com/api/v1/products/{id} \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tagline": {
      "en": "Your product tagline",
      "zh": "你的产品标语"
    },
    "shortDesc": {
      "en": "A factual description of the product."
    },
    "topics": [
      "productivity",
      "developer-tools"
    ],
    "contactEmail": "hello@example.com"
  }'
json — resposta
{
  "success": true,
  "data": {
    "name": "string",
    "url": "string",
    "primaryLanguage": "en",
    "businessType": "saas",
    "launchStatus": "live",
    "pricingModel": "free",
    "tagline": null,
    "shortDesc": null,
    "longDesc": null,
    "firstComment": null,
    "topics": [
      "string"
    ],
    "promoCode": "string",
    "videoUrl": "string",
    "demoUrl": "string",
    "links": {
      "webApp": "string",
      "appStore": "string",
      "playStore": "string",
      "chromeExtension": "string",
      "macos": "string",
      "windows": "string"
    },
    "socials": {
      "x": "string",
      "linkedin": "string",
      "github": "string",
      "instagram": "string",
      "youtube": "string",
      "facebook": "string"
    },
    "contactName": "string",
    "contactEmail": "string",
    "gallery": [
      "string"
    ],
    "productId": "string",
    "domain": "string",
    "thumbnailUrl": "string",
    "completeness": 0,
    "missing": [
      "string"
    ],
    "createdAt": "2026-09-01T00:00:00.000Z",
    "updatedAt": "2026-09-01T00:00:00.000Z",
    "sites": [
      {
        "siteId": "string",
        "domain": "string",
        "syncedThrough": "string"
      }
    ]
  }
}
GET/v1/products/{id}

Ler o perfil completo de um produto

Retorna os textos localizados salvos, os links, os dados de contato, as imagens, o nível de preenchimento e os campos faltantes. Permissão read; não consome créditos. Um produto que não pertence a este espaço de trabalho também retorna product_not_found.

Resposta200

CampoTipoDescrição
successboolean
dataProductDetail
└namestring
└urlstring
└primaryLanguageenumValores: enzhjakodefresptitrunlpltrarthviid
└businessTypeenumValores: saastoolecommercecontentserviceother
└launchStatusenumValores: livebeta
└pricingModelenumValores: freefreemiumpaidtrial
└taglineaceita nullobject
└shortDescaceita nullobject
└longDescaceita nullobject
└firstCommentaceita nullobject
└topicsarray<string>
└promoCodeaceita nullstring
└videoUrlaceita nullstring
└demoUrlaceita nullstring
└linksaceita nullobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
└socialsaceita nullobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
└contactNameaceita nullstring
└contactEmailaceita nullstring
└galleryarray<string>
└productIdstring
└domainstring
└thumbnailUrlaceita nullstring
└completenessinteger (0–100)
└missingarray<string>
└createdAtstring
└updatedAtstring
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughaceita nullstring
Erros possíveis
401Falha na autenticação
403plan_required ou, em operações de escrita, missing_scope_publish
404product_not_found
429rate_limited — 120 requisições por minuto por chave

Exemplo

bash
curl "https://www.querywin.com/api/v1/products/{id}" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "name": "string",
    "url": "string",
    "primaryLanguage": "en",
    "businessType": "saas",
    "launchStatus": "live",
    "pricingModel": "free",
    "tagline": null,
    "shortDesc": null,
    "longDesc": null,
    "firstComment": null,
    "topics": [
      "string"
    ],
    "promoCode": "string",
    "videoUrl": "string",
    "demoUrl": "string",
    "links": {
      "webApp": "string",
      "appStore": "string",
      "playStore": "string",
      "chromeExtension": "string",
      "macos": "string",
      "windows": "string"
    },
    "socials": {
      "x": "string",
      "linkedin": "string",
      "github": "string",
      "instagram": "string",
      "youtube": "string",
      "facebook": "string"
    },
    "contactName": "string",
    "contactEmail": "string",
    "gallery": [
      "string"
    ],
    "productId": "string",
    "domain": "string",
    "thumbnailUrl": "string",
    "completeness": 0,
    "missing": [
      "string"
    ],
    "createdAt": "2026-09-01T00:00:00.000Z",
    "updatedAt": "2026-09-01T00:00:00.000Z",
    "sites": [
      {
        "siteId": "string",
        "domain": "string",
        "syncedThrough": "string"
      }
    ]
  }
}
POST/v1/products/{id}/images/from-url

Importar uma imagem do produto a partir de uma URL

Exige a permissão publish. Baixa uma imagem pública via HTTP(S) e armazena uma cópia como miniatura (substituindo a atual) ou na galeria (adicionada ao final, no máximo seis). JPEG, PNG, WebP e SVG, até 4 MB; SVG é convertido em PNG. Redes privadas e redirecionamentos inseguros são bloqueados pelo módulo de download de imagens já existente. Não consome créditos. Importações repetidas na galeria podem gerar duplicatas: se a resposta se perder, consulte o produto com GET antes de tentar de novo.

Corpo da requisição

CampoTipoDescrição
urlobrigatóriostring
kindenumValores: thumbnailgalleryPadrão gallery

Resposta200

CampoTipoDescrição
successboolean
dataProductDetail
└namestring
└urlstring
└primaryLanguageenumValores: enzhjakodefresptitrunlpltrarthviid
└businessTypeenumValores: saastoolecommercecontentserviceother
└launchStatusenumValores: livebeta
└pricingModelenumValores: freefreemiumpaidtrial
└taglineaceita nullobject
└shortDescaceita nullobject
└longDescaceita nullobject
└firstCommentaceita nullobject
└topicsarray<string>
└promoCodeaceita nullstring
└videoUrlaceita nullstring
└demoUrlaceita nullstring
└linksaceita nullobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
└socialsaceita nullobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
└contactNameaceita nullstring
└contactEmailaceita nullstring
└galleryarray<string>
└productIdstring
└domainstring
└thumbnailUrlaceita nullstring
└completenessinteger (0–100)
└missingarray<string>
└createdAtstring
└updatedAtstring
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughaceita nullstring
Erros possíveis
400invalid_product_input ou invalid_url
401Falha na autenticação
403plan_required ou, em operações de escrita, missing_scope_publish
404product_not_found
409gallery_full
413image_too_large
415image_type
429rate_limited — 120 requisições por minuto por chave
502image_unreachable
503storage_unavailable

Exemplo

bash
curl -X POST https://www.querywin.com/api/v1/products/{id}/images/from-url \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/logo.png",
    "kind": "thumbnail"
  }'
json — resposta
{
  "success": true,
  "data": {
    "name": "string",
    "url": "string",
    "primaryLanguage": "en",
    "businessType": "saas",
    "launchStatus": "live",
    "pricingModel": "free",
    "tagline": null,
    "shortDesc": null,
    "longDesc": null,
    "firstComment": null,
    "topics": [
      "string"
    ],
    "promoCode": "string",
    "videoUrl": "string",
    "demoUrl": "string",
    "links": {
      "webApp": "string",
      "appStore": "string",
      "playStore": "string",
      "chromeExtension": "string",
      "macos": "string",
      "windows": "string"
    },
    "socials": {
      "x": "string",
      "linkedin": "string",
      "github": "string",
      "instagram": "string",
      "youtube": "string",
      "facebook": "string"
    },
    "contactName": "string",
    "contactEmail": "string",
    "gallery": [
      "string"
    ],
    "productId": "string",
    "domain": "string",
    "thumbnailUrl": "string",
    "completeness": 0,
    "missing": [
      "string"
    ],
    "createdAt": "2026-09-01T00:00:00.000Z",
    "updatedAt": "2026-09-01T00:00:00.000Z",
    "sites": [
      {
        "siteId": "string",
        "domain": "string",
        "syncedThrough": "string"
      }
    ]
  }
}
GET/v1/tasks

Tarefas de todas as campanhas

O histórico de envios, com a atividade mais recente primeiro. Filtre por produto, por campanha ou por uma lista de status separados por vírgula. byStatus conta todas as tarefas da consulta antes de aplicar o filtro de status. Grátis.

Parâmetros de consulta

CampoTipoDescrição
productIdstringApenas as tarefas deste produto.
campaignIdstring
statusstringSeparados por vírgula, por exemplo prepared,in_progress.
pageintegerPadrão: 1.
pageSizeintegerPadrão: 30.

Resposta200

CampoTipoDescrição
successenumValores: true
dataTaskList
└itemsarray<Task>
└taskIdstring
└campaignIdstring
└productIdstring
└statusenumpublished é o que você informou. verified é o que o QueryWin viu na página da listagem (um link, no caso de diretórios e listas de ferramentas de IA; uma menção, nos demais). Mantenha os dois separados ao reportar.Valores: plannedpreparedin_progressblockedsubmittedpublishedverifiedfailedskipped
└blockedReasonaceita nullenumValores: logincaptchapaymentmissing_materialothernull
└missingarray<string>Conteúdos obrigatórios que faltam no perfil. Complete o perfil no painel; a tarefa se prepara de novo sozinha.
└listingUrlaceita nullstring
└markedByenumQuem fez a última mudança de status: uma pessoa (ou esta API), a extensão do navegador ou o próprio QueryWin.Valores: userdevicesystem
└hasGeneratedbooleanExiste uma reescrita específica para este canal.
└reviewDueAtaceita nullstringQuando voltar a conferir depois do envio (submittedAt + os dias de análise do canal).
└submittedAtaceita nullstring
└publishedAtaceita nullstring
└verifiedAtaceita nullstring
└nextarray<string>Status que você pode definir a partir do atual, via POST /v1/tasks/{id}/status.
└targetobject
└targetIdstring
└namestring
└urlstring
└submitUrlstring
└kindstring
└sourceenumValores: seeduserrivals
└languagestring
└requiresBacklinkboolean
└checkaceita nullobjectA verificação que o QueryWin faz na listagem depois da publicação. É null até a tarefa ser publicada.
└kindenumO que é procurado: um link para o produto (diretórios, listas de ferramentas de IA) ou uma menção à marca (todos os demais).Valores: link_livemention_seen
└stateaceita nullenumconfirmed = encontrado. unconfirmed = não encontrado em uma rodada de verificações (status inalterado; confira a URL). lost = estava lá e sumiu (tarefa marcada como falha).Valores: confirmedunconfirmedlostnull
└checkedAtaceita nullstring
└dueAtaceita nullstring
└updatedAtstring
└totalinteger
└pageinteger
└pageSizeinteger
└byStatusobject
Erros possíveis
401Falha na autenticação

Exemplo

bash
curl "https://www.querywin.com/api/v1/tasks" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "items": [
      {
        "taskId": "string",
        "campaignId": "string",
        "productId": "string",
        "status": "planned",
        "blockedReason": "login",
        "missing": [
          "string"
        ],
        "listingUrl": "string",
        "markedBy": "user",
        "hasGenerated": true,
        "reviewDueAt": "2026-09-01T00:00:00.000Z",
        "submittedAt": "2026-09-01T00:00:00.000Z",
        "publishedAt": "2026-09-01T00:00:00.000Z",
        "verifiedAt": "2026-09-01T00:00:00.000Z",
        "next": [
          "string"
        ],
        "target": {
          "targetId": "string",
          "name": "string",
          "url": "string",
          "submitUrl": "string",
          "kind": "string",
          "source": "seed",
          "language": "string",
          "requiresBacklink": true
        },
        "check": {
          "kind": "link_live",
          "state": "confirmed",
          "checkedAt": "2026-09-01T00:00:00.000Z",
          "dueAt": "2026-09-01T00:00:00.000Z"
        },
        "updatedAt": "2026-09-01T00:00:00.000Z"
      }
    ],
    "total": 0,
    "page": 0,
    "pageSize": 0,
    "byStatus": null
  }
}
GET/v1/tasks/{id}

Uma tarefa e os conteúdos a enviar

Todos os campos que o formulário do canal pede, extraídos do perfil do produto e cortados nos limites do canal (source: "profile"), mais a reescrita específica para o canal, se houver (source: "ai"), e as edições feitas no painel (source: "override"). source: "none" significa que o perfil não tem nada para aquele campo — não invente. Nunca dispara uma reescrita e nunca custa nada.

Resposta200

CampoTipoDescrição
successenumValores: true
dataTaskDetailResult
└taskTaskDetail
Erros possíveis
401Falha na autenticação
404task_not_found

Exemplo

bash
curl "https://www.querywin.com/api/v1/tasks/{id}" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — resposta
{
  "success": true,
  "data": {
    "task": null
  }
}
POST/v1/tasks/{id}/materials

Reescrever os conteúdos para este canal (consome créditos)

Uma rodada de IA que adapta o perfil a este canal específico: uma descrição mais enxuta para o diretório, o primeiro comentário do criador, uma publicação na comunidade, um e-mail de apresentação para um editor ou — para sites citados pela IA — um e-mail de apresentação com um parágrafo que o responsável pela página poderia acrescentar. A chamada é síncrona e leva alguns segundos. Usa apenas fatos do perfil; qualquer parte que contenha um link ausente do perfil é descartada e não é cobrada.

Entradas idênticas retornam a versão anterior sem cobrança (cached: true); force: true reescreve mesmo assim e é cobrado. Os conteúdos do perfil retornados por GET /v1/tasks/{id} costumam bastar para diretórios; reescreva quando o canal pedir outro tom.

Retorna HTTP 200 com ok: false e failure: "engine_failed" quando nada passa na validação. Já a falta de créditos retorna um 402 de verdade.

Corpo da requisição

CampoTipoDescrição
confirmSpendobrigatóriointegerTeto de autorização em créditos, com a mesma semântica das estruturas e dos rascunhos. Consulte o preço em GET /v1/usage (materials.pricePerTask). Obrigatório mesmo enquanto restarem gerações gratuitas. (min 0)
forcebooleanReescreve mesmo que nada tenha mudado desde a última versão. É cobrado.

Resposta200

CampoTipoDescrição
successenumValores: true
dataWriteMaterialsOutcome
└okboolean
└failureenumPresente quando ok é false. Nada é cobrado.Valores: engine_failedengine_unavailable
└cachedbooleanAs entradas não mudaram: a versão anterior foi retornada e nada foi cobrado.
└freeUsedboolean
└creditsSpentinteger
└taskTaskDetail
Erros possíveis
400confirm_spend_required ou confirm_spend_too_low (o corpo traz o price atual)
401Falha na autenticação
402insufficient_credits — o corpo traz requiredCredits, currentBalance, shortfall
403missing_scope_spend — esta chave não recebeu a permissão spend
404task_not_found
429rate_limited ou daily_limit_reached (o corpo traz resetAt)

Exemplo

bash
curl -X POST https://www.querywin.com/api/v1/tasks/{id}/materials \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "confirmSpend": 10
  }'
json — resposta
{
  "success": true,
  "data": {
    "ok": true,
    "failure": "engine_failed",
    "cached": true,
    "freeUsed": true,
    "creditsSpent": 0,
    "task": null
  }
}
POST/v1/tasks/{id}/status

Informar o que aconteceu com o envio

Registre o resultado de um envio que você fez com suas próprias contas. O QueryWin nunca envia nada por conta própria. Defina submitted assim que o formulário for enviado e depois published com a URL da listagem (a própria entrada ou publicação, não a página inicial do site) quando ela entrar no ar; cerca de 72 horas depois, o QueryWin verifica novamente essa página em busca de um link para o seu produto (diretórios, listas de ferramentas de IA) ou de uma menção (todos os demais) e define verified por conta própria — você não pode definir esse status.

blocked quer dizer “precisa de uma pessoa”: informe reason (login, captcha, payment, missing_material, other). failed / skipped encerram a tarefa; prepared a devolve ao estado de preparada. Transições que a máquina de estados não permite retornam **409 transition_not_allowed**; o campo next da tarefa lista o que é permitido a partir do status atual.

Corpo da requisição

CampoTipoDescrição
statusobrigatórioenumverified não pode ser definido: o QueryWin o define depois de verificar novamente a listagem.Valores: preparedin_progressblockedsubmittedpublishedfailedskipped
listingUrlstringObrigatório para published: a entrada ou publicação no ar, não a página inicial do site. Somente http/https. Opcional com submitted, se você já souber.
notestring
reasonenumObrigatório para blocked.Valores: logincaptchapaymentmissing_materialother

Resposta200

CampoTipoDescrição
successenumValores: true
dataTaskDetailResult
└taskTaskDetail
Erros possíveis
400invalid_status, listing_url_required (published exige listingUrl), invalid_listing_url ou reason_required (blocked exige reason)
401Falha na autenticação
403missing_scope_publish — esta chave não recebeu a permissão publish
404task_not_found
409transition_not_allowed — leia a tarefa: next lista os status permitidos

Exemplo

bash
curl -X POST https://www.querywin.com/api/v1/tasks/{id}/status \
  -H "Authorization: Bearer $QUERYWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "published",
    "listingUrl": "https://example-directory.com/tools/your-product"
  }'
json — resposta
{
  "success": true,
  "data": {
    "task": null
  }
}
API do QueryWin — fluxos de conteúdo e divulgação para scripts e agentes de IA