API

Integra QueryWin nella tua pipeline

QueryWin trova le ricerche per cui il tuo sito riceve già impressioni ma non ha ancora una pagina, prepara una bozza dell’articolo mancante e prepara il tuo prodotto per l’invio a directory, piattaforme di lancio, community e siti già citati nelle risposte dell’IA. Questa API mette entrambe le pipeline a disposizione dei tuoi script e degli assistenti IA. La pubblicazione e l’invio avvengono comunque con le tue credenziali e i tuoi account: QueryWin non si connette mai al tuo CMS e non invia mai nulla direttamente.

Base URLhttps://www.querywin.com/apiCrea una chiave APISpec OpenAPI (JSON) →

Avvio rapido

Tre passaggi. Tutto ciò che segue è una semplice chiamata REST con un solo header.

1

Crea una chiave

Nel Dashboard di QueryWin, alla voce API. La chiave in testo semplice viene mostrata una sola volta, al momento della creazione. Concedi solo gli scope necessari: una chiave senza caselle selezionate è di sola lettura.

2

Inviala come token bearer

Authorization: Bearer qw_live_… per ogni richiesta. Funziona anche X-API-Key, per gli strumenti che consentono di impostare un solo header.

3

Leggi cosa scrivere, poi recupera la bozza

L’elenco degli argomenti è gratuito e non richiede crediti. Generare una scaletta o una bozza detrae crediti e richiede lo scope spend.

bash
# 1. Su quali siti può agire questa chiave
curl "https://www.querywin.com/api/v1/sites" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"

# 2. Cosa scrivere questa settimana (gratuito, senza crediti)
curl "https://www.querywin.com/api/v1/topics?siteId=YOUR_SITE_ID" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
bash
# 3. Recupera la bozza (Markdown + JSON-LD con i dati compilati)
curl "https://www.querywin.com/api/v1/topics/article?siteId=YOUR_SITE_ID&key=standard%20wardrobe%20depth" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"

# 4. Il tuo script la pubblica sul tuo blog, poi restituisce l’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"
  }'

Ogni risposta è racchiusa in: {"success": true, "data": …}. Gli errori sono codici leggibili dalle macchine, mai testo discorsivo: QueryWin è bilingue e una frase in inglese codificata nel sistema finirebbe visualizzata su una pagina in cinese.

La pipeline dei contenuti

Cinque passaggi, e solo due hanno un costo. Il passaggio 4 spetta a te: QueryWin ti consegna il markdown e i dati strutturati, e tu li pubblichi.

PassaggioEndpointCosto
Trova cosa ha impressioni ma nessuna paginaGET /v1/topicsGratuito
Genera una scaletta supportata da evidenzePOST /v1/topics/outlineCrediti
Trasformala in una bozza pronta per la pubblicazionePOST /v1/topics/articleCrediti
Pubblicala sul tuo blogil tuo CMS—
Restituisci l’URLPOST /v1/topics/publishedGratuito

QueryWin non si collega mai al tuo CMS, non conserva mai le credenziali del tuo sito e non pubblica mai al posto tuo. Questa API ti consegna i contenuti; la scrittura avviene sul tuo computer, con le tue credenziali. Questo è il confine, non una scappatoia per aggirarlo.

La pipeline della distribuzione

Sette passaggi, e solo uno ha un costo. Il passaggio 6 spetta a te: QueryWin ti consegna il materiale per l’invio a ciascun canale e tu, oppure il tuo agente con i tuoi account, lo invii. Poi QueryWin ricontrolla autonomamente ogni scheda pubblicata.

PassaggioEndpointCosto
Elenca i tuoi prodotti e il livello di completezza di ogni schedaGET /v1/productsGratuito
Elenca dove inviare, ordinando per pertinenza, inclusi i siti citati dalle risposte dell’IAGET /v1/channels?productId=Gratuito
Crea una campagna dai canali sceltiPOST /v1/campaignsGratuito
Recupera il materiale preparato per un’attivitàGET /v1/tasks/{id}Gratuito
Riscrivilo per quel canalePOST /v1/tasks/{id}/materialsCrediti
Invialoi tuoi account—
Comunica che è stato inviato, poi pubblicato, con l’URL della schedaPOST /v1/tasks/{id}/statusGratuito

published indica ciò che hai comunicato; verified indica ciò che QueryWin ha visto ricontrollando la scheda circa 72 ore dopo: un link al tuo prodotto nelle directory e negli elenchi di strumenti IA, una menzione altrove. Sono campi distinti. Un sito citato dalle risposte dell’IA (citedByAi) è un sito a cui vale la pena chiedere; non è una promessa che inserirà la tua scheda.

Scope

Ogni chiave include le autorizzazioni concesse al momento della creazione. GET /v1/usage le comunica, così non devi scoprirle provocando un 403.

read

Sempre attivo

Ogni endpoint GET: siti, lacune dei contenuti, scalette, bozze, prodotti, canali, campagne, attività e relativo materiale, utilizzo.

publish

Disattivato per impostazione predefinita

Crea e aggiorna le schede prodotto, importa immagini e registra ciò che è accaduto: l’URL di un articolo pubblicato (inviato anche a Bing, Yandex, Seznam e Naver, non a Google), una nuova campagna, un’attività contrassegnata come inviata o pubblicata. È gratuito, ma ogni operazione crea un record con conseguenze: le campagne incidono sul tuo piano e una scheda pubblicata viene ricontrollata.

spend

Disattivato per impostazione predefinita

Genera scalette e bozze e riscrive il materiale per l’invio a un canale. Queste operazioni detraranno crediti. Concedilo solo se vuoi che il chiamante, uno script o un assistente IA, possa spendere autonomamente.

Non esiste una gerarchia: publish non implica spend e spend non implica publish. Sono categorie di rischio diverse. Una chiamata per cui la chiave non dispone dello scope restituisce 403 con missing_scope_<name> e gli scope di cui disponi.

Spendere crediti

Due endpoint detraggono crediti: POST /v1/topics/outline e POST /v1/topics/article. Prima di raggiungerli ci sono tre controlli.

ControlloCosa blocca
confirmSpendUno script che continua ad addebitare dopo un aumento del prezzo.
Limite giornalieroUn ciclo fuori controllo che consuma il saldo durante la notte. Restituisce 429 con l’orario di ripristino.
Impronta dell’inputUn doppio addebito per lo stesso argomento. Gli input identici restituiscono gratuitamente il risultato memorizzato nella cache, quindi riprovare una richiesta scaduta è sicuro.

confirmSpend è un tetto di autorizzazione, non un importo esatto. Invia un valore maggiore o uguale al prezzo attuale e ti verrà addebitato il costo effettivo, spesso zero in caso di cache hit. Se il prezzo dovesse superare il tuo tetto, la chiamata fallirà con confirm_spend_too_low invece di addebitare silenziosamente un importo maggiore.

Convenzioni

Tre aspetti validi per ogni endpoint.

Involucro della risposta

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

Gli errori sono codici, mai frasi. Leggili, ma non mostrarli senza modificarli: devono essere associati alle tue formulazioni.

I risultati di business non sono errori

Una chiamata di generazione che non riesce a produrre un risultato valido restituisce HTTP 200 con data.ok = false e un codice data.failure, così puoi distinguerla da un errore di autenticazione o da una connessione interrotta. Non viene addebitato nulla.

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

Limite di frequenza

120 richieste al minuto per chiave. Oltre questo limite ricevi 429 con l’orario di ripristino. È distinto dal limite giornaliero di generazione indicato sopra.

MCP per gli agenti IA

Entrambe le pipeline sono disponibili tramite Model Context Protocol, autenticate con la stessa chiave e lo stesso header. Aggiungilo a Claude Code, Cursor, n8n o a qualsiasi altro strumento che supporti MCP su 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"

Diciannove strumenti. Schede prodotto: create_product, get_product, update_product, import_product_image. Contenuti: list_sites, get_usage, list_content_gaps, get_outline, get_article_draft, generate_outline, generate_article_draft, mark_published. Distribuzione: list_products, list_channels, create_campaign, list_tasks, get_task, write_task_materials, report_task_status. Chiedi al tuo assistente cosa scrivere questa settimana o dove inviare il tuo prodotto e lo scoprirà.

prompt
Usando il server MCP di QueryWin, trova le tre lacune di contenuto più importanti sul mio sito,
mostrami le ricerche alla base di ciascuna e dimmi quanto costerebbe creare la bozza della prima.

Gli strumenti vengono filtrati in base all’ambito. Con una chiave di sola lettura, gli strumenti di generazione non compaiono affatto nell’elenco degli strumenti dell’assistente: un agente non può chiamare uno strumento che non vede. Assegna a un agente una chiave propria, così potrai revocarla senza modificare le altre integrazioni.

L’autenticazione usa un token bearer, consentito dalla specifica MCP, dove l’autorizzazione è facoltativa. I client che permettono di impostare un’intestazione, come Claude Code, Cursor e n8n, si collegano direttamente. Gli host che richiedono una schermata di consenso OAuth potrebbero non riuscirci.

Account

GET/v1/sites

Elenca i siti su cui questa chiave può operare

Inizia da qui. Ogni altro endpoint richiede il siteId restituito da questa chiamata. Se ometti siteId altrove, viene usato il sito collegato per primo: va bene per gli account con un solo sito, ma può causare problemi in tutti gli altri.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataSiteList
└sitesarray<Site>
└siteIdstring
└domainstring
└gscPropertystringLa proprietà di Search Console, riportata senza modifiche: sc-domain:example.com oppure https://example.com/.
└syncStatusenumValori: pendingsyncingdonefailed
└syncedThroughpuò essere nullostringSearch Console presenta un ritardo di 2-3 giorni. Ogni metrica di questo sito è aggiornata “alla data” indicata qui: specificalo se mostri questi numeri altrove.
Possibili errori
401Chiave mancante, non valida, revocata o scaduta

Esempio

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

Prezzi attuali, disponibilità gratuita, saldo crediti e limiti giornalieri

Leggi queste informazioni prima di generare qualsiasi cosa. Sono la stessa fonte utilizzata dall’interfaccia web per decidere cosa mostrare sul pulsante: la disponibilità viene stabilita dal server, non tentando un’operazione che poi fallisce.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataUsage
└scopesarray<enum>Le operazioni consentite a questa chiave. Leggile una volta all’avvio, invece di scoprire i permessi tentando chiamate che restituiscono 403.Valori: readpublishspend
└creditsobject
└balanceinteger
└outlineStepUsage
└availablebooleanFalse quando il motore di generazione non è configurato. Non chiamare l’endpoint POST.
└pricePerOutlineintegerCrediti per scaletta, presente solo su outline.
└pricePerArticleintegerCrediti per bozza, presente solo su article.
└pricePerTaskintegerCrediti per riscrittura del canale, presente solo su materials.
└freeRemainingintegerGenerazioni gratuite rimaste su questo account, conteggiate per argomento distinto (scalette, bozze) o per task distinta (materiali), non per numero di clic. Le generazioni gratuite richiedono comunque confirmSpend.
└dailyDailyLimitUn limite di sicurezza contro gli script fuori controllo, conteggiato nel database su tutti i canali (anche l’interfaccia web contribuisce). Si azzera a mezzanotte locale, non su una finestra mobile.
└usedinteger
└limitinteger
└remaininginteger
└resetAtstring
└articleStepUsage
└availablebooleanFalse quando il motore di generazione non è configurato. Non chiamare l’endpoint POST.
└pricePerOutlineintegerCrediti per scaletta, presente solo su outline.
└pricePerArticleintegerCrediti per bozza, presente solo su article.
└pricePerTaskintegerCrediti per riscrittura del canale, presente solo su materials.
└freeRemainingintegerGenerazioni gratuite rimaste su questo account, conteggiate per argomento distinto (scalette, bozze) o per task distinta (materiali), non per numero di clic. Le generazioni gratuite richiedono comunque confirmSpend.
└dailyDailyLimitUn limite di sicurezza contro gli script fuori controllo, conteggiato nel database su tutti i canali (anche l’interfaccia web contribuisce). Si azzera a mezzanotte locale, non su una finestra mobile.
└usedinteger
└limitinteger
└remaininginteger
└resetAtstring
└materialsStepUsage
└availablebooleanFalse quando il motore di generazione non è configurato. Non chiamare l’endpoint POST.
└pricePerOutlineintegerCrediti per scaletta, presente solo su outline.
└pricePerArticleintegerCrediti per bozza, presente solo su article.
└pricePerTaskintegerCrediti per riscrittura del canale, presente solo su materials.
└freeRemainingintegerGenerazioni gratuite rimaste su questo account, conteggiate per argomento distinto (scalette, bozze) o per task distinta (materiali), non per numero di clic. Le generazioni gratuite richiedono comunque confirmSpend.
└dailyDailyLimitUn limite di sicurezza contro gli script fuori controllo, conteggiato nel database su tutti i canali (anche l’interfaccia web contribuisce). Si azzera a mezzanotte locale, non su una finestra mobile.
└usedinteger
└limitinteger
└remaininginteger
└resetAtstring
└distributionDistributionQuotaI limiti di distribuzione del piano e l’utilizzo attuale. Un limite null indica che non è previsto alcun limite.
└planstring
└limitsobject
└channelspuò essere nullointegerNumero di canali distinti per i quali un prodotto può avere task, conteggiati nell’arco di channelsPeriod.
└channelsPeriodenummonth (piani a pagamento): conteggio per mese solare (UTC), quindi ogni mese consente un nuovo gruppo di canali. total (piano Gratuito): conteggio per tutta la durata del prodotto.Valori: monthtotal
└tasksPerMonthpuò essere nullointegerTask che possono essere creati per mese solare (UTC), per tutti i prodotti.
└activeCampaignspuò essere nullointegerCampagne che possono essere attive contemporaneamente.
└usageobject
└activeCampaignsinteger
└tasksThisMonthinteger
└channelspuò essere nullointegerSolo quando la richiesta specifica un prodotto.
Possibili errori
401Autenticazione non riuscita

Esempio

bash
curl "https://www.querywin.com/api/v1/usage" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

Ricerche con impressioni ma senza una pagina dedicata

Gratuito, senza fatturazione esterna (l’API richiede comunque un piano a pagamento). Viene calcolato da zero a ogni chiamata usando i tuoi dati di Google Search Console: non viene usato alcun database esterno di parole chiave. Ed è proprio questo il punto: uno strumento per parole chiave non può dirti che “hai già impressioni e non hai una pagina dedicata”.

I risultati sono ordinati per opportunità. I topic che hai ignorato nell’interfaccia sono esclusi.

Parametri di ricerca

CampoTipoDescrizione
siteIdstringDa GET /v1/sites. Per impostazione predefinita usa il primo sito collegato.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataTopicList
└siteIdpuò essere nullostring
└topicsarray<Topic>
└keystringLa chiave del cluster: passala come key a tutti gli altri endpoint dei topic. È il testo normalizzato della ricerca rappresentativa, quindi può contenere spazi, barre e caratteri non latini. Inviala sempre nella stringa di query o nel corpo, mai nel percorso dell’URL.
└titlestringLa ricerca con più impressioni nel cluster, riportata alla lettera. Non è un titolo generato: quello viene creato con la scaletta.
└shapeenumcomparison indica che questo cluster ha trovato un concorrente nella tua lista. L’articolo deve fare un confronto, non spiegare il concorrente: altrimenti stai creando contenuti per lui.Valori: comparisonroundupguide
└intentstring
└membersarray<TopicMember>
└textstringLa ricerca, così come è stata digitata.
└impressionsinteger
└clicksinteger
└positionpuò essere nullonumber
└landingUrlpuò essere nullostringLa pagina che Google Search Console registra attualmente per questa ricerca, se presente.
└impressionsintegerMisurate, da Google Search Console.
└clicksintegerMisurati, da Google Search Console.
└positionpuò essere nullonumberMisurata: posizione media ponderata per le impressioni nell’intero cluster.
└competitorboolean
└scorenumber
└rankinteger
└upsideClicksintegerUna stima, non una misurazione: clic mensili aggiuntivi se una pagina dedicata raggiungesse la posizione 3. È intenzionalmente un campo distinto da clicks e impressions e deve restare visivamente separato ovunque venga mostrato. Presentare una proiezione come dato misurato è il tipico errore di questa categoria di prodotti.
└statusenumValori: newdismissedplannedpublished
└outlineAtpuò essere nullostring
└articleAtpuò essere nullostringNon dedurlo da outlineAt. Avere una scaletta non significa che esista una bozza: sono due passaggi a pagamento separati.
└totalQueriesintegerQuante ricerche distinte coprono complessivamente questi topic.
Possibili errori
401Autenticazione non riuscita
404site_not_found: il siteId non appartiene a questo account

Esempio

bash
curl "https://www.querywin.com/api/v1/topics" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

Leggi una bozza già generata

Non avvia mai una generazione e non costa nulla. article è null se non ne esiste ancora una.

Parametri di ricerca

CampoTipoDescrizione
keyobbligatoriostringLa chiave del cluster ottenuta da GET /v1/topics.
siteIdstringDa GET /v1/sites. Per impostazione predefinita usa il primo sito collegato.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataArticleResult
└articlepuò essere nulloArticle
└titlestring
└descriptionstringMeta description.
└markdownstringIl corpo da pubblicare.
└jsonLdstringDati strutturati già compilati per questo articolo. JSON valido: inseriscilo in un tag script di tipo application/ld+json nella pagina pubblicata.
└wordCountinteger
└warningsarray<ArticleWarning>Frasi che sembrano generate dall’IA. La bozza resta utilizzabile: vengono segnalate invece di essere riscritte in silenzio. Registrale. In una pipeline automatizzata, questo è l’unico momento in cui qualcuno potrebbe accorgersene.
└kindenumValori: banned_phrasebanned_wordem_dash_overuseno_short_sentence
└hitstringIl testo problematico.
└articleAtpuò essere nullostring
└modelpuò essere nullostring
Possibili errori
400key_required
401Autenticazione non riuscita

Esempio

bash
curl "https://www.querywin.com/api/v1/topics/article?key=..." \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

Trasforma la scaletta in una bozza pubblicabile (costa crediti)

È la chiamata più costosa del prodotto. È sincrona e può richiedere da uno a due minuti.

Prima deve esistere una scaletta: in caso contrario ricevi failure: "no_outline". La scaletta contiene le evidenze: le ricerche reali del cluster, le pagine citate oggi dall’IA e la deduplicazione rispetto alle pagine esistenti. Saltarla ridurrebbe tutto a uno strumento di scrittura IA senza basi.

article.markdown è il corpo da pubblicare. article.jsonLd contiene dati strutturati già compilati con i contenuti dell’articolo. **article.warnings va registrato, non scartato**: ogni avviso indica una frase specifica che sembra generata dall’IA e, in una pipeline automatizzata, nessuno rilegge la bozza prima della pubblicazione.

Le bozze che non superano la validazione strutturale (sezioni mancanti, domande obbligatorie senza risposta, link inventati o JSON-LD non valido) vengono scartate e non addebitate.

Corpo della richiesta

CampoTipoDescrizione
keyobbligatoriostringLa chiave del cluster ottenuta da GET /v1/topics.
siteIdstringPer impostazione predefinita, il primo sito collegato.
confirmSpendobbligatoriointegerUn tetto di autorizzazione in crediti, non un importo esatto. Invia un valore >= al prezzo attuale indicato da GET /v1/usage; ti verrà addebitato il costo effettivo, spesso pari a zero quando il risultato è in cache. Se il prezzo supera il tetto, la chiamata viene rifiutata invece di addebitare silenziosamente un importo maggiore. È obbligatorio anche se hai ancora crediti gratuiti: l’agevolazione termina e quello non dovrebbe essere il momento in cui il tuo script scopre per la prima volta che questo endpoint ha un costo. (min 0)

Risposta200

CampoTipoDescrizione
successenumValori: true
dataArticleOutcome
└okboolean
└failureenumPresente solo quando ok è false. HTTP è comunque 200.Valori: topic_not_foundno_outlineengine_unavailableengine_failedno_valid_article
└articlepuò essere nulloArticle
└titlestring
└descriptionstringMeta description.
└markdownstringIl corpo da pubblicare.
└jsonLdstringDati strutturati già compilati per questo articolo. JSON valido: inseriscilo in un tag script di tipo application/ld+json nella pagina pubblicata.
└wordCountinteger
└warningsarray<ArticleWarning>Frasi che sembrano generate dall’IA. La bozza resta utilizzabile: vengono segnalate invece di essere riscritte in silenzio. Registrale. In una pipeline automatizzata, questo è l’unico momento in cui qualcuno potrebbe accorgersene.
└kindenumValori: banned_phrasebanned_wordem_dash_overuseno_short_sentence
└hitstringIl testo problematico.
└generatedbooleanFalse quando viene restituito un risultato in cache: non è stato addebitato nulla.
└creditsSpentinteger
└freeUsedboolean
└rejectedarray<string>Errori strutturali che hanno causato lo scarto della bozza (senza addebito): missing_sections, missing_faq, invented_link, invalid_json_ld, comparison_without_contrast, body_too_short.
Possibili errori
400key_required, confirm_spend_required o confirm_spend_too_low (il corpo contiene il price attuale)
401Autenticazione non riuscita
402insufficient_credits: il corpo contiene requiredCredits, currentBalance, shortfall
403missing_scope_spend — a questa chiave non è stato concesso lo scope spend
429rate_limited o daily_limit_reached (il body contiene resetAt)

Esempio

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 — risposta
{
  "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

Leggi una scaletta già generata

Non avvia mai una generazione e non costa nulla. outline è null se non ne esiste ancora una.

Parametri di ricerca

CampoTipoDescrizione
keyobbligatoriostringLa chiave del cluster ottenuta da GET /v1/topics.
siteIdstringDa GET /v1/sites. Per impostazione predefinita usa il primo sito collegato.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataOutlineResult
└outlinepuò essere nulloOutline
└titlestring
└slugstring
└anglestringL’argomentazione che questa pagina dovrebbe sostenere.
└sectionsarray<object>
└headingstring
└pointsarray<string>
└faqarray<object>Le domande a cui la pagina deve rispondere. Sono gli spunti che le risposte dell’IA citano.
└questionstring
└answerstring
└schemaTypestringIl tipo JSON-LD più adatto a questa pagina.
└internalLinksarray<string>Pagine del tuo sito a cui vale la pena collegarsi. Scelte da URL reali, mai inventate.
└outlineAtpuò essere nullostring
└modelpuò essere nullostring
Possibili errori
400key_required
401Autenticazione non riuscita

Esempio

bash
curl "https://www.querywin.com/api/v1/topics/outline?key=..." \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

Genera una scaletta (costa crediti)

Sincrona; richiede circa 10-20 secondi.

Input identici restituiscono la scaletta memorizzata nella cache senza un nuovo addebito: l’impronta comprende il cluster di topic e la classificazione dei concorrenti, quindi riprovare dopo una richiesta HTTP non riuscita è sicuro.

Restituisce HTTP 200 con ok: false e un codice failure per gli esiti operativi (motore non disponibile, output non valido). I crediti insufficienti producono invece un vero 402.

Corpo della richiesta

CampoTipoDescrizione
keyobbligatoriostringLa chiave del cluster ottenuta da GET /v1/topics.
siteIdstringPer impostazione predefinita, il primo sito collegato.
confirmSpendobbligatoriointegerUn tetto di autorizzazione in crediti, non un importo esatto. Invia un valore >= al prezzo attuale indicato da GET /v1/usage; ti verrà addebitato il costo effettivo, spesso pari a zero quando il risultato è in cache. Se il prezzo supera il tetto, la chiamata viene rifiutata invece di addebitare silenziosamente un importo maggiore. È obbligatorio anche se hai ancora crediti gratuiti: l’agevolazione termina e quello non dovrebbe essere il momento in cui il tuo script scopre per la prima volta che questo endpoint ha un costo. (min 0)

Risposta200

CampoTipoDescrizione
successenumValori: true
dataOutlineOutcome
└okboolean
└failureenumPresente solo quando ok è false. HTTP è comunque 200: si tratta di un esito, non di un errore.Valori: topic_not_foundengine_unavailableengine_failedno_valid_outline
└outlinepuò essere nulloOutline
└titlestring
└slugstring
└anglestringL’argomentazione che questa pagina dovrebbe sostenere.
└sectionsarray<object>
└headingstring
└pointsarray<string>
└faqarray<object>Le domande a cui la pagina deve rispondere. Sono gli spunti che le risposte dell’IA citano.
└questionstring
└answerstring
└schemaTypestringIl tipo JSON-LD più adatto a questa pagina.
└internalLinksarray<string>Pagine del tuo sito a cui vale la pena collegarsi. Scelte da URL reali, mai inventate.
└generatedbooleanFalse quando viene restituito un risultato in cache: non è stato addebitato nulla.
└creditsSpentinteger
└freeUsedboolean
└rejectedarray<string>Regole di convalida violate dall’output del modello. È utile registrarle come segnale di qualità.
Possibili errori
400key_required, confirm_spend_required o confirm_spend_too_low (il corpo contiene il price attuale)
401Autenticazione non riuscita
402insufficient_credits: il corpo contiene requiredCredits, currentBalance, shortfall
403missing_scope_spend: a questa chiave non è stato concesso l’ambito spend
429rate_limited o daily_limit_reached (il corpo contiene resetAt)

Esempio

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 — risposta
{
  "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

Segnala dove l’hai pubblicato

Chiude il ciclo. Contrassegna la scaletta come pubblicata, salva l’URL e la invia a IndexNow per tuo conto.

IndexNow copre Bing, Yandex, Seznam e Naver, non Google. Google non dispone di un endpoint equivalente per l’indicizzazione istantanea; trova la pagina tramite la tua sitemap.

L’invio a IndexNow non causa mai il fallimento della richiesta: il tuo articolo è già pubblicato, ed è questo il fatto registrato dalla chiamata. Controlla il campo indexnow per sapere cosa è realmente successo. Per l’invio, il file della chiave IndexNow deve essere verificato per il sito (configuralo una volta nel Dashboard).

Registrare l’URL permette anche a QueryWin di misurare di nuovo le ricerche a cui punta l’articolo, dopo che ha avuto il tempo di posizionarsi.

Corpo della richiesta

CampoTipoDescrizione
keyobbligatoriostring
siteIdstring
urlobbligatoriostringDove l’hai pubblicato. Solo http/https. A questo punto non viene recuperato.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataPublishedResult
└clusterKeystring
└statusenumValori: published
└publishedUrlstring
└publishedAtstring
└indexnowobjectBing, Yandex, Seznam, Naver. Non Google.
└pushedboolean
└outcomestringskipped di solito significa che il file della chiave non è ancora verificato.
└enginesstring
Possibili errori
400key_required, url_required o invalid_url (solo http/https)
401Autenticazione non riuscita
403missing_scope_publish — a questa chiave non è stato concesso lo scope publish
404site_not_found

Esempio

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 — risposta
{
  "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

Campagne e quota di distribuzione

Campagne (quelle archiviate sono escluse, salvo includeArchived=true), oltre ai limiti di distribuzione del piano e all’utilizzo attuale. Gratuito.

Parametri di ricerca

CampoTipoDescrizione
productIdstringSolo le campagne di questo prodotto.
includeArchivedboolean

Risposta200

CampoTipoDescrizione
successenumValori: true
dataCampaignList
└campaignsarray<Campaign>
└campaignIdstring
└productIdstring
└namestring
└statusenumcompleted viene calcolato: ogni task è pubblicato, verificato, fallito o saltato.Valori: activecompletedarchived
└quotaintegerNumero di canali con cui è stata creata la campagna.
└startsAtstring
└endsAtpuò essere nullostring
└countsobject
└totalinteger
└submittedintegerinviati + pubblicati + verificati.
└liveintegerpubblicati + verificati.
└blockedinteger
└doneinteger
└byStatusobject
└createdAtstring
└quotaDistributionQuotaI limiti di distribuzione del piano e l’utilizzo attuale. Un limite null indica che non è previsto alcun limite.
└planstring
└limitsobject
└channelspuò essere nullointegerNumero di canali distinti per i quali un prodotto può avere task, conteggiati nell’arco di channelsPeriod.
└channelsPeriodenummonth (piani a pagamento): conteggio per mese solare (UTC), quindi ogni mese consente un nuovo gruppo di canali. total (piano Gratuito): conteggio per tutta la durata del prodotto.Valori: monthtotal
└tasksPerMonthpuò essere nullointegerTask che possono essere creati per mese solare (UTC), per tutti i prodotti.
└activeCampaignspuò essere nullointegerCampagne che possono essere attive contemporaneamente.
└usageobject
└activeCampaignsinteger
└tasksThisMonthinteger
└channelspuò essere nullointegerSolo quando la richiesta specifica un prodotto.
Possibili errori
401Autenticazione non riuscita

Esempio

bash
curl "https://www.querywin.com/api/v1/campaigns" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

Crea una campagna da un elenco esplicito di canali

Crea una task per ogni ID canale, preparando subito e senza costi il materiale di invio dalla scheda prodotto. Passa i canali che hai scelto effettivamente: una campagna registra dove hai deciso di inviare il prodotto, non è un filtro che il server amplia.

I canali per cui il prodotto ha già una task aperta o inviata vengono ignorati e inclusi in skipped con un motivo (already_open, already_submitted, not_found, broken, inactive, other_product, locked). Se non rimane nulla, la chiamata restituisce HTTP 200 con ok: false e failure: "no_valid_targets". Il superamento della quota di distribuzione del piano comporta un rifiuto: **409 quota_exceeded** con dimension, limit, used e requested; non viene creata nessuna task, nemmeno la parte che sarebbe rientrata nella quota.

Corpo della richiesta

CampoTipoDescrizione
productIdobbligatoriostring
nameobbligatoriostring
targetIdsobbligatorioarray<string>ID dei canali ottenuti da GET /v1/channels. Solo i canali selezionati.
endsAtstringScadenza facoltativa mostrata nel Dashboard. Nulla si chiude automaticamente.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataCreateCampaignOutcome
└okboolean
└failureenumPresente quando ok è false.Valori: no_valid_targets
└campaignCampaign
└campaignIdstring
└productIdstring
└namestring
└statusenumcompleted viene calcolato: ogni task è pubblicato, verificato, fallito o saltato.Valori: activecompletedarchived
└quotaintegerNumero di canali con cui è stata creata la campagna.
└startsAtstring
└endsAtpuò essere nullostring
└countsobject
└totalinteger
└submittedintegerinviati + pubblicati + verificati.
└liveintegerpubblicati + verificati.
└blockedinteger
└doneinteger
└byStatusobject
└createdAtstring
└tasksarray<Task>
└taskIdstring
└campaignIdstring
└productIdstring
└statusenumpublished è ciò che hai segnalato tu. verified è ciò che QueryWin ha visto nella pagina dell’inserzione (un link per directory ed elenchi di strumenti IA, una menzione altrove). Mantienili distinti nei report.Valori: plannedpreparedin_progressblockedsubmittedpublishedverifiedfailedskipped
└blockedReasonpuò essere nulloenumValori: logincaptchapaymentmissing_materialothernull
└missingarray<string>Materiali obbligatori mancanti nella scheda. Completa la scheda nel Dashboard: il task si prepara di nuovo automaticamente.
└listingUrlpuò essere nullostring
└markedByenumChi ha modificato per ultimo lo stato: una persona (o questa API), l’estensione del browser o QueryWin.Valori: userdevicesystem
└hasGeneratedbooleanEsiste una riscrittura specifica per il canale.
└reviewDueAtpuò essere nullostringQuando ricontrollare dopo l’invio (submittedAt + i giorni di revisione del canale).
└submittedAtpuò essere nullostring
└publishedAtpuò essere nullostring
└verifiedAtpuò essere nullostring
└nextarray<string>Stati che puoi impostare da quello attuale tramite POST /v1/tasks/{id}/status.
└targetobject
└targetIdstring
└namestring
└urlstring
└submitUrlstring
└kindstring
└sourceenumValori: seeduserrivals
└languagestring
└requiresBacklinkboolean
└checkpuò essere nulloobjectIl nuovo controllo che QueryWin esegue sull’inserzione dopo la pubblicazione. Null finché il task non viene pubblicato.
└kindenumCosa viene cercato: un link al prodotto (directory, elenchi di strumenti IA) oppure una menzione del brand (in tutti gli altri casi).Valori: link_livemention_seen
└statepuò essere nulloenumconfirmed = trovato. unconfirmed = non trovato in un ciclo di controlli (lo stato non cambia; verifica l’URL). lost = era presente, ma non lo è più (il task non è riuscito).Valori: confirmedunconfirmedlostnull
└checkedAtpuò essere nullostring
└dueAtpuò essere nullostring
└updatedAtstring
└skippedarray<object>
└targetIdstring
└reasonenumother_product = candidato citato dall’IA appartenente a un altro prodotto; inactive = canale disabilitato o ignorato; locked = fuori dalla finestra della directory del piano Gratuito (i primi window canali per corrispondenza, restituiti da GET /v1/channels; i tuoi canali, i preferiti e quelli citati dall’IA non sono limitati).Valori: not_foundbrokeninactiveother_productalready_openalready_submittedlocked
Possibili errori
400product_id_required, name_required / name_too_long (80), target_ids_required / too_many_targets (100) oppure invalid_date
401Autenticazione non riuscita
403missing_scope_publish: a questa chiave non è stato concesso lo scope publish
404product_not_found: il productId non appartiene a questo account
409quota_exceeded: il corpo contiene dimension (active_campaigns / tasks_per_month / channels), limit, used, requested; per channels include anche period (month / total, come DistributionQuota.limits.channelsPeriod)

Esempio

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 — risposta
{
  "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}

Una campagna con le relative task

La campagna e tutte le task che contiene. Gratuito.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataCampaignDetail
└campaignCampaign
└campaignIdstring
└productIdstring
└namestring
└statusenumcompleted viene calcolato: ogni task è pubblicato, verificato, fallito o saltato.Valori: activecompletedarchived
└quotaintegerNumero di canali con cui è stata creata la campagna.
└startsAtstring
└endsAtpuò essere nullostring
└countsobject
└totalinteger
└submittedintegerinviati + pubblicati + verificati.
└liveintegerpubblicati + verificati.
└blockedinteger
└doneinteger
└byStatusobject
└createdAtstring
└tasksarray<Task>
└taskIdstring
└campaignIdstring
└productIdstring
└statusenumpublished è ciò che hai segnalato tu. verified è ciò che QueryWin ha visto nella pagina dell’inserzione (un link per directory ed elenchi di strumenti IA, una menzione altrove). Mantienili distinti nei report.Valori: plannedpreparedin_progressblockedsubmittedpublishedverifiedfailedskipped
└blockedReasonpuò essere nulloenumValori: logincaptchapaymentmissing_materialothernull
└missingarray<string>Materiali obbligatori mancanti nella scheda. Completa la scheda nel Dashboard: il task si prepara di nuovo automaticamente.
└listingUrlpuò essere nullostring
└markedByenumChi ha modificato per ultimo lo stato: una persona (o questa API), l’estensione del browser o QueryWin.Valori: userdevicesystem
└hasGeneratedbooleanEsiste una riscrittura specifica per il canale.
└reviewDueAtpuò essere nullostringQuando ricontrollare dopo l’invio (submittedAt + i giorni di revisione del canale).
└submittedAtpuò essere nullostring
└publishedAtpuò essere nullostring
└verifiedAtpuò essere nullostring
└nextarray<string>Stati che puoi impostare da quello attuale tramite POST /v1/tasks/{id}/status.
└targetobject
└targetIdstring
└namestring
└urlstring
└submitUrlstring
└kindstring
└sourceenumValori: seeduserrivals
└languagestring
└requiresBacklinkboolean
└checkpuò essere nulloobjectIl nuovo controllo che QueryWin esegue sull’inserzione dopo la pubblicazione. Null finché il task non viene pubblicato.
└kindenumCosa viene cercato: un link al prodotto (directory, elenchi di strumenti IA) oppure una menzione del brand (in tutti gli altri casi).Valori: link_livemention_seen
└statepuò essere nulloenumconfirmed = trovato. unconfirmed = non trovato in un ciclo di controlli (lo stato non cambia; verifica l’URL). lost = era presente, ma non lo è più (il task non è riuscito).Valori: confirmedunconfirmedlostnull
└checkedAtpuò essere nullostring
└dueAtpuò essere nullostring
└updatedAtstring
Possibili errori
401Autenticazione non riuscita
404campaign_not_found

Esempio

bash
curl "https://www.querywin.com/api/v1/campaigns/{id}" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

Dove è possibile inviare un prodotto, ordinato per affinità

La libreria dei canali (directory, piattaforme di lancio, elenchi di strumenti IA, community e piattaforme di pubblicazione), le tue voci e, quando viene fornito productId, i siti che le risposte IA citano già per le ricerche di quel prodotto (source: "rivals"). Con productId, l’elenco è ordinato per pertinenza e ogni canale include taskStatus (non nullo quando il prodotto ha già un’attività su quel canale). Gratuito.

**citedByAi indica che le risposte IA hanno citato quel sito per le tue ricerche. Non significa che il sito inserirà il tuo prodotto**: l’attività serve proprio per contattarlo e chiederlo.

Parametri di ricerca

CampoTipoDescrizione
productIdstringOrdina per affinità con questo prodotto, aggiungi taskStatus e includi i candidati citati dall’IA.
kindstringTipo di canale.Valori: directorylaunchai_directorycommunitycontentother
sourcestringseed = la libreria, user = aggiunto da te, rivals = siti citati dall’IA per questo prodotto.Valori: seeduserrivals
pricingstringCosto dell’invio.Valori: freeconditionalpaidunknown
submitMethodstringModalità di invio: compilare un modulo, pubblicare in una community o inviare una proposta via email.Valori: formpostemail
qstringCerca per nome, dominio e argomenti.
hideSubmittedbooleanEsclude i canali per cui questo prodotto ha già un’attività aperta o inviata. Richiede productId.
pageintegerIl valore predefinito è 1.
pageSizeintegerIl valore predefinito è 30.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataChannelList
└productIdpuò essere nullostring
└itemsarray<Channel>
└targetIdstringPassali come targetIds a POST /v1/campaigns.
└namestring
└urlstring
└submitUrlstringIl modulo d’invio o la pagina di pubblicazione; per i siti citati dall’IA, la pagina citata più spesso dalle risposte dell’IA.
└kindenumValori: directorylaunchai_directorycommunitycontentother
└submitMethodenumform = compila il relativo modulo d’invio, post = pubblica tu nella community, email = contatta gli editor via email (l’indirizzo viene letto dalla pagina quando la apri; non viene memorizzato).Valori: formpostemail
└sourceenumseed = la directory, user = aggiunto da te, rivals = un sito citato dalle risposte dell’IA per le ricerche di questo prodotto.Valori: seeduserrivals
└pricingTypeenumValori: freeconditionalpaidunknown
└priceNotepuò essere nullostring
└languagestringen, zh, multi o un codice lingua rilevato dalle ricerche per i siti citati dall’IA.
└topicsarray<string>
└requiresAccountboolean
└requiresBacklinkboolean
└reviewDayspuò essere nullointegerTempo di revisione tipico. Il task ti ricorda di ricontrollare dopo questo periodo.
└siteRankpuò essere nullointegerPosizione globale nella lista pubblica Tranco (un numero più basso indica più visite). null = fuori dal primo milione. Non è il Domain Rating.
└hasFormSpecbooleanI campi del modulo del canale sono registrati, quindi i materiali vengono adattati ai relativi limiti esatti.
└relevancepuò essere nullointegerPunteggio di corrispondenza per il prodotto indicato nella richiesta. Serve solo per l’ordinamento.
└taskStatuspuò essere nullostringIl task aperto o completato del prodotto per questo canale, se presente. null = nessun task.
└citedByAipuò essere nulloobjectSolo per source: "rivals". Le risposte dell’IA hanno citato questo sito per le ricerche elencate. Questo non garantisce che il sito inserirà il tuo prodotto: il task serve proprio a fare la richiesta.
└queriesintegerNumero di ricerche distinte per cui è stato citato.
└samplesintegerNumero di campioni di risposte dell’IA in cui è stato citato.
└searchesarray<string>
└pagesarray<string>Le pagine citate, dalla più citata alla meno citata. Vuoto per i campioni più vecchi che registravano solo il dominio.
└totalinteger
└pageinteger
└pageSizeinteger
└limitedbooleanPiano Gratuito: vengono restituiti solo i primi window canali nell’ordine predefinito (quelli più adatti a productId), mentre i parametri di filtro, ricerca e ordinamento vengono rifiutati con 403 library_locked. total indica comunque le dimensioni dell’intera directory.
└windowpuò essere nullointegerNumero di canali visibili con il piano gratuito: 20, più 20 per ogni amico iscritto tramite il tuo link di invito (e 20 se ti sei iscritto tramite un invito). null se la lista non ha limiti.
Possibili errori
401Autenticazione non riuscita

Esempio

bash
curl "https://www.querywin.com/api/v1/channels" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

I tuoi prodotti e il livello di completezza di ogni scheda

Inizia qui per la pipeline di distribuzione: ogni altro endpoint di distribuzione richiede un productId. completeness e missing derivano dalla scheda prodotto salvata: un campo vuoto qui resta vuoto in ogni invio, e le lacune obbligatorie bloccano un’attività con stato blocked / missing_material finché non completi la scheda. Leggila con GET /v1/products/{id} e completala con PATCH /v1/products/{id}. Gratuito.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataProductList
└productsarray<Product>
└productIdstring
└namestring
└urlstring
└domainstring
└primaryLanguageenumValori: enzhjakodefresptitrunlpltrarthviid
└topicsarray<string>
└completenessintegerCompletezza della scheda, da 0 a 100. Compila i campi mancanti nel Dashboard o con PATCH /v1/products/{id}.
└missingarray<string>Campi vuoti della scheda. Ognuno rappresenta una lacuna in ogni invio.
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughpuò essere nullostring
Possibili errori
401Autenticazione non riuscita

Esempio

bash
curl "https://www.querywin.com/api/v1/products" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

Crea un prodotto

Richiede lo scope publish e un piano a pagamento. Crea un prodotto a partire dall’URL pubblico della homepage e da un nome facoltativo; usa la quota del prodotto e non consuma crediti. Restituisce la scheda prodotto completa salvata. Poi usa PATCH /v1/products/{id} per completarla. Non esegue crawling né IA. Se il dominio è duplicato, restituisce 409 product_exists con il productId esistente; riutilizzalo se la risposta è andata persa. I valori predefiniti del prodotto sono modificabili e non fatti verificati sul sito.

Corpo della richiesta

CampoTipoDescrizione
urlobbligatoriostring
namestring

Risposta201

CampoTipoDescrizione
successboolean
dataProductDetail
└namestring
└urlstring
└primaryLanguageenumValori: enzhjakodefresptitrunlpltrarthviid
└businessTypeenumValori: saastoolecommercecontentserviceother
└launchStatusenumValori: livebeta
└pricingModelenumValori: freefreemiumpaidtrial
└taglinepuò essere nulloobject
└shortDescpuò essere nulloobject
└longDescpuò essere nulloobject
└firstCommentpuò essere nulloobject
└topicsarray<string>
└promoCodepuò essere nullostring
└videoUrlpuò essere nullostring
└demoUrlpuò essere nullostring
└linkspuò essere nulloobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
└socialspuò essere nulloobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
└contactNamepuò essere nullostring
└contactEmailpuò essere nullostring
└galleryarray<string>
└productIdstring
└domainstring
└thumbnailUrlpuò essere nullostring
└completenessinteger (0–100)
└missingarray<string>
└createdAtstring
└updatedAtstring
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughpuò essere nullostring
Possibili errori
400invalid_product_input, invalid_url, url_not_public
401Autenticazione non riuscita
403plan_required o missing_scope_publish per le scritture
409product_exists (include il productId esistente) o product_limit_reached
429rate_limited — 120 richieste al minuto per chiave

Esempio

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 — risposta
{
  "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}

Completa o aggiorna una scheda prodotto

Richiede lo scope publish. Salva subito solo i campi forniti; gli oggetti, inclusi i testi localizzati, e gli array sostituiscono interamente il campo. Leggi prima la scheda per conservare le altre lingue. Nessun credito né generazione IA. I materiali di invio in attesa vengono aggiornati in modo asincrono. Usa solo fatti noti; non inventare dettagli mancanti sul prodotto.

Corpo della richiesta

CampoTipoDescrizione
namestring
urlstring
primaryLanguageenumValori: enzhjakodefresptitrunlpltrarthviid
businessTypeenumValori: saastoolecommercecontentserviceother
launchStatusenumValori: livebeta
pricingModelenumValori: 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>

Risposta200

CampoTipoDescrizione
successboolean
dataProductDetail
└namestring
└urlstring
└primaryLanguageenumValori: enzhjakodefresptitrunlpltrarthviid
└businessTypeenumValori: saastoolecommercecontentserviceother
└launchStatusenumValori: livebeta
└pricingModelenumValori: freefreemiumpaidtrial
└taglinepuò essere nulloobject
└shortDescpuò essere nulloobject
└longDescpuò essere nulloobject
└firstCommentpuò essere nulloobject
└topicsarray<string>
└promoCodepuò essere nullostring
└videoUrlpuò essere nullostring
└demoUrlpuò essere nullostring
└linkspuò essere nulloobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
└socialspuò essere nulloobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
└contactNamepuò essere nullostring
└contactEmailpuò essere nullostring
└galleryarray<string>
└productIdstring
└domainstring
└thumbnailUrlpuò essere nullostring
└completenessinteger (0–100)
└missingarray<string>
└createdAtstring
└updatedAtstring
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughpuò essere nullostring
Possibili errori
400invalid_product_input, invalid_url, url_not_public, invalid_email o invalid_gallery
401Autenticazione non riuscita
403plan_required o missing_scope_publish per le scritture
404product_not_found
409product_exists (include il productId esistente)
429rate_limited — 120 richieste al minuto per chiave

Esempio

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 — risposta
{
  "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}

Leggi una scheda prodotto completa

Restituisce i testi localizzati salvati, i link, i recapiti, le immagini, la completezza e i campi mancanti. Scope di lettura; nessun credito. Un prodotto al di fuori di questo spazio di lavoro restituisce comunque product_not_found.

Risposta200

CampoTipoDescrizione
successboolean
dataProductDetail
└namestring
└urlstring
└primaryLanguageenumValori: enzhjakodefresptitrunlpltrarthviid
└businessTypeenumValori: saastoolecommercecontentserviceother
└launchStatusenumValori: livebeta
└pricingModelenumValori: freefreemiumpaidtrial
└taglinepuò essere nulloobject
└shortDescpuò essere nulloobject
└longDescpuò essere nulloobject
└firstCommentpuò essere nulloobject
└topicsarray<string>
└promoCodepuò essere nullostring
└videoUrlpuò essere nullostring
└demoUrlpuò essere nullostring
└linkspuò essere nulloobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
└socialspuò essere nulloobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
└contactNamepuò essere nullostring
└contactEmailpuò essere nullostring
└galleryarray<string>
└productIdstring
└domainstring
└thumbnailUrlpuò essere nullostring
└completenessinteger (0–100)
└missingarray<string>
└createdAtstring
└updatedAtstring
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughpuò essere nullostring
Possibili errori
401Autenticazione non riuscita
403plan_required o missing_scope_publish per le scritture
404product_not_found
429rate_limited — 120 richieste al minuto per chiave

Esempio

bash
curl "https://www.querywin.com/api/v1/products/{id}" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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

Importa un’immagine del prodotto da un URL

Richiede lo scope publish. Recupera un’immagine HTTP(S) pubblica e ne salva una copia come miniatura, sostituendola, oppure nella gallery, aggiungendola fino a un massimo di sei. JPEG, PNG, WebP e SVG, fino a 4 MB; gli SVG vengono convertiti in PNG. Le reti private e i reindirizzamenti non sicuri sono bloccati dal fetcher di immagini esistente. Nessun credito. Importazioni ripetute nella gallery possono creare duplicati; dopo una risposta persa, esegui GET sul prodotto prima di riprovare.

Corpo della richiesta

CampoTipoDescrizione
urlobbligatoriostring
kindenumValori: thumbnailgalleryPredefinito gallery

Risposta200

CampoTipoDescrizione
successboolean
dataProductDetail
└namestring
└urlstring
└primaryLanguageenumValori: enzhjakodefresptitrunlpltrarthviid
└businessTypeenumValori: saastoolecommercecontentserviceother
└launchStatusenumValori: livebeta
└pricingModelenumValori: freefreemiumpaidtrial
└taglinepuò essere nulloobject
└shortDescpuò essere nulloobject
└longDescpuò essere nulloobject
└firstCommentpuò essere nulloobject
└topicsarray<string>
└promoCodepuò essere nullostring
└videoUrlpuò essere nullostring
└demoUrlpuò essere nullostring
└linkspuò essere nulloobject
└webAppstring
└appStorestring
└playStorestring
└chromeExtensionstring
└macosstring
└windowsstring
└socialspuò essere nulloobject
└xstring
└linkedinstring
└githubstring
└instagramstring
└youtubestring
└facebookstring
└contactNamepuò essere nullostring
└contactEmailpuò essere nullostring
└galleryarray<string>
└productIdstring
└domainstring
└thumbnailUrlpuò essere nullostring
└completenessinteger (0–100)
└missingarray<string>
└createdAtstring
└updatedAtstring
└sitesarray<object>
└siteIdstring
└domainstring
└syncedThroughpuò essere nullostring
Possibili errori
400invalid_product_input o invalid_url
401Autenticazione non riuscita
403plan_required o missing_scope_publish per le scritture
404product_not_found
409gallery_full
413image_too_large
415image_type
429rate_limited — 120 richieste al minuto per chiave
502image_unreachable
503storage_unavailable

Esempio

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 — risposta
{
  "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

Task di tutte le campagne

Il registro degli invii, con l’attività più recente per prima. Filtra per prodotto, campagna o per un elenco di stati separati da virgole. byStatus conta tutte le task incluse nell’ambito prima di applicare il filtro per stato. Gratuito.

Parametri di ricerca

CampoTipoDescrizione
productIdstringSolo le task di questo prodotto.
campaignIdstring
statusstringSeparati da virgole, ad esempio prepared,in_progress.
pageintegerIl valore predefinito è 1.
pageSizeintegerIl valore predefinito è 30.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataTaskList
└itemsarray<Task>
└taskIdstring
└campaignIdstring
└productIdstring
└statusenumpublished è ciò che hai segnalato tu. verified è ciò che QueryWin ha visto nella pagina dell’inserzione (un link per directory ed elenchi di strumenti IA, una menzione altrove). Mantienili distinti nei report.Valori: plannedpreparedin_progressblockedsubmittedpublishedverifiedfailedskipped
└blockedReasonpuò essere nulloenumValori: logincaptchapaymentmissing_materialothernull
└missingarray<string>Materiali obbligatori mancanti nella scheda. Completa la scheda nel Dashboard: il task si prepara di nuovo automaticamente.
└listingUrlpuò essere nullostring
└markedByenumChi ha modificato per ultimo lo stato: una persona (o questa API), l’estensione del browser o QueryWin.Valori: userdevicesystem
└hasGeneratedbooleanEsiste una riscrittura specifica per il canale.
└reviewDueAtpuò essere nullostringQuando ricontrollare dopo l’invio (submittedAt + i giorni di revisione del canale).
└submittedAtpuò essere nullostring
└publishedAtpuò essere nullostring
└verifiedAtpuò essere nullostring
└nextarray<string>Stati che puoi impostare da quello attuale tramite POST /v1/tasks/{id}/status.
└targetobject
└targetIdstring
└namestring
└urlstring
└submitUrlstring
└kindstring
└sourceenumValori: seeduserrivals
└languagestring
└requiresBacklinkboolean
└checkpuò essere nulloobjectIl nuovo controllo che QueryWin esegue sull’inserzione dopo la pubblicazione. Null finché il task non viene pubblicato.
└kindenumCosa viene cercato: un link al prodotto (directory, elenchi di strumenti IA) oppure una menzione del brand (in tutti gli altri casi).Valori: link_livemention_seen
└statepuò essere nulloenumconfirmed = trovato. unconfirmed = non trovato in un ciclo di controlli (lo stato non cambia; verifica l’URL). lost = era presente, ma non lo è più (il task non è riuscito).Valori: confirmedunconfirmedlostnull
└checkedAtpuò essere nullostring
└dueAtpuò essere nullostring
└updatedAtstring
└totalinteger
└pageinteger
└pageSizeinteger
└byStatusobject
Possibili errori
401Autenticazione non riuscita

Esempio

bash
curl "https://www.querywin.com/api/v1/tasks" \
  -H "Authorization: Bearer $QUERYWIN_API_KEY"
json — risposta
{
  "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}

Una task con il materiale da inviare

Ogni campo richiesto dal modulo del canale, ricavato dalla scheda prodotto entro i limiti del canale (source: "profile"), oltre alla riscrittura specifica per il canale, se presente (source: "ai"), e alle modifiche apportate nel Dashboard (source: "override"). source: "none" indica che nella scheda prodotto non c’è nulla per quel campo: non inventarlo. Non avvia mai una riscrittura e non comporta costi.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataTaskDetailResult
└taskTaskDetail
Possibili errori
401Autenticazione non riuscita
404task_not_found

Esempio

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

Riscrivi il materiale per questo canale (costa crediti)

Un passaggio IA adatta la scheda prodotto a questo canale specifico: una descrizione più breve per una directory, il primo commento del creatore, un post per una community, una proposta per un editor oppure, per i siti con citazioni IA, una proposta più un paragrafo che il proprietario della pagina potrebbe aggiungere. È sincrono e richiede pochi secondi. Usa solo i fatti presenti nella scheda prodotto; qualsiasi parte contenente un link assente dalla scheda viene scartata e non addebitata.

Input identici restituiscono la versione precedente senza addebito (cached: true); force: true riscrive comunque e comporta un addebito. Per le directory, il materiale della scheda prodotto restituito da GET /v1/tasks/{id} è di solito sufficiente; riscrivi quando il canale richiede un tono diverso.

Restituisce HTTP 200 con ok: false e failure: "engine_failed" quando nulla supera la convalida. I crediti insufficienti producono effettivamente un errore 402.

Corpo della richiesta

CampoTipoDescrizione
confirmSpendobbligatoriointegerSoglia massima di autorizzazione in crediti, con lo stesso significato previsto per scalette e bozze. Leggi il prezzo da GET /v1/usage (materials.pricePerTask). Obbligatorio anche se rimane un’indennità Gratuito. (min 0)
forcebooleanRiscrivi anche se nulla è cambiato dall’ultima versione. Operazione addebitata.

Risposta200

CampoTipoDescrizione
successenumValori: true
dataWriteMaterialsOutcome
└okboolean
└failureenumPresente quando ok è false. Non viene addebitato nulla.Valori: engine_failedengine_unavailable
└cachedbooleanGli input non sono cambiati; è stata restituita la versione precedente e non è stato addebitato nulla.
└freeUsedboolean
└creditsSpentinteger
└taskTaskDetail
Possibili errori
400confirm_spend_required o confirm_spend_too_low (il corpo contiene il price attuale)
401Autenticazione non riuscita
402insufficient_credits: il corpo contiene requiredCredits, currentBalance, shortfall
403missing_scope_spend: a questa chiave non è stato concesso lo scope spend
404task_not_found
429rate_limited o daily_limit_reached (il corpo contiene resetAt)

Esempio

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 — risposta
{
  "success": true,
  "data": {
    "ok": true,
    "failure": "engine_failed",
    "cached": true,
    "freeUsed": true,
    "creditsSpent": 0,
    "task": null
  }
}
POST/v1/tasks/{id}/status

Indica cosa è successo con l’invio

Registra l’esito di un invio effettuato con i tuoi account. QueryWin non invia mai nulla autonomamente. Imposta submitted quando hai inviato il modulo, poi published con l’URL dell’inserzione (la voce o il post stesso, non la home page del sito) quando è online; QueryWin ricontrolla quella pagina circa 72 ore dopo per verificare la presenza di un link al tuo prodotto (directory, elenchi di strumenti IA) o di una menzione (in tutti gli altri casi) e imposta autonomamente verified: non puoi impostarlo tu.

blocked significa “richiede l’intervento di una persona”: passa reason (login, captcha, payment, missing_material, other). failed / skipped chiudono la task; prepared la riapre. Le transizioni non consentite dalla macchina a stati restituiscono **409 transition_not_allowed**; il campo next della task elenca gli stati consentiti da quello attuale.

Corpo della richiesta

CampoTipoDescrizione
statusobbligatorioenumNon puoi impostare verified; QueryWin lo imposta dopo aver ricontrollato l’inserzione.Valori: preparedin_progressblockedsubmittedpublishedfailedskipped
listingUrlstringObbligatorio per published: l’inserzione o il post online, non la home page del sito. Solo http/https. Facoltativo con submitted se lo conosci già.
notestring
reasonenumObbligatorio per blocked.Valori: logincaptchapaymentmissing_materialother

Risposta200

CampoTipoDescrizione
successenumValori: true
dataTaskDetailResult
└taskTaskDetail
Possibili errori
400invalid_status, listing_url_required (published richiede listingUrl), invalid_listing_url oppure reason_required (blocked richiede reason)
401Autenticazione non riuscita
403missing_scope_publish: a questa chiave non è stato concesso lo scope publish
404task_not_found
409transition_not_allowed: leggi la task; next elenca gli stati consentiti

Esempio

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 — risposta
{
  "success": true,
  "data": {
    "task": null
  }
}
API di QueryWin — pipeline di contenuti e distribuzione per script e agenti IA