API per sviluppatori v2.1

Costruisci su Nonli con un riferimento API chiaro e pensato per l'IA.

Usa i token API, cerca post e dati di listening, crea link brevi, pubblica post e carica video, tutto in un'unica interfaccia documentata.

URL di base

https://api.nonli.com/v2.1
71Endpoint
11Risorse

Avvio rapido

Bastano tre abitudini per autenticarti, tenere i payload leggeri e interrogare i dati di produzione in modo affidabile.

bash
1curl --location 'https://api.nonli.com/v2.1/brands?size=1&fields=id,name' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
1

Crea un token API

Gli amministratori dell'azienda creano e rinnovano i token API nel back office di Nonli.

2

Invia l'header Bearer

Invia il token a ogni chiamata autenticata con Authorization: Bearer YOUR_API_TOKEN. Per verificare un token, chiama l'endpoint brands mostrato qui.

3

Chiedi solo i campi necessari

Con fields, filters, filtersOr, excludes, size, page e sort mantieni le risposte compatte, sia nelle app sia negli agenti IA.

Indicazioni per l'integrazione con l'IA

OpenAPI JSON: /api-docs/openapi.json

llms.txt: /llms.txt

  • Preferisci /api-docs/openapi.json per generare strumenti ed estrarre gli schemi.
  • Usa /llms.txt come punto di ingresso compatto e scansionabile.
  • I token API sono credenziali legate a un ruolo aziendale, non sessioni di utenti web: non usare /me con i token Bearer.
  • Aggiungi fields ogni volta che puoi, per ridurre il consumo di token.
  • Rispetta i rate limit e scorri i dataset di grandi dimensioni pagina per pagina con size/page.

Esplora gli endpoint

71 endpoint

GETSearch brands/brandsApriGETGet a brand/brands/:brandIdApriGETSearch social accounts/social-accountsApriGETGet a social account/social-accounts/:socialAccountIdApriGETGet publishing quota/social-accounts/publishing-quotaApriGETGet YouTube playlists/social-accounts/youtube/playlistsApriPOSTCreate a YouTube playlist/social-accounts/youtube/playlistsApriPUTUpdate a YouTube playlist/social-accounts/youtube/playlists/:playlistIdApriDELETEDelete a YouTube playlist/social-accounts/youtube/playlists/:playlistIdApriGETList the videos of a YouTube playlist/social-accounts/youtube/playlists/:playlistId/itemsApriPUTMove a video in a YouTube playlist/social-accounts/youtube/playlists/:playlistId/items/:itemIdApriGETGet Pinterest boards/social-accounts/pinterest/boardsApriGETSearch posts/postsApriGETGet a post/posts/:postIdApriGETGet post categories/posts/categoriesApriGETGet post brands/posts/brandsApriGETGet post users/posts/usersApriGETGet post languages/posts/languagesApriGETGet post histogram/posts/histogramApriGETGet post aggregation/posts/aggregationApriGETGet post stats/posts/statsApriPOSTCheck post content/posts/checkApriGETGet post tags/posts/tagsApriGETGet post topics/posts/topicsApriPOSTCreate posts in bulk/posts/bulkApriPUTUpdate posts in bulk/posts/bulkApriDELETEDelete posts in bulk/posts/bulkApriGETSearch shortlinks/shortlinksApriGETGet a shortlink/shortlinks/:shortlinkIdApriGETGet shortlink stats/shortlinks/statsApriGETGet shortlink authors/shortlinks/authorsApriGETGet shortlink brands/shortlinks/brandsApriPOSTCreate a shortlink/shortlinksApriPOSTCreate shortlinks in bulk/shortlinks/bulkApriPUTUpdate a shortlink/shortlinks/:shortlinkIdApriPOSTGenerate a message for a shortlink/shortlinks/:shortlinkId/generate-messageApriGETSearch themes/themesApriGETGet a theme/themes/:themeIdApriPOSTCreate a theme/themesApriPUTUpdate a theme/themes/:themeIdApriDELETEDelete a theme/themes/:themeIdApriGETGet theme roles/themes/rolesApriGETGet social listening/listening/socialApriGETGet organic listening/listening/organicApriPUTUpsert an organic with videos/listening/organicApriGETGet organic content/listening/organic/:organicIdApriGETGet organic stats/listening/organic/statsApriGETGet organic aggregation/listening/organic/aggregationApriGETSuggest organic filter values/listening/organic/suggest/:fieldApriGETGet the leaderboard/posts/leaderboardApriPOSTSearch the leaderboard/posts/leaderboard/_searchApriGETGet leaderboard categories/posts/leaderboard/categoriesApriGETSearch accounts/accountsApriPOSTAdd an account/accountsApriGETGet account categories/accounts/categoriesApriGETSearch account locations/accounts/locationsApriGETGet account stats/accounts/statsApriGETSearch bots/botsApriGETGet a bot/bots/:botIdApriPOSTCreate bots in bulk/bots/bulkApriPUTUpdate bots in bulk/bots/bulkApriDELETEDelete a bot/bots/:botIdApriGETGet bot configuration info/bots/infoApriGETSearch media/mediaApriGETGet media tags/media/tagsApriPOSTUpload an image/upload/imageApriPOSTCreate upload video token/upload/video/tokenApriPOSTCheck uploaded video existence (legacy)/upload/video/existsApriPOSTUpload video chunk/upload/video/chunkApriPOSTCreate upload document token/upload/document/tokenApriPOSTUpload document chunk/upload/document/chunkApri

Flussi di integrazione frequenti

5 Endpoint
In questa pagina

1.Convenzioni

Regole valide per tutti gli endpoint.

Autenticazione

Invia un token API a ogni chiamata autenticata nell'header Authorization: Authorization: Bearer YOUR_API_TOKEN. I token API rappresentano un ruolo aziendale, non la sessione di un utente web; per questo /me non è disponibile con i token Bearer. Conservali in modo sicuro, rinnovali dal back office e non indicarli mai nelle query string.

Paginazione

Gli endpoint di elenco restituiscono total e un array rows. Scorri i risultati con size e un page che parte da zero, finché non hai recuperato total elementi. Con fields ogni elemento resta compatto. Su GET /posts, size accetta valori da 0 a 100 e (page + 1) × size non può superare 10.000: oltre questo limite l'API risponde con 400 Bad Request. In quel caso adatta i filtri, per esempio con un intervallo publishedAt, per raggiungere i post più vecchi.

Date e fusi orari

Le date sono in UTC nel formato ISO 8601, per esempio 2026-05-27T23:59:59Z. I parametri di data e le date nel corpo della richiesta, come createdAt, publishedAt o schedule.startDate, accettano il formato YYYY-MM-DDTHH:MM:SSZ con o senza frazioni di secondo, per esempio 2026-05-27T23:59:59.123Z: una data letta in una risposta può essere rinviata così com'è. La frazione viene ignorata e uno scostamento come +02:00 restituisce 400 Bad Request. I filtri a intervallo usano «..» tra due valori, per esempio publishedAt:-30 days..now; negli endpoint di ricerca di post e listening il limite superiore è escluso.

Rate limit

I limiti sono conteggiati per azienda e per endpoint e sono condivisi da tutti i token API dell'azienda, in finestre fisse che iniziano alla prima chiamata. Il riferimento degli endpoint indica ogni limite; un endpoint senza indicazione non ha un limite di chiamate per endpoint. Ogni risposta di un endpoint limitato include gli header RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset; RateLimit-Reset indica i secondi che mancano alla fine della finestra. Quando il limite è raggiunto, ogni chiamata restituisce 429 fino alla fine della finestra, con un header Retry-After con lo stesso numero di secondi e un body JSON. GET /listening/social, chiamato con un token API Bearer, viene scalato dal pacchetto di social listening dell'azienda, senza finestra per endpoint e senza header RateLimit; una sessione utente API di tipo legacy è limitata a 720 chiamate ogni 12 ore per utente e riceve gli header RateLimit. Una sessione utente API di tipo legacy è un utente connesso con il ruolo API, non un token Bearer.

Stato dei post

Senza un valore di stato in filters (filtersOr non conta), GET /posts, /posts/aggregation e /posts/histogram restituiscono o contano tutti gli stati, tranne i post eliminati (404) e quelli obsoleti (410), comprese bozze, post programmati, post in approvazione e pubblicazioni fallite. GET /posts/stats non applica alcun filtro di stato per impostazione predefinita. Aggiungi filtersOr=status:200,203,204, il gruppo «Pubblicati» del back office, per ottenere numeri coerenti con i contenuti pubblicati. Il riferimento di GET /posts elenca tutti i valori di stato.

Identificatori

Post, link brevi e contenuti organici usano UUID (stringa). Brand, account social e argomenti usano ID interi. Passa gli identificatori esattamente come li restituisce l'API.

Nomi dei contatori

Negli endpoint basati sui post usa i nomi dei contatori documentati in sort, filters, filtersOr, excludes, trigger dei bot e metriche di tasso dei post. Gli endpoint di organic listening e dei link brevi espongono contatori propri e rifiutano i contatori dei post con l'errore 400 Bad Request.

2.Parametri di ricerca

La maggior parte degli endpoint di elenco condivide la stessa grammatica di query.

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
pageintZero-based page number.Predefinito: 0
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
sortstringSort expression. Prefix a field with "-" for descending order. Counter-backed endpoints can sort by counters.total.<field> or by the counter shorthand itself, for example sort=-engagement on post/social data or sort=-click on organic data.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
fieldsstringComma-separated fields to include in each returned item.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

3.Contatori disponibili

Usa questo elenco come panoramica pubblica dei contatori per famiglia di endpoint. I vecchi nomi di contatori sostituiti sono omessi di proposito.

Posts, Social Listening, and Bots

/posts/posts/categories/posts/brands/posts/users/posts/languages/posts/histogram/posts/aggregation/posts/stats/posts/tags/posts/topics/posts/leaderboard/posts/leaderboard/_search/posts/leaderboard/categories/listening/social/bots/bulk

Contatori

clickclick_uniquectrengagementfacebook_engagement_per_minuteweighted_facebook_engagementweighted_facebook_engagement_per_minutefacebook_commentfacebook_total_reactionfacebook_sharereachimpressionvideoViewsvideo_views_3secfacebook_post_video_avg_time_watchedvideo_completion_rateoverperformingrepostsquotesmonetizationsnapchat_screenshotssubscriberfacebook_link_clicksfacebook_other_clicksfacebook_photo_viewfacebook_video_playfacebook_post_video_views_60s_excludes_shorterinstagram_savedinstagram_total_interactionsinstagram_clips_replays_countlinkedin_private_click_countyoutube_likesyoutube_dislikesyoutube_estimated_minutes_watched

Nota: Use these names in post-backed counter filters, counter sorts, bot trigger counters, and bot post-rate metrics. Facebook reach is requested as reach; the API maps the current Meta reach metric internally. TikTok metrics currently feed the common post counters engagement, facebook_total_reaction, facebook_comment, facebook_share, videoViews, and impression, instead of dedicated TikTok counters. Historical counter names replaced by these public names, and counters that are no longer available from network APIs, are intentionally omitted.

Organic Listening

/listening/organic/listening/organic/stats/listening/organic/aggregation

Contatori

clicknew_leadquantityamountpaywall_viewpaywall_unlockedpaywall_clickpaywall_subscribe_clickpaywall_login_clicksubscription_completecomment_countvideo_viewsview_in_stockview_out_of_stockpriceprice_variationlow_pricehigh_priceratingreview_countstock

Nota: Use these names for standard Organic Listening counter filters and sorts. Legacy Organic counters are intentionally excluded from the public API surface.

Shortlinks

/shortlinks/shortlinks/:shortlinkId/shortlinks/stats/shortlinks/authors/shortlinks/brands

Contatori

click

Nota: Shortlink counter endpoints only expose shortlink counters and reject post counters.

Accounts

/accounts/accounts/stats

Contatori

follower_count

Nota: Use the full counter path on Accounts endpoints, for example sort=-counters.total.follower_count or filters=counters.graph.counter:follower_count. The counter sort shorthand (sort=-follower_count) also resolves. The documented name is resolved to the stored account counter internally; historical internal counter names are intentionally omitted. Post counters such as engagement, reach, or videoViews are invalid for Accounts.

4.Errori

Imposta la gestione degli errori sul codice HTTP, non sul testo del messaggio. I codici mantengono il loro significato HTTP abituale.

StatoSignificato
200OKLa richiesta è stata completata con successo.
400Richiesta non validaIl body o i parametri sono malformati o non superano la validazione.
401Non autenticatoIl token Bearer nell'header Authorization manca o non è valido. Controlla il valore del token o rinnovalo dal back office prima di ripetere la chiamata.
403VietatoIl token API è valido ma non ha il permesso di accedere a questa risorsa.
404Non trovatoLa risorsa non esiste o la tua azienda non può vederla.
409ConflittoUna richiesta PUT /posts/bulk su un singolo post invia uno schedule, publishedAt, shortId o status che non si può applicare, oppure un post della richiesta modifica un publishedAt, un postType o un link breve che non è più modificabile. Il messaggio indica il motivo e non viene modificato nulla.
423BloccatoUn post di una richiesta PUT /posts/bulk è stato modificato meno di 3 secondi fa oppure è in corso di modifica o pubblicazione. Se è presente l'header Retry-After, ripeti la richiesta dopo quel numero di secondi; altrimenti tra qualche secondo.
429Troppe richiesteÈ stato raggiunto un rate limit. Se è presente l'header Retry-After, attendi quel numero di secondi prima di riprovare.
500Errore del serverErrore imprevisto del server. Ripeti le letture idempotenti con backoff.

Le risposte di errore restituiscono un body JSON che descrive il problema, ma conta il codice HTTP: imposta la gestione degli errori sul codice, non sul testo del messaggio.

Raccomandazioni per la gestione degli errori

  • Tratta come fallimento ogni stato diverso da 2xx e leggi il body JSON per il contesto.
  • In caso di 401, controlla il valore del token API, rinnovalo se serve e ripeti la richiesta.
  • In caso di 409, leggi il motivo nel messaggio, correggi la richiesta e reinviala subito: una richiesta che non ha modificato alcun post non lascia nessun blocco.
  • In caso di 423, attendi il numero di secondi dell'header Retry-After, al massimo 3 dopo un aggiornamento concluso, e reinvia la stessa richiesta. Un 423 senza Retry-After indica che il post è in corso di pubblicazione: riprova tra qualche secondo.
  • In caso di 429 del rate limiter, attendi il numero di secondi dell'header Retry-After, inviato anche in RateLimit-Reset, prima di riprovare. Un 429 dovuto alla quota YouTube negli endpoint delle playlist non ha Retry-After: riprova più tardi.
  • In caso di 5xx, ripeti le letture idempotenti con backoff esponenziale.

Riferimento

5.Brands

2 Endpoint

Read the brands attached to the authenticated company. Brand IDs are required by publishing and shortlink workflows.

GET/brands/:brandIdBearer richiesto

5.2.Get a brand

Return one brand with optional embedded configuration used for publishing, shortlinks, smart images, and URL parameters.

Parametri di query

CampoTipoDescrizione
fieldsstringComma-separated fields to include.
embedstringCan embed urlParameters, domains, geoblocking, smart image configs, smart message configs, and URLs to stalk.

Campi restituiti

CampoTipoDescrizione
idintUnique brand ID.
namestringBrand name.
shortDomainstringBrand short domain.
domainsBrandDomain[]Configured brand domains when embedded.
urlParametersobject[]URL parameter configuration when embedded.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/brands/1?fields=id,name,shortDomain&embed=domains,urlParameters' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

6.Social accounts

10 Endpoint

Read connected social accounts and quota data required before creating or scheduling posts.

GET/social-accounts/:socialAccountIdBearer richiesto

6.2.Get a social account

Return one connected social account with publishing metadata.

Parametri di query

CampoTipoDescrizione
fieldsstringComma-separated fields to include.

Campi restituiti

CampoTipoDescrizione
idintSocial account ID.
accountIdstringSocial network account/page ID. Pass this value as socialAccount.externalId when creating a post.
namestringAccount display name.
typestringNetwork type.
brandIdintAssociated brand ID.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/social-accounts/42?fields=id,name,type,accountId,brandId' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/social-accounts/publishing-quotaBearer richiesto

6.3.Get publishing quota

Return publishing quota information for selected social accounts before scheduling posts.

Parametri di query

CampoTipoDescrizione
socialAccountsstringComma-separated network|externalId pairs, using type and accountId from the social accounts endpoints (e.g. facebook|123456789,twitter|987654321). Internal social account IDs are not accepted: one malformed item returns 400 Bad Request for the whole request.

Campi restituiti

CampoTipoDescrizione
totalintNumber of social accounts returned in rows.
rowsobject[]Publishing quota of each requested social account. Accounts not found in your company, or whose quota cannot be fetched from the network, are omitted and the request still succeeds.
->externalIdstringAccount ID on the network, as sent in socialAccounts.
->typestringNetwork type, as sent in socialAccounts.
->quotaint|nullPublishing limit reported by the network: the total number of posts allowed in the network's rolling window for Instagram and Threads, or 0 for TikTok when posting is currently blocked. Null when the network reports no per-account limit (all other networks, and TikTok when posting is allowed). The value may be cached for up to 10 minutes.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/social-accounts/publishing-quota?socialAccounts=facebook%7C123456789,twitter%7C987654321' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/social-accounts/youtube/playlistsBearer richiesto

6.4.Get YouTube playlists

Return the playlists of connected YouTube social accounts, to choose the `youtube.playlistIds` of a YouTube post. Playlist items (videos) are not returned.

  • IDs that are not YouTube social accounts of your company are silently left out of the response. When YouTube rejects the request for one account, its entry has an empty playlists array and an error message, and the other accounts are still returned.
  • Playlists of every visibility are returned, with their privacyStatus. They are cached for one hour per account. The cache is cleared after a create, update, or delete through this API; a change made in YouTube Studio appears after the cache expires or with force=true. A playlist created moments ago can be missing from the list until YouTube exposes it: read the list again with force=true.

Parametri di query

CampoTipoDescrizione
socialAccounts*stringComma-separated Nonli social account IDs: the `id` field returned by GET /social-accounts, not the network `accountId`. Any other query parameter, such as page, size, or filters, returns 400 Bad Request.
forceboolBypass the one-hour cache and read the playlists from YouTube.Predefinito: false

Campi restituiti

CampoTipoDescrizione
YouTubeAccountPlaylists[]YouTubeAccountPlaylists[]One entry per requested ID that is a YouTube social account of your company. The response is a JSON array, not a paginated { total, rows } object.
->socialAccountIdintNonli social account ID.
->playlistsobject[]Playlists of the account.
->->idstringYouTube playlist ID, to send in youtube.playlistIds.
->->titlestringPlaylist title.
->->descriptionstringPlaylist description.
->->thumbnailUrlstringPlaylist thumbnail URL.
->->itemCountintNumber of videos in the playlist.
->->privacyStatusstringPlaylist visibility: public, unlisted, or private.
->errorstringPresent only when the playlists of this account could not be read from YouTube.

Esempio

bash
1# 1. Find the Nonli IDs of your YouTube social accounts.
2curl --location 'https://api.nonli.com/v2.1/social-accounts?filters=type:youtube&fields=id,name,type' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN'
4
5# 2. Read the playlists of those accounts, for example IDs 42 and 43.
6curl --location 'https://api.nonli.com/v2.1/social-accounts/youtube/playlists?socialAccounts=42,43' \
7 --header 'Authorization: Bearer YOUR_API_TOKEN'

Esempio di risposta

json
1[
2 {
3 "socialAccountId": 42,
4 "playlists": [
5 {
6 "id": "PLxxxxxxxxxxxxxxxx",
7 "title": "Interviews",
8 "description": "",
9 "thumbnailUrl": "https://i.ytimg.com/vi/xxxxxxxxxxx/hqdefault.jpg",
10 "itemCount": 12,
11 "privacyStatus": "public"
12 }
13 ]
14 },
15 {
16 "socialAccountId": 43,
17 "playlists": [],
18 "error": "Error message returned for this account"
19 }
20]
POST/social-accounts/youtube/playlistsBearer richiesto

6.5.Create a YouTube playlist

Create a playlist on the channel of a connected YouTube social account. The API answers 201 Created with the new playlist. The API-token role must be allowed to manage social accounts. An unknown socialAccountId, or one that is not a YouTube account, returns 404 Not Found.

  • Each call counts against the 24-hour limit of its own endpoint, including calls that fail. When the YouTube quota is exhausted the API returns 429 with a message such as "The YouTube quota is reached, please retry later."; this 429 does not come from the Nonli rate limiter and carries no Retry-After header. When the YouTube authorization of the account has expired the API returns 400 and the account must be reconnected.
  • A playlist created through this API can be updated and deleted right away, even before YouTube exposes it everywhere. During that short delay the update response has a null itemCount and the playlist items endpoint returns an empty page.
Rate limit20 calls every 24 hours per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 21st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
socialAccountId*intNonli ID of the YouTube social account that owns the playlist: the `id` field returned by GET /social-accounts, not the network `accountId`.
title*stringPlaylist title, 1 to 150 characters.
descriptionstringPlaylist description, up to 5,000 characters.
privacyStatusstringPlaylist visibility: public, unlisted, or private.Predefinito: private

Campi restituiti

CampoTipoDescrizione
idstringYouTube playlist ID.
titlestringPlaylist title.
descriptionstringPlaylist description.
thumbnailUrlstringPlaylist thumbnail URL.
itemCountintNumber of videos in the playlist.
privacyStatusstringPlaylist visibility: public, unlisted, or private.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/social-accounts/youtube/playlists' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "socialAccountId": 42,
6 "title": "Interviews",
7 "description": "Every interview of the season",
8 "privacyStatus": "unlisted"
9 }'

Esempio di risposta

json
1{
2 "id": "PLxxxxxxxxxxxxxxxx",
3 "title": "Interviews",
4 "description": "Every interview of the season",
5 "thumbnailUrl": "https://i.ytimg.com/img/no_thumbnail.jpg",
6 "itemCount": 0,
7 "privacyStatus": "unlisted"
8}
PUT/social-accounts/youtube/playlists/:playlistIdBearer richiesto

6.6.Update a YouTube playlist

Change the title, description, or visibility of a playlist of a connected YouTube social account. Fields left out keep their current value. The API answers with the updated playlist.

  • Each call counts against the 24-hour limit of its own endpoint, including calls that fail. When the YouTube quota is exhausted the API returns 429 with a message such as "The YouTube quota is reached, please retry later."; this 429 does not come from the Nonli rate limiter and carries no Retry-After header. When the YouTube authorization of the account has expired the API returns 400 and the account must be reconnected.
  • A playlist that does not exist or belongs to another channel returns 404 Not Found.
  • A playlist created through this API can be updated and deleted right away, even before YouTube exposes it everywhere. During that short delay the update response has a null itemCount and the playlist items endpoint returns an empty page.
Rate limit20 calls every 24 hours per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 21st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
socialAccountId*intNonli ID of the YouTube social account that owns the playlist: the `id` field returned by GET /social-accounts, not the network `accountId`.
titlestringNew playlist title, 1 to 150 characters.
descriptionstringNew playlist description, up to 5,000 characters.
privacyStatusstringNew playlist visibility: public, unlisted, or private.

Campi restituiti

CampoTipoDescrizione
idstringYouTube playlist ID.
titlestringPlaylist title.
descriptionstringPlaylist description.
thumbnailUrlstringPlaylist thumbnail URL.
itemCountint|nullNumber of videos in the playlist; null while YouTube does not return a playlist that was just created.
privacyStatusstringPlaylist visibility: public, unlisted, or private.

Esempio

bash
1curl --location --request PUT 'https://api.nonli.com/v2.1/social-accounts/youtube/playlists/PLxxxxxxxxxxxxxxxx' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "socialAccountId": 42,
6 "title": "Interviews 2026",
7 "privacyStatus": "public"
8 }'
DELETE/social-accounts/youtube/playlists/:playlistIdBearer richiesto

6.7.Delete a YouTube playlist

Delete a playlist of a connected YouTube social account. The videos themselves are not deleted. The API answers 204 No Content.

  • Each call counts against the 24-hour limit of its own endpoint, including calls that fail. When the YouTube quota is exhausted the API returns 429 with a message such as "The YouTube quota is reached, please retry later."; this 429 does not come from the Nonli rate limiter and carries no Retry-After header. When the YouTube authorization of the account has expired the API returns 400 and the account must be reconnected.
  • A playlist that does not exist or belongs to another channel returns 404 Not Found.
  • A playlist created through this API can be updated and deleted right away, even before YouTube exposes it everywhere. During that short delay the update response has a null itemCount and the playlist items endpoint returns an empty page.
Rate limit20 calls every 24 hours per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 21st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri di query

CampoTipoDescrizione
socialAccountId*intNonli ID of the YouTube social account that owns the playlist: the `id` field returned by GET /social-accounts, not the network `accountId`. Send it in the query string, without a request body.

Esempio

bash
1curl --location --request DELETE 'https://api.nonli.com/v2.1/social-accounts/youtube/playlists/PLxxxxxxxxxxxxxxxx?socialAccountId=42' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/social-accounts/youtube/playlists/:playlistId/itemsBearer richiesto

6.8.List the videos of a YouTube playlist

Return one page of the videos of a YouTube playlist, in playlist order. The result is read from YouTube on each call and is not cached.

  • Page through the playlist with pageToken: send the nextPageToken of the previous response until it is null. An unknown or inaccessible playlist returns 404 Not Found; a playlist created through this API moments ago returns an empty page instead.
  • When the YouTube quota is exhausted the API returns 429 with a message such as "The YouTube quota is reached, please retry later."; this 429 does not come from the Nonli rate limiter and carries no Retry-After header. When the YouTube authorization of the account has expired the API returns 400 and the account must be reconnected.

Parametri di query

CampoTipoDescrizione
socialAccountId*intNonli ID of the YouTube social account that owns the playlist: the `id` field returned by GET /social-accounts, not the network `accountId`.
sizeintNumber of videos per page, from 1 to 50.Predefinito: 50
pageTokenstringToken of the page to read, taken from nextPageToken.

Campi restituiti

CampoTipoDescrizione
itemsobject[]Videos of the requested page.
->idstringPlaylist item ID, to send as itemId when moving the video.
->videoIdstringYouTube video ID.
->positionintZero-based position of the video in the playlist.
->titlestringVideo title.
->addedAtdateDate the video was added to the playlist.
->videoPublishedAtdatePublication date of the video.
->privacyStatusstringVisibility of the video.
nextPageTokenstring|nullToken of the next page, or null on the last page.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/social-accounts/youtube/playlists/PLxxxxxxxxxxxxxxxx/items?socialAccountId=42&size=50' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

Esempio di risposta

json
1{
2 "items": [
3 {
4 "id": "UExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
5 "videoId": "xxxxxxxxxxx",
6 "position": 0,
7 "title": "Interview of the week",
8 "addedAt": "2026-10-01T09:30:00Z",
9 "videoPublishedAt": "2026-09-30T16:00:00Z",
10 "privacyStatus": "public"
11 }
12 ],
13 "nextPageToken": null
14}
PUT/social-accounts/youtube/playlists/:playlistId/items/:itemIdBearer richiesto

6.9.Move a video in a YouTube playlist

Move a video to another position of a YouTube playlist. The API answers with the moved item and its new position.

  • Each call counts against the 24-hour limit of its own endpoint, including calls that fail. When the YouTube quota is exhausted the API returns 429 with a message such as "The YouTube quota is reached, please retry later."; this 429 does not come from the Nonli rate limiter and carries no Retry-After header. When the YouTube authorization of the account has expired the API returns 400 and the account must be reconnected.
  • The playlist must be sorted manually in YouTube: otherwise the call returns 400 Bad Request with a message such as "This playlist is not sorted manually in YouTube: set its order to manual before moving a video." A position beyond the end of the playlist also returns 400, and an unknown playlist or item returns 404 Not Found.
Rate limit20 calls every 24 hours per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 21st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
socialAccountId*intNonli ID of the YouTube social account that owns the playlist: the `id` field returned by GET /social-accounts, not the network `accountId`.
position*intNew zero-based position of the video, from 0 to 4999.

Campi restituiti

CampoTipoDescrizione
idstringPlaylist item ID, to send as itemId when moving the video.
videoIdstringYouTube video ID.
positionintZero-based position of the video in the playlist.
titlestringVideo title.
addedAtdateDate the video was added to the playlist.
videoPublishedAtdatePublication date of the video.
privacyStatusstringVisibility of the video.

Esempio

bash
1# itemId is the id of the item returned by the playlist items endpoint, not the video ID.
2curl --location --request PUT 'https://api.nonli.com/v2.1/social-accounts/youtube/playlists/PLxxxxxxxxxxxxxxxx/items/UExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
3 --header 'Content-Type: application/json' \
4 --header 'Authorization: Bearer YOUR_API_TOKEN' \
5 --data '{
6 "socialAccountId": 42,
7 "position": 0
8 }'
GET/social-accounts/pinterest/boardsBearer richiesto

6.10.Get Pinterest boards

Return the boards of connected Pinterest social accounts, to choose the `pinterest.boardId` of a Pinterest post.

  • IDs that are not Pinterest social accounts of your company are silently left out of the response. When Pinterest rejects the request for one account, its boards array is empty.
  • Boards are cached for one hour per account; set force=true to read them from Pinterest again.

Parametri di query

CampoTipoDescrizione
socialAccounts*stringComma-separated Nonli social account IDs: the `id` field returned by GET /social-accounts, not the network `accountId`. Any other query parameter, such as page, size, or filters, returns 400 Bad Request.
forceboolBypass the one-hour cache and read the boards from Pinterest.Predefinito: false

Campi restituiti

CampoTipoDescrizione
PinterestAccountBoards[]PinterestAccountBoards[]One entry per requested ID that is a Pinterest social account of your company. The response is not a paginated { total, rows } object. Send only valid Pinterest account IDs: when an ID placed before a valid one is left out, the entries currently come back as a JSON object keyed by position instead of an array.
->socialAccountIdintNonli social account ID.
->boardsobject[]Boards of the account, as returned by Pinterest, including `id` (to send in pinterest.boardId) and `name`.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/social-accounts/pinterest/boards?socialAccounts=42' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

7.Posts

15 Endpoint

Search, create, update, and delete social posts. Bulk write endpoints accept arrays so integrations can synchronize several posts at once.

To export the posts of a connected network:

  1. 1Call GET /posts with filters=socialAccount.type:youtube;publishedAt:2026-06-01T00:00:00Z..2026-07-01T00:00:00Z, replacing the network and the range. The upper bound of the range is excluded. A brandId filter is optional: without it, the posts of every account the token can access are returned.
  2. 2Request only the fields you need with fields, set size=100, and increase page from 0 until a page returns fewer rows than size. total gives the number of matching posts.
  3. 3Keep (page + 1) × size at or below 10,000: for a larger export, split the period into shorter publishedAt ranges and paginate each one.

Post dei tuoi account collegati o del social listening? Leggi la guida al social listening

GET/posts/:postIdBearer richiesto

7.2.Get a post

Return one post by ID with optional filters and field selection.

Parametri di query

CampoTipoDescrizione
fieldsstringComma-separated fields to include.
filtersstringOptional filters used to optimize lookup, such as createdAt.
embedstringOptional related data to embed.

Campi restituiti

CampoTipoDescrizione
idstringPost ID.
createdAtdateCreation date.
publishedAtdatePublication date.
messagestringPost message.
postTypestringPost type.
socialAccountSocialAccountTarget social account.
statusintPost status.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/4283ab2d-e3dd-7b1a-7cd6-bbcc71e1xxxx?fields=id,message,postType,publishedAt,socialAccount,status' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/posts/categoriesBearer richiesto

7.3.Get post categories

Return category aggregations used by post filters and dashboards.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.
Rate limit200 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 201st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri di query

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of categories found.
rows.keystringCategory value.
rows.doc_countintNumber of posts in the category.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/categories?filters=brandId:1&size=100' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/posts/brandsBearer richiesto

7.4.Get post brands

Return brand aggregations for posts visible to the API token role.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.
Rate limit200 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 201st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri di query

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of brands found.
rows.keystringBrand identifier or name.
rows.doc_countintNumber of posts for the brand.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/brands?filters=publishedAt:-30%20days..now&size=100' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/posts/usersBearer richiesto

7.5.Get post users

Return user aggregations for post author and collaboration filters.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.

Parametri di query

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of users found.
rows.keystringUser identifier or name.
rows.doc_countintNumber of posts for the user.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/users?filters=brandId:1&size=100' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/posts/languagesBearer richiesto

7.6.Get post languages

Return language aggregations for post filters.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.
Rate limit200 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 201st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri di query

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of languages found.
rows.keystringLanguage code.
rows.doc_countintNumber of posts for the language.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/languages?filters=brandId:1&size=50' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/posts/histogramBearer richiesto

7.7.Get post histogram

Return time-bucketed post counts for graphing.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.
  • Without a status value in filters, posts of every status except 404 (deleted) and 410 (outdated) are counted, including drafts, scheduled posts, posts awaiting review, and failed publications. For figures consistent with published content, add filtersOr=status:200,203,204: published, publishing, and waiting to retry, the statuses of the "Published" group in the Nonli back office.
  • A brandId value in filters or filtersOr selects the posts of the social accounts currently attached to that brand, whether they were published through Nonli or imported from the network: the brandId stored on each post is not used, so imported posts are included and an account moved to another brand takes its history with it. excludes=brandId: still tests the stored field and does not exclude imported posts.

Parametri di query

CampoTipoDescrizione
qstringFull-text search query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.
fieldstringDate field used to build the buckets.Predefinito: publishedAt
baseIntervalstringBucket interval: minute, hour, day, week, month, or year.Predefinito: year
searchForMinPointsCountboolWhen true, the API retries with the next smaller interval while the histogram has fewer than minPointsCount buckets. Set false to keep baseInterval.Predefinito: true
minPointsCountintMinimum number of buckets sought when searchForMinPointsCount is true.Predefinito: 10
keyAsDateboolReturn bucket keys as dates. Set false to receive millisecond timestamps.Predefinito: true
timezonestringTimezone of the buckets, as an ISO 8601 UTC offset (+01:00) or a TZ database name (Europe/Paris).

Campi restituiti

CampoTipoDescrizione
totalintNumber of buckets.
intervalstringInterval actually used for the buckets.
rowsobject[]Histogram buckets.
rows.keystringBucket date, or millisecond timestamp when keyAsDate is false.
rows.valueintNumber of posts in the bucket.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/histogram?filters=brandId:1;publishedAt:2026-05-01T00:00:00Z..2026-05-28T00:00:00Z&filtersOr=status:200,203,204&field=publishedAt&baseInterval=day&searchForMinPointsCount=false' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/posts/aggregationBearer richiesto

7.8.Get post aggregation

Return generic aggregations for post dashboards and filter facets.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.
  • Without a status value in filters, posts of every status except 404 (deleted) and 410 (outdated) are counted, including drafts, scheduled posts, posts awaiting review, and failed publications. For figures consistent with published content, add filtersOr=status:200,203,204: published, publishing, and waiting to retry, the statuses of the "Published" group in the Nonli back office.
  • A brandId value in filters or filtersOr selects the posts of the social accounts currently attached to that brand, whether they were published through Nonli or imported from the network: the brandId stored on each post is not used, so imported posts are included and an account moved to another brand takes its history with it. excludes=brandId: still tests the stored field and does not exclude imported posts.
  • To list the custom tags in use, call GET /posts/aggregation?fields=customTag with a brandId filter: each row is a tag with its number of posts. Tags are case-sensitive. To read the list configured on a brand, call GET /brands/:brandId?fields=id,name,customTags,forceCustomTags.

Parametri di query

CampoTipoDescrizione
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.
sizeintMaximum number of buckets to return.Predefinito: 1000
fieldsstringField to aggregate, for example customTag or socialAccount.type. Several comma-separated fields are aggregated together, with their values joined by "|" in each bucket key.
fieldstringField to aggregate when fields is not set.
intervalstringDate interval, to aggregate a date field as a date histogram.
timezonestringTimezone of the date buckets.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of aggregation buckets.
rows.keystringBucket key.
rows.valueintNumber of posts in the bucket.

Esempio

bash
1# Posts per network of a brand.
2curl --location 'https://api.nonli.com/v2.1/posts/aggregation?field=socialAccount.type&filters=brandId:1&filtersOr=status:200,203,204&size=20' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN'
4
5# Custom tags in use on a brand, with their number of posts.
6curl --location 'https://api.nonli.com/v2.1/posts/aggregation?fields=customTag&filters=brandId:1&filtersOr=status:200,203,204' \
7 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/posts/statsBearer richiesto

7.9.Get post stats

Return aggregated post performance statistics for dashboard widgets.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.
  • This endpoint applies no default status filter: without a status filter, posts of every status are counted, including drafts, scheduled posts, failed publications, and deleted (404) or outdated (410) posts. For figures consistent with published content, add filtersOr=status:200,203,204: published, publishing, and waiting to retry, the statuses of the "Published" group in the Nonli back office.
  • A brandId value in filters or filtersOr selects the posts of the social accounts currently attached to that brand, whether they were published through Nonli or imported from the network: the brandId stored on each post is not used, so imported posts are included and an account moved to another brand takes its history with it. excludes=brandId: still tests the stored field and does not exclude imported posts.

Parametri di query

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
pageintZero-based page number.Predefinito: 0
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
sortstringSort expression. Prefix a field with "-" for descending order. Counter-backed endpoints can sort by counters.total.<field> or by the counter shorthand itself, for example sort=-engagement on post/social data or sort=-click on organic data.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
fieldsstringComma-separated fields to include in each returned item.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
rowsobject[]Stat rows returned by the selected filters.
totalintTotal matching posts or stats rows.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/stats?filters=brandId:1;publishedAt:-30%20days..now&fields=counters.total&size=50' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
POST/posts/checkBearer richiesto

7.10.Check post content

Validate post content before publishing. The BO uses this endpoint to surface publication warnings.

Parametri del body

CampoTipoDescrizione
messagestringPost message to validate.
titlestringPost title.
descriptionstringPost description.
socialAccount.typestringTarget social network type.
brandIdintBrand ID.

Campi restituiti

CampoTipoDescrizione
validboolWhether the post content passed validation.
warningsobject[]Warnings returned by publication checks.
errorsobject[]Blocking validation errors.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/check' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "brandId": 1,
6 "message": "Post copy",
7 "socialAccount.type": "linkedin"
8 }'
GET/posts/tagsBearer richiesto

7.11.Get post tags

Return the most used values of the post `tag` field that match a prefix, for autocomplete and reporting filters.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.
  • Only published posts (status 200) are counted.
  • This endpoint aggregates the tag field, not customTag: tags written in customTag are not returned. To list the custom tags in use, call GET /posts/aggregation?fields=customTag with a brandId filter: each row is a tag with its number of posts. Tags are case-sensitive. To read the list configured on a brand, call GET /brands/:brandId?fields=id,name,customTags,forceCustomTags.
  • Prefix matching is case-sensitive.
Rate limit200 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 201st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri di query

CampoTipoDescrizione
sizeintCount of results to return.Predefinito: 10
prefix*stringTag prefix, case-sensitive. The parameter must be present; send it empty (prefix=) to match every tag.
selected*stringAlready selected tags, comma separated: only posts that carry every selected tag are counted, and the selected tags are removed from the results. The parameter must be present; send it empty (selected=) when no tag is selected.
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of items found.
rows.keystringTag.
rows.doc_countintNumber of published posts where the tag was found.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/tags?prefix=&selected=&filters=brandId:1;publishedAt:-30%20days..now&size=100' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/posts/topicsBearer richiesto

7.12.Get post topics

Return the most used post topics that match a prefix, for autocomplete and reporting filters.

  • Post-backed endpoints accept the documented post counters in counter filters and counter sorts, and responses echo the counter names the request used. Historical counter names replaced by public counter names are intentionally omitted.
  • Only published posts (status 200) are counted. Prefix matching is case-sensitive.
Rate limit200 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 201st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri di query

CampoTipoDescrizione
sizeintCount of results to return.Predefinito: 10
prefix*stringTopic prefix, case-sensitive. The parameter must be present; send it empty (prefix=) to match every topic.
selected*stringAlready selected topics, comma separated: only posts that carry every selected topic are counted, and the selected topics are removed from the results. The parameter must be present; send it empty (selected=) when no topic is selected.
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of items found.
rows.keystringTopic.
rows.doc_countintNumber of published posts where the topic was found.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/topics?prefix=&selected=&filters=brandId:1;publishedAt:-30%20days..now;socialAccount.type:facebook&size=100' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
POST/posts/bulkBearer richiesto

7.13.Create posts in bulk

Create one or more posts: submit them for review, publish them immediately, or schedule them. The API-token role must grant `post.write` and access to every requested brand and social account.

  • Post status is constrained by the API-token role. review always remains pending validation, and read access to the target is sufficient to create a post for review. A publish request is automatically downgraded to review when the role cannot publish directly. Tokens without post.write or access to the requested resources are rejected.
  • The creator identity is server-managed. Posts created with a Bearer token use API <token name> as author and have no userId. Client-supplied author identity fields are ignored, so an API integration cannot impersonate a Nonli user.
  • When the brand has forceCustomTags enabled, a request that attaches a customTag value absent from the brand customTags list fails as a whole with 400 Bad Request, and no post is written. Matching is exact and case-sensitive. Tags already stored on an updated post are accepted. Add the tag to the brand list first, with PUT /brands/:brandId and the complete customTags array, which replaces the stored list.
  • When a post published through Nonli has also been imported from the network as a separate post, Nonli removes the imported copy once the publication succeeds, so the post appears once. Duplicates created before this behavior shipped are not removed.
  • Build a thread sequentially. Set thread.activated=true on every segment. The root omits isChild, parent, and origin; every later segment sets isChild=true, parent to the immediately previous segment ID, and origin to the root ID. For the first child, parent and origin therefore have the same root ID. After adding each child, read the root and verify its ordered thread.children list: a successful HTTP response alone does not prove that the chain is complete.
  • On networks with first-comment support, put the article link in post.comment. Threads and Bluesky do not publish a native first comment, so a hand-built chain must instead attach the shortlink to the root with shortlink.mappingId and put the resolved public URL only in the final segment message. Create that final segment without the URL, update it with the root response values https://ROOT_SHORT_DOMAIN/ROOT_SHORT_ID, then read the root and final segment back before publication. Do not also place that URL in a root first comment.
  • On Threads and Bluesky, Nonli can automatically convert an active first comment into the first child when the root has neither an activated thread nor existing children. [SHORTLINK] is expanded only during that conversion and only when the first comment has a shortlink. It is not expanded in segments created directly through /posts/bulk; hand-built segments must contain the resolved URL.
  • Validate every segment against its destination network before creating the chain. Threads currently allows 500 characters per segment; Bluesky allows 300 graphemes. Mentions and links contribute to those limits, including the resolved shortlink added to the final segment.
Rate limit200 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 201st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
post*Post[]Array of post objects.
->brandId*intBrand ID used to create the post.
->postType*stringNonli post type: text, video, link, photo, album, flying, reel, document, etc.
->socialAccountobjectTarget connected social account. Required for standard posts sent for review or publication; omit only when the post type has an implicit or special target.
->->externalIdstringNetwork ID of the target connected social account. Use the accountId returned by GET /social-accounts.
->->publisherIdstringPublisher ID used when the target account publishes through another account.
->->typestringTarget social network type, such as facebook, instagram, linkedin, or tiktok.
->->subTypestringTarget account subtype, such as page or group.
->statusstringCreation mode: review or publish. API tokens cannot use draft: the whole request fails with 400; use review. The default depends on the token role permissions.
->shortlinkobjectRelated shortlink. Required when postType is flying.
->->mappingIdstringRelated shortlink ID.
->messagestringPost message.
->titlestringDisplayed post title.
->descriptionstringLink or media description.
->commentobjectOptional first comment. On networks with native first-comment support, put the article link in this comment. On Threads and Bluesky, an active comment can be converted to the first thread child when the root has no active thread or children.
->->activatedboolSet true to publish or convert the first comment.
->->messagestringFirst-comment text. `[SHORTLINK]` is resolved only during the documented automatic comment-to-thread conversion.
->threadobjectThread relationship. Create the root first, then create each child after the previous segment succeeds.
->->activatedboolSet true on the root and every child to activate the thread relationship.
->->isChildboolSet true on child segments. Omit on the root.
->->parentstringUUID of the immediately previous segment. For the first child, this is the root UUID.
->->originstringUUID of the root segment. It remains the same for every child.
->scheduleobjectOptional publication schedule. With status publish, omit this object to publish immediately.
->->startDatedateScheduled publication start date.
->->endDatedateOptional end of the scheduling optimization range.
->imageobject[]Post images.
->->fontanSlugstringFontan image slug.
->->urlstringExternal image URL.
->videoobject[]Post videos.
->->mappingIdstringUploaded video ID.

Campi restituiti

CampoTipoDescrizione
Post[]Post[]Created posts with status and additional values.
->idstringCreated post UUID.
->authorstringServer-managed author. Bearer-created posts use API followed by the token name.
->userIdint|nullNull for posts created with an API token.
->statusintStored status. Common values are 201 for draft, 210 for awaiting review, 202 for pending or scheduled publication, 203 for publishing, and 200 for published. A publish request may return 210 when the token role causes a review downgrade.
->shortDomainstring|nullDomain of the generated public shortlink. Combine with shortId when resolving the URL for a hand-built thread.
->shortIdstring|nullPath identifier of the generated public shortlink.
->threadobject|nullStored thread relationship.
->->activatedboolWhether the thread relationship is active.
->->isChildboolWhether this post is a child segment.
->->parentstring|nullUUID of the immediately previous segment.
->->originstring|nullUUID of the root segment.
->->childrenstring[]On the root, ordered UUIDs of every descendant segment, from the first child to the final segment.

Esempio

bash
1# A. Create an ordinary post for review.
2curl --location 'https://api.nonli.com/v2.1/posts/bulk' \
3 --header 'Content-Type: application/json' \
4 --header 'Authorization: Bearer YOUR_API_TOKEN' \
5 --data '{
6 "post": [{
7 "brandId": 1,
8 "postType": "text",
9 "message": "Hello from the Nonli API",
10 "status": "review",
11 "socialAccount": {
12 "externalId": "SOCIAL_ACCOUNT_ACCOUNT_ID",
13 "type": "facebook"
14 }
15 }]
16 }'
17
18# B. Build a three-segment thread.
19# 1. Create the root with the shortlink mapping.
20curl --location 'https://api.nonli.com/v2.1/posts/bulk' \
21 --header 'Content-Type: application/json' \
22 --header 'Authorization: Bearer YOUR_API_TOKEN' \
23 --data '{
24 "post": [
25 {
26 "brandId": 1,
27 "postType": "text",
28 "message": "The opening segment",
29 "status": "review",
30 "shortlink": { "mappingId": "SHORTLINK_MAPPING_ID" },
31 "socialAccount": {
32 "externalId": "SOCIAL_ACCOUNT_ACCOUNT_ID",
33 "type": "threads"
34 },
35 "thread": { "activated": true }
36 }
37 ]
38 }'
39
40# Save the returned id as ROOT_POST_ID and its shortDomain/shortId values.
41# 2. Create the middle segment. The first child's parent is also the origin.
42curl --location 'https://api.nonli.com/v2.1/posts/bulk' \
43 --header 'Content-Type: application/json' \
44 --header 'Authorization: Bearer YOUR_API_TOKEN' \
45 --data '{
46 "post": [{
47 "brandId": 1,
48 "postType": "text",
49 "message": "The middle segment",
50 "status": "review",
51 "socialAccount": { "externalId": "SOCIAL_ACCOUNT_ACCOUNT_ID", "type": "threads" },
52 "thread": {
53 "activated": true,
54 "isChild": true,
55 "parent": "ROOT_POST_ID",
56 "origin": "ROOT_POST_ID"
57 }
58 }]
59 }'
60
61# Save the returned id as MIDDLE_POST_ID.
62# Verify that the root lists the first child before continuing.
63curl --location 'https://api.nonli.com/v2.1/posts?filters=id:ROOT_POST_ID&fields=id,thread' \
64 --header 'Authorization: Bearer YOUR_API_TOKEN'
65
66# 3. Create the final segment without the URL. Its parent is the middle segment.
67curl --location 'https://api.nonli.com/v2.1/posts/bulk' \
68 --header 'Content-Type: application/json' \
69 --header 'Authorization: Bearer YOUR_API_TOKEN' \
70 --data '{
71 "post": [{
72 "brandId": 1,
73 "postType": "text",
74 "message": "Read the complete story:",
75 "status": "review",
76 "socialAccount": { "externalId": "SOCIAL_ACCOUNT_ACCOUNT_ID", "type": "threads" },
77 "thread": {
78 "activated": true,
79 "isChild": true,
80 "parent": "MIDDLE_POST_ID",
81 "origin": "ROOT_POST_ID"
82 }
83 }]
84 }'
85
86# Save the returned id as FINAL_POST_ID and verify the complete child order.
87curl --location 'https://api.nonli.com/v2.1/posts?filters=id:ROOT_POST_ID&fields=id,thread' \
88 --header 'Authorization: Bearer YOUR_API_TOKEN'
89
90# Then add the resolved root URL to the final segment.
91curl --request PUT --location 'https://api.nonli.com/v2.1/posts/bulk' \
92 --header 'Content-Type: application/json' \
93 --header 'Authorization: Bearer YOUR_API_TOKEN' \
94 --data '{
95 "post": [{
96 "id": "FINAL_POST_ID",
97 "message": "Read the complete story: https://ROOT_SHORT_DOMAIN/ROOT_SHORT_ID"
98 }]
99 }'
100
101# 4. Read back the root and final segment. Verify the ordered root thread.children
102# list and the final resolved URL before publication. This GET returns the usual
103# paginated { "total": ..., "rows": [...] } search response.
104curl --location 'https://api.nonli.com/v2.1/posts?filtersOr=id:ROOT_POST_ID,FINAL_POST_ID&fields=id,author,userId,status,postType,message,shortDomain,shortId,thread' \
105 --header 'Authorization: Bearer YOUR_API_TOKEN'

Esempio di risposta

json
1[
2 {
3 "id": "ROOT_POST_ID",
4 "author": "API Website publisher",
5 "userId": null,
6 "status": 210,
7 "postType": "text",
8 "message": "The opening segment",
9 "shortDomain": "non.li",
10 "shortId": "abc123",
11 "thread": {
12 "activated": true,
13 "isChild": false,
14 "parent": null,
15 "origin": null,
16 "children": []
17 }
18 }
19]
PUT/posts/bulkBearer richiesto

7.14.Update posts in bulk

Update a collection of posts. Send post.id and, when available, createdAt to optimize lookup.

  • Send each post with id, its createdAt when known, and only the fields you change. createdAt locates the post and is never modified; send it as returned by the API, with or without milliseconds. Accepted post fields: socialAccount (externalId, publisherId, type, subType), brandId, tag, customTag, postType, subType, shortlink.mappingId, source.documentId, title, message, description, image, video, media, schedule (startDate, endDate), publishedAt, createdAt, breakingNews, callToAction, shortId, comment (activated, message, pictureUrl), status, isAiGenerated, sponsor, location, geoblocking.country, commentEnabled, duetEnabled, stitchEnabled, madeForKids, threads, visibility, collaborator, pinterest.boardId, youtube.playlistIds, commercialContent, and tiktok.anchor. Each video item accepts url, mappingId, embed, sourceExternalId, title, tag, thumbnail, shareToFeed, isAiGenerated, duration, height, width, and size.
  • Any other key, at any level, makes the whole request fail with 400 Bad Request and an "Unexpected item(s)" message. This includes read-only fields returned by GET /posts/:postId, such as socialAccount fields other than externalId, publisherId, type, and subType, or video[].externalId: do not send back a post object read from the API unchanged.
  • Submitted schedule, publishedAt, shortId, and status values are either applied or rejected with a reason: the API never answers 200 while leaving one of them unapplied. A request that updates a single post and is rejected answers 409 Conflict with the reason in the JSON body, for example {"messages":{"errors":["The publication date (publishedAt) is in the past"]}}, and nothing is updated; an error that has its own status, such as 403, 404, or 423, keeps it. In a request that updates several posts the API answers 200: a rejected post is returned as its id and an error message only, and the other posts are updated. A post whose update failed on an internal error is also returned as its id and error, with a 200 answer, even alone.
  • Scheduling a post that is not published yet: publishedAt alone moves the publication to that date and extends the schedule range to contain it; schedule.startDate without endDate is a fixed publication date; an empty schedule object publishes now. A new publishedAt that is not in the future is rejected with 409, unless the post is published now or the value lies inside a schedule range sent in the same request. An endDate before the startDate, or a schedule ending more than 2 minutes in the past, is rejected with 409. When the schedule of a post that is not published has already elapsed, content changes are saved without rescheduling it, and the returned post carries an error field; a shortId change, a status change, or any update of a post awaiting review is rejected with 409 until a new schedule is sent.
  • On a post already published, a different publishedAt or different schedule dates are rejected with 409. Once publication has started (status 203, 204, or 200), changing postType or the shortlink is rejected with 409 as well. A publishedAt, postType, or shortlink change that can no longer be made fails the whole request with 409, even when the request updates several posts. A status of review that the post can no longer take is rejected with 409; API tokens cannot send draft, which fails the whole request with 400. An invalid shortId, or one already in use, is rejected with 409.
  • When the brand has forceCustomTags enabled, a request that attaches a customTag value absent from the brand customTags list fails as a whole with 400 Bad Request, and no post is written. Matching is exact and case-sensitive. Tags already stored on an updated post are accepted. Add the tag to the brand list first, with PUT /brands/:brandId and the complete customTags array, which replaces the stored list.
  • There is no maximum number of posts per call, and one call counts as one call against the rate limit whatever the number of posts. A very large batch can exceed the server time limits and fail, so split large batches into several calls.
  • After an update has been applied, the same post cannot be updated again for 3 seconds: a request on it during that time fails as a whole with 423 Locked, a Retry-After header giving the real number of seconds left, and a JSON body such as {"messages":{"errors":["The post is being edited, please try again in 3s."]}}. Wait for that number of seconds, then send the request again. While another update of the post is still running, the delay can reach 30 seconds. A request in which no post was updated holds no lock: the corrected request can be sent at once. A post being published also answers 423, without Retry-After: retry a few seconds later.
  • With updatePostOnNetwork=false, Nonli updates the post in Nonli only and sends nothing to the social network: no update of an already published post and no Facebook crosspost preparation. The trace of a published post then records "Update post in Nonli only, not sent to social network" instead of "Update post in social network". The flag concerns the update itself: it does not cancel the publication of a post that is not published yet, which stays in the publication scheduler and is published at its scheduled date. With updatePostOnNetwork=false, YouTube playlists are not changed either.
  • On a published YouTube video updated on the network (updatePostOnNetwork left to true), sending youtube.playlistIds synchronizes the playlists that Nonli manages for this post: the IDs sent now and the IDs previously stored on the post. Nonli adds the video to a requested playlist only when YouTube reports it absent, so no duplicate entry is created, and removes it from a previously stored playlist that is no longer requested. Playlists the video was added to outside Nonli are never changed. Omit youtube.playlistIds to leave playlists untouched; send an empty array to remove the video from the stored playlists. A playlist error does not fail the update: it is recorded in the post trace.
  • Updating a published YouTube video without visibility does not reset it to a default: Nonli sends the visibility stored on the post, or keeps the one YouTube holds when none is stored. On a post already published, the network ID of each video is kept whatever video[].url is sent.
Rate limit200 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 201st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
checkBeforeUpdateboolCheck posts before updating them and skip update if checks fail.
updatePostOnNetworkboolSend the update to the social network. Set false to update the post in Nonli only: nothing is sent to the network.Predefinito: true
post*Post[]Array of post objects to update. Only the fields listed in the notes are accepted.
->id*stringPost ID.
->createdAtdateCreation date as returned by the API. It locates the post faster and is never modified.
->shortIdstringNew short ID.
->scheduleobjectScheduling information: startDate and optional endDate. An empty object publishes now. See the notes for the scheduling rules.
->publishedAtdateNew publication date of a post that is not published yet. See the notes for the scheduling rules.
->statusstringreview or publish. A review that the post can no longer take is rejected with 409. API tokens cannot use draft: the whole request fails with 400; use review.
->messagestringUpdated post message.

Campi restituiti

CampoTipoDescrizione
Post[]Post[]One entry per requested post: the updated post with its applied values, or its id and an error message only when its update was rejected.

Esempio

bash
1curl --location --request PUT 'https://api.nonli.com/v2.1/posts/bulk' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "post": [
6 {
7 "id": "4283ab2d-e3dd-7b1a-7cd6-bbcc71e1xxxx",
8 "shortId": "new-short-id",
9 "schedule": {}
10 }
11 ]
12 }'
DELETE/posts/bulkBearer richiesto

7.15.Delete posts in bulk

Delete a collection of posts. The async flag can be used for background deletion.

Rate limit100 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 101st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
post*Post[]Array of posts to delete.
->id*stringPost ID to delete.
->socialAccountExternalIdstringExternal social account ID.
->createdAtdateCreation date of the post to delete.
asyncboolDelete posts asynchronously.Predefinito: false

Campi restituiti

CampoTipoDescrizione
Post[]Post[]Deleted posts or per-item errors.

Esempio

bash
1curl --location --request DELETE 'https://api.nonli.com/v2.1/posts/bulk' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "post": [
6 {
7 "id": "4283ab2d-e3dd-7b1a-7cd6-bbcc71e1xxxx",
8 "createdAt": "2024-11-26T10:07:36Z"
9 }
10 ]
11 }'

9.Themes

6 Endpoint

Create and manage saved theme filters used by listening, publishing, and automation workflows.

GET/themes/:themeIdBearer richiesto

9.2.Get a theme

Return one saved theme by ID.

Parametri di query

CampoTipoDescrizione
fieldsstringComma-separated fields to include.
embedstringUse embed=roles to include associated roles.

Campi restituiti

CampoTipoDescrizione
idintUnique theme ID.
namestringName.
valuestringJSON-encoded theme filters and section configuration.
queryStringsstringJSON-encoded object of prebuilt query strings for matching sections.
scopestringVisibility scope.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/themes/123?fields=id,name,value,queryStrings,scope&embed=roles' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
POST/themesBearer richiesto

9.3.Create a theme

Create a saved theme. The API stores value and queryStrings as JSON-encoded strings and does not derive one from the other.

Rate limit20 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 21st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
name*stringTheme name.
typestringTheme type: listening or post.Predefinito: listening
scopestringVisibility scope allowed by the API token role: team, bot, or public. Private is reserved for user sessions.Predefinito: team
langstringTheme language: en or fr.Predefinito: en
value*stringJSON-encoded theme filters and section configuration.
queryStringsstringJSON-encoded object containing the prebuilt query for each enabled section. Supply it when the theme must return results.
rolesAclRoles[]Roles allowed to use a team-scoped theme.

Campi restituiti

CampoTipoDescrizione
idintCreated theme ID.
namestringTheme name.
valuestringJSON-encoded theme filters and section configuration.
queryStringsstringJSON-encoded object of prebuilt query strings.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/themes' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "name": "Editorial performance",
6 "type": "listening",
7 "scope": "team",
8 "lang": "fr",
9 "value": "{\"Sections\":{\"social\":true,\"leaderboard\":true},\"Search\":\"\",\"Filters\":{\"listening\":{\"social\":{\"filters\":{\"publication\":[\"30 days\"],\"socialAccount.type\":\"instagram\"},\"sort\":[{\"field\":\"publishedAt\",\"direction\":\"desc\"}],\"normalization\":false}}}}",
10 "queryStrings": "{\"social\":\"filters=publishedAt:-30 days..now;socialAccount.type:instagram&excludes=subType:story;socialAccount.subType:news,group&qf=title,message,description,category&sort=-publishedAt\",\"leaderboard\":\"filters=publishedAt:-30 days..now;socialAccount.type:instagram&excludes=subType:story;socialAccount.subType:news,group&qf=title,message,description,category&sort=-publishedAt&aggregationField=engagement\"}"
11 }'
PUT/themes/:themeIdBearer richiesto

9.4.Update a theme

Update a saved theme. Every field is optional, so a partial update such as a rename alone is valid. When value changes, send the matching queryStrings because the API does not rebuild it automatically.

Rate limit100 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 101st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
namestringTheme name.
typestringTheme type: listening or post.
scopestringVisibility scope allowed by the API token role: team, bot, or public. Private is reserved for user sessions.
langstringTheme language: en or fr.
defaultboolWhether this is a default public theme. Restricted to authorized roles.
valuestringJSON-encoded theme filters and section configuration.
queryStringsstringJSON-encoded object containing the prebuilt query for each enabled section.
rolesAclRoles[]Roles allowed to use a team-scoped theme.

Campi restituiti

CampoTipoDescrizione
idintUpdated theme ID.
namestringTheme name.
valuestringJSON-encoded theme filters and section configuration.
queryStringsstringJSON-encoded object of prebuilt query strings.

Esempio

bash
1curl --location --request PUT 'https://api.nonli.com/v2.1/themes/123' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "value": "{\"Sections\":{\"social\":true,\"leaderboard\":true},\"Search\":\"\",\"Filters\":{\"listening\":{\"social\":{\"filters\":{\"publication\":[\"30 days\"],\"socialAccount.type\":\"instagram\"},\"sort\":[{\"field\":\"publishedAt\",\"direction\":\"desc\"}],\"normalization\":false}}}}",
6 "queryStrings": "{\"social\":\"filters=publishedAt:-30 days..now;socialAccount.type:instagram&excludes=subType:story;socialAccount.subType:news,group&qf=title,message,description,category&sort=-publishedAt\",\"leaderboard\":\"filters=publishedAt:-30 days..now;socialAccount.type:instagram&excludes=subType:story;socialAccount.subType:news,group&qf=title,message,description,category&sort=-publishedAt&aggregationField=engagement\"}"
7 }'
DELETE/themes/:themeIdBearer richiesto

9.5.Delete a theme

Delete a saved theme.

Rate limit50 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 51st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Campi restituiti

CampoTipoDescrizione
deletedboolWhether the theme was deleted.

Esempio

bash
1curl --location --request DELETE 'https://api.nonli.com/v2.1/themes/123' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/themes/rolesBearer richiesto

9.6.Get theme roles

Return roles that can be associated with team-scoped themes.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of roles found.
rowsAclRoles[]Available roles.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/themes/roles' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

10.Listening

7 Endpoint

Search social-network performance and organic website content signals.

Come il social listening raccoglie i post e come esportarli: leggi la guida

PUT/listening/organicBearer richiesto

10.3.Upsert an organic with videos

Create or update an organic content item keyed by its normalized URL, together with its complete video association collection. Each video can carry its own thumbnail. The API-token role must grant `listening.write`.

  • The video array is the complete association collection of the organic: associations omitted from the request are removed, and an empty array clears the collection. Already imported video assets survive in your company video library and are relinked automatically when a later request references the same sourceId or the same normalized video URL.
  • Each video association must provide exactly one of mappingId (an already imported video asset of your company) or url (an external HTTPS video imported server-side). Video and thumbnail URLs must be HTTPS; they are fetched by the Nonli import pipeline, never by the browser.
  • When at least one external video import is queued, the API returns 202 Accepted instead of 200. The response echoes each association with its import state: linked when the asset is already available, queued while the import runs, or failed.
Rate limit300 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 301st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri del body

CampoTipoDescrizione
url*stringOrganic URL. Its normalized form is the resource key used for the upsert.
canonicalUrlstringCanonical URL of the organic.
titlestringTitle of the organic.
descriptionstringDescription of the organic.
languagestringLanguage of the organic.
typestringOrganic type: article, product, website, etc.
publishedAtdatePublication date.
modifiedAtdateLast modification date.
tagsstring[]Tags of the organic.
categoriesstring[]Categories of the organic.
videoobject[]Complete video association collection, up to 20 associations. Associations omitted here are removed.
->mappingIdstringIdentifier of an already imported video asset of your company. Provide exactly one of mappingId or url.
->urlstringHTTPS URL of an external video to import. Provide exactly one of mappingId or url.
->sourceIdstringIdentifier of the rendition in your source system, used to relink the same asset on later upserts.
->usagestring[]Intended usages of this rendition: standard or short.
->typestringMIME type of the video.
->widthintWidth in pixels.
->heightintHeight in pixels.
->durationfloatDuration in seconds.
->sizeintSize in bytes.
->thumbnailobjectThumbnail of this video.
->->urlstringHTTPS URL of the thumbnail image, fetched server-side.

Campi restituiti

CampoTipoDescrizione
idstringOrganic identifier.
videoobject[]Resolution state of each video association.
->sourceIdstringRendition identifier as provided.
->urlstringVideo URL as provided.
->mappingIdstringIdentifier of the associated video asset.
->statestringImport state: linked, queued, or failed.

Esempio

bash
1curl --location --request PUT 'https://api.nonli.com/v2.1/listening/organic/' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "url": "https://www.example.com/articles/breaking-news",
6 "title": "Breaking news",
7 "type": "article",
8 "publishedAt": "2026-08-31T09:00:00.000Z",
9 "video": [
10 {
11 "url": "https://cdn.example.com/videos/breaking-news.mp4",
12 "sourceId": "breaking-news-standard",
13 "usage": ["standard"],
14 "type": "video/mp4",
15 "width": 1920,
16 "height": 1080,
17 "duration": 42.5,
18 "thumbnail": { "url": "https://cdn.example.com/videos/breaking-news-thumb.jpg" }
19 }
20 ]
21 }'
GET/listening/organic/:organicIdBearer richiesto

10.4.Get organic content

Return one indexed organic content item by ID.

Parametri di query

CampoTipoDescrizione
fieldsstringComma-separated fields to include.

Campi restituiti

CampoTipoDescrizione
idstringOrganic content ID.
canonicalUrlstringCanonical URL.
titlestringTitle.
descriptionstringDescription.
publishedAtdatePublication date.
counters.totalCounterTotal[]Aggregated counters.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/listening/organic/8f14e45fceea167a5a36dedd4bea2543?fields=id,title,canonicalUrl,counters.total' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/listening/organic/statsBearer richiesto

10.5.Get organic stats

Return aggregated Organic Listening performance statistics.

  • Organic Listening endpoints accept standard Organic Listening counters only. Post counters such as engagement, reach, or videoViews are invalid for Organic Listening and return 400 Bad Request.

Parametri di query

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
pageintZero-based page number.Predefinito: 0
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
sortstringSort expression. Prefix a field with "-" for descending order. Counter-backed endpoints can sort by counters.total.<field> or by the counter shorthand itself, for example sort=-engagement on post/social data or sort=-click on organic data.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
fieldsstringComma-separated fields to include in each returned item.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
rowsobject[]Stat rows returned by the selected filters.
totalintTotal matching content or stats rows.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/listening/organic/stats?filters=type:article;publishedAt:-30%20days..now&fields=counters.total' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/listening/organic/aggregationBearer richiesto

10.6.Get organic aggregation

Return generic aggregations for Organic Listening dashboards and filters.

  • Organic Listening endpoints accept standard Organic Listening counters only. Post counters such as engagement, reach, or videoViews are invalid for Organic Listening and return 400 Bad Request.

Parametri di query

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.
fieldstringField to aggregate.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of buckets.
rows.keystringBucket key.
rows.doc_countintBucket count.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/listening/organic/aggregation?field=type&filters=publishedAt:-30%20days..now&size=20' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/listening/organic/suggest/:fieldBearer richiesto

10.7.Suggest organic filter values

Return autocomplete suggestions for an Organic Listening field.

Parametri di query

CampoTipoDescrizione
qstringSuggestion query.
sizeintCount of suggestions to return.Predefinito: 10
filtersstringOptional filters to constrain suggestions.

Campi restituiti

CampoTipoDescrizione
rowsobject[]Suggested field values.
rows.keystringSuggested value.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/listening/organic/suggest/title?q=election&size=10' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

11.Leaderboard

3 Endpoint

Rank social accounts by an aggregated post counter over any post filter set. Rows combine ranking values with account profile data.

GET/posts/leaderboardBearer richiesto

11.1.Get the leaderboard

Return social accounts ranked by an aggregated post counter, with per post type breakdowns and account profile data.

  • aggregationField and additionalAggregationFields accept the documented post counter names, such as engagement, reach, videoViews, click, ctr, overperforming, or monetization. The API resolves each documented name to the stored counter internally; historical internal counter names are intentionally undocumented.
  • Counters built from public network data, such as engagement or videoViews, rank any listened account. Counters that require account ownership automatically restrict the leaderboard to social accounts the API token can access.
  • Leaderboard filters and sorts accept the shorthand fields name, slug, category, and verificationStatus, resolved to the matching socialAccount fields internally.

Parametri di query

CampoTipoDescrizione
aggregationField*stringDocumented post counter used to rank accounts, for example engagement, reach, videoViews, click, ctr, overperforming, or monetization.
additionalAggregationFieldsstringComma-separated additional documented counters to aggregate per account. Each counter is returned on the row under the requested counter name.
sizeintCount of accounts to return, between 1 and 200.Predefinito: 20
pageintZero-based page number.Predefinito: 0
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query, for example title,message,description,category.
sortstringRanking direction. Use counters.total.<counter> with the documented counter name, for example sort=-counters.total.engagement. Defaults to descending on aggregationField.
filtersstringAND filters on post fields, such as publishedAt ranges or socialAccount.type. The shorthand fields name, slug, category, and verificationStatus filter the matching socialAccount fields.
filtersOrstringOR filters on post fields.
excludesstringExclude results when a field contains a value.
existsstringComma-separated fields that must exist in matching posts.

Campi restituiti

CampoTipoDescrizione
LeaderboardRow[]LeaderboardRow[]Accounts ranked by the aggregated counter. The response is a JSON array without a total wrapper.
->externalIdstringSocial network account/page ID.
->namestringAccount display name.
->typestringSocial network: facebook, instagram, linkedin, etc.
->slugstringAccount slug.
->picturestringAccount profile picture URL.
->websitestringAccount website.
->docCountintNumber of posts matching the filters for the account.
->valuefloatAggregated aggregationField value across matching posts.
->percentfloatAggregated value relative to the account fan count.
->typesobjectPer post type breakdown keyed by post type, each entry with type, docCount, value, and percent.
->fanCountintAccount fan count.
->followerCountintEstimated follower count.
->verificationStatusstringNetwork verification status.
->createdAtdateAccount creation date.
->countersCounterTotal[]Account counter totals.
-><counter>objectOne entry per additionalAggregationFields counter, keyed by the requested counter name, with value, docCount, and types.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/leaderboard?aggregationField=engagement&additionalAggregationFields=videoViews&filters=publishedAt:-30%20days..now;socialAccount.type:facebook&size=10' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

Esempio di risposta

json
1[
2 {
3 "externalId": "339028534278",
4 "name": "Media page",
5 "type": "facebook",
6 "slug": "@mediapage",
7 "picture": "https://...",
8 "docCount": 1240,
9 "value": 458200,
10 "percent": 12.4,
11 "fanCount": 3690000,
12 "followerCount": 3720000,
13 "verificationStatus": "blue_verified",
14 "types": {
15 "link": { "type": "link", "docCount": 980, "value": 310500, "percent": 8.4 },
16 "video": { "type": "video", "docCount": 260, "value": 147700, "percent": 4.0 }
17 },
18 "videoViews": {
19 "externalId": "339028534278",
20 "docCount": 260,
21 "value": 985000,
22 "types": {
23 "video": { "type": "video", "docCount": 260, "value": 985000 }
24 }
25 }
26 }
27]
GET/posts/leaderboard/categoriesBearer richiesto

11.3.Get leaderboard categories

Return the per post type counter breakdown for selected leaderboard accounts.

  • aggregationField and additionalAggregationFields accept the documented post counter names, such as engagement, reach, videoViews, click, ctr, overperforming, or monetization. The API resolves each documented name to the stored counter internally; historical internal counter names are intentionally undocumented.

Parametri di query

CampoTipoDescrizione
accountIds*stringComma-separated external account IDs, from the externalId of leaderboard rows.
aggregationField*stringDocumented post counter to aggregate per post type.
sizeintCount of accounts to return, between 1 and 200.Predefinito: 20
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on post fields.
filtersOrstringOR filters on post fields.
excludesstringExclude results when a field contains a value.
existsstringComma-separated fields that must exist in matching posts.

Campi restituiti

CampoTipoDescrizione
LeaderboardCategoryRow[]LeaderboardCategoryRow[]Per account post type breakdown. The response is a JSON array without a total wrapper.
->externalIdstringSocial network account/page ID.
->typesobjectBreakdown keyed by post type, each entry with value and docCount for the aggregated counter.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/posts/leaderboard/categories?accountIds=339028534278,224444370873&aggregationField=engagement&filters=publishedAt:-30%20days..now' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

12.Accounts

5 Endpoint

Search and manage the social accounts tracked by Social Listening, such as media pages and competitors. These are listening entities, distinct from the connected social accounts used for publishing.

POST/accountsBearer richiesto

12.2.Add an account

Add a social account to Social Listening. The account is fetched from the network, activated for listening, and returned. Requires a role with the listening manage permission.

Parametri del body

CampoTipoDescrizione
network*stringSocial network of the account: facebook, instagram, linkedin, etc.
accountIdstringExternal account ID or slug on the network. Provide accountId or slug.
slugstringAccount slug on the network. Provide accountId or slug.
instanceUrlstringInstance URL, required for federated networks such as Mastodon.

Campi restituiti

CampoTipoDescrizione
AccountAccountCreated or updated account, with the same fields as search results.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/accounts' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "network": "facebook",
6 "slug": "mediapage"
7 }'
GET/accounts/categoriesBearer richiesto

12.3.Get account categories

Return account category aggregations for autocomplete and listening filters.

  • The category filter and query field resolve to the localized account category internally; filter with category:<value> rather than a localized field path.
Rate limit200 calls every 10 minutes per company and per endpoint, shared by all API tokens of the company. Each window opens with the first counted call. Creating or rotating a token neither resets nor extends the window; the 201st call in the window returns 429, and every later call returns 429 until the window closes. Each request counts as one call, whatever the number of items in a bulk request

Parametri di query

CampoTipoDescrizione
sizeintCount of results to return.Predefinito: 10
prefixstringCategory prefix.
selected*stringAlready selected categories, comma separated. Send an empty value when no category is selected yet.
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on account fields.
filtersOrstringOR filters on account fields.
excludesstringExclude results when a field contains a value.
existsstringComma-separated fields that must exist in each returned item.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of categories found.
rows.keystringCategory value.
rows.doc_countintNumber of accounts in the category.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/accounts/categories?prefix=News&selected=&filters=type:facebook&size=10' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/accounts/locationsBearer richiesto

12.4.Search account locations

Return location accounts, such as city pages, matching a query. Local index results are combined with a remote network search. A type filter selecting the social network is required.

Parametri di query

CampoTipoDescrizione
sizeintCount of results to return.Predefinito: 20
qstringLocation search query.
filters*stringAND filters. The type:<network> filter selecting the social network to search is required.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of locations found.
rowsAccount[]Location accounts, including geoname data.
qstringEcho of the search query.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/accounts/locations?q=Paris&filters=type:facebook&size=20' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/accounts/statsBearer richiesto

12.5.Get account stats

Return aggregated account counter statistics with optional trend and histogram data.

  • Accounts endpoints expose account counters only, currently follower_count (account fans or followers). The API resolves this documented name to the stored account counter internally, and responses echo the name the request used. Address it with the full counter path, for example sort=-counters.total.follower_count or filters=counters.graph.counter:follower_count. Post counters such as engagement or reach are invalid for Accounts.

Parametri di query

CampoTipoDescrizione
filtersstringAND filters on account fields.
filtersOrstringOR filters on account fields.
excludesstringExclude results when a field contains a value.
existsstringComma-separated fields that must exist in each item.
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
metricstringAggregation metric: sum, avg, med, or percentiles.
dateTimeRangestringDate-time range used to calculate the trend.
timezonestringTimezone used to resolve previous periods for the trend.
percentilefloatQueried percentile when metric is percentiles.
granularitystringHistogram granularity: hour, day, week, month, or year. Auto-selected from the filtered period when omitted.

Campi restituiti

CampoTipoDescrizione
totalobject[]Aggregated counter totals.
total.fieldstringAccount counter field.
total.valuefloatAggregated value.
total.trendfloatTrend versus the previous period when dateTimeRange is provided.
graphobject[]Counter histograms.
graph.counterstringAccount counter field.
graph.histogramobject[]Time-bucketed counter values.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/accounts/stats?filters=type:facebook;listeningActivated:true;counters.graph.counter:follower_count&metric=sum&granularity=day' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

13.Bots

6 Endpoint

Manage automation bots that transform sources such as feeds, themes, or social accounts into publishable posts.

GET/bots/:botIdBearer richiesto

13.2.Get a bot

Return one bot by UUID.

Parametri di query

CampoTipoDescrizione
fieldsstringComma-separated fields to include.
embedstringCan embed running and spamControl.

Campi restituiti

CampoTipoDescrizione
idstringBot UUID.
titlestringBot title.
brandIdintBrand attached to the bot.
activationobjectActivation and scheduling state.
sourceobjectSource configuration.
targetobject[]Target configurations.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/bots/5c9b6e52-8d4e-4f16-9f16-c01e9cbbd9e1?fields=id,title,brandId,activation,source,target&embed=running' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
POST/bots/bulkBearer richiesto

13.3.Create bots in bulk

Create one or more bots. Source and target URNs must reference resources visible to the API token role.

  • Bot trigger counters and post-rate metrics use the documented post counters. Percentile-based triggers resolve the documented counter name before calculating the percentile.
  • The creator identity is server-managed. Bots created with a Bearer token use API <token name> as creatorName and have no creatorUserId. Client-supplied creator identity fields are ignored, so an API integration cannot impersonate a Nonli user.

Parametri del body

CampoTipoDescrizione
bot*Bot[]Bots to create.
->title*stringBot title.
->brandId*intBrand attached to the bot.
->autoboolIf false, created posts are kept for review before publishing.
->activationobjectActivation state and optional time window.
->timezonestringTimezone used for activation windows.
->runCyclePeriodobjectMinimum and maximum minutes between source reads.
->scheduleobjectPost scheduling options.
->source*objectSource object with a Nonli URN.
->target*object[]Target objects with Nonli URNs.
->postobject[]Post template configuration.
->fieldMappingobject[]Source-to-target field mappings.
->sourceMessagestringSource field used to build the post message.
->sourceTitlestringSource field used to build the post title.
->sourceImagestringSource field used to build the post image.
->trigger.counter[].fieldstringCounter field that must reach trigger.counter[].triggerValue before the bot posts. Accepts documented post counter names such as engagement or reach for social-account sources.
->trigger.counter[].triggerValueintCounter threshold used by the trigger.
->trigger.counter[].metricstringOptional aggregation metric used to calculate the trigger value, for example percentiles. The trigger counter field is resolved from the documented counter name before the aggregation is built.
->postRate.modestringPost type selection mode. Use manual for fixed post-type ratios, auto_all to auto-select from all post types, or auto_photo_link to auto-select between photo and link posts.
->postRate.metricstringPerformance metric used by automatic post type selection. Accepts documented post counter names such as engagement, reach, videoViews, and monetization.

Campi restituiti

CampoTipoDescrizione
Bot[]Bot[]Created bots or per-item errors.
->idstringCreated bot UUID.
->creatorNamestringServer-managed creator. Bearer-created bots use API followed by the token name.
->creatorUserIdint|nullNull for bots created with an API token.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/bots/bulk' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "bot": [
6 {
7 "title": "Morning feed review",
8 "brandId": 1,
9 "auto": false,
10 "activation": {
11 "alive": false
12 },
13 "timezone": "Europe/Paris",
14 "runCyclePeriod": {
15 "min": 30,
16 "max": 60
17 },
18 "source": {
19 "urn": "urn:nonli:feed:https://example.com/rss.xml",
20 "name": "Editorial feed"
21 },
22 "target": [
23 {
24 "urn": "urn:nonli:sa:facebook:PAGE_ID",
25 "name": "Facebook page"
26 }
27 ],
28 "post": [
29 {
30 "index": 0,
31 "prefix": "",
32 "suffix": ""
33 }
34 ],
35 "trigger": {
36 "counter": [
37 {
38 "field": "engagement",
39 "triggerValue": 75,
40 "metric": "percentiles"
41 }
42 ]
43 },
44 "postRate": {
45 "mode": "auto_all",
46 "metric": "engagement"
47 },
48 "fieldMapping": [
49 {
50 "source": "title",
51 "target": "message"
52 }
53 ],
54 "sourceMessage": "title",
55 "sourceTitle": "title",
56 "sourceImage": "og_image"
57 }
58 ]
59 }'

Esempio di risposta

json
1[
2 {
3 "id": "5c9b6e52-8d4e-4f16-9f16-c01e9cbbd9e1",
4 "title": "Morning feed review",
5 "brandId": 1,
6 "creatorName": "API Website publisher",
7 "creatorUserId": null,
8 "activation": {
9 "alive": false
10 }
11 },
12 {
13 "error": "Bot creation failed"
14 }
15]
PUT/bots/bulkBearer richiesto

13.4.Update bots in bulk

Update one or more bots. Each item must include the bot UUID and only the fields to change.

  • Bot trigger counters and post-rate metrics use the documented post counters. Percentile-based triggers resolve the documented counter name before calculating the percentile.

Parametri del body

CampoTipoDescrizione
bot*Bot[]Bots to update.
->id*stringBot UUID.
->titlestringBot title.
->brandIdintBrand attached to the bot.
->autoboolWhether the bot publishes automatically.
->activationobjectActivation state and optional time window.
->runCyclePeriodobjectMinimum and maximum minutes between source reads.
->scheduleobjectPost scheduling options.
->sourceobjectSource object with a Nonli URN.
->targetobject[]Target objects with Nonli URNs.
->postobject[]Post template configuration.
->fieldMappingobject[]Source-to-target field mappings.
->trigger.counter[].fieldstringCounter field that must reach trigger.counter[].triggerValue before the bot posts. Accepts documented post counter names such as engagement or reach for social-account sources.
->trigger.counter[].triggerValueintCounter threshold used by the trigger.
->trigger.counter[].metricstringOptional aggregation metric used to calculate the trigger value, for example percentiles. The trigger counter field is resolved from the documented counter name before the aggregation is built.
->postRate.modestringPost type selection mode. Use manual for fixed post-type ratios, auto_all to auto-select from all post types, or auto_photo_link to auto-select between photo and link posts.
->postRate.metricstringPerformance metric used by automatic post type selection. Accepts documented post counter names such as engagement, reach, videoViews, and monetization.

Esempio

bash
1curl --location --request PUT 'https://api.nonli.com/v2.1/bots/bulk' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "bot": [
6 {
7 "id": "5c9b6e52-8d4e-4f16-9f16-c01e9cbbd9e1",
8 "title": "Morning feed review",
9 "activation": {
10 "alive": true
11 },
12 "runCyclePeriod": {
13 "min": 45,
14 "max": 90
15 }
16 }
17 ]
18 }'

Esempio di risposta

json
1[
2 {
3 "id": "5c9b6e52-8d4e-4f16-9f16-c01e9cbbd9e1",
4 "title": "Morning feed review",
5 "brandId": 1,
6 "activation": {
7 "alive": true
8 }
9 },
10 {
11 "error": "Bot update failed"
12 }
13]
DELETE/bots/:botIdBearer richiesto

13.5.Delete a bot

Delete one bot by UUID. A successful deletion returns HTTP 204 with no response body.

Esempio

bash
1curl --location --request DELETE 'https://api.nonli.com/v2.1/bots/5c9b6e52-8d4e-4f16-9f16-c01e9cbbd9e1' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'
GET/bots/infoBearer richiesto

13.6.Get bot configuration info

Return schedule periods, target validation rules, and bot configuration constraints used to build valid bot payloads.

Parametri di query

CampoTipoDescrizione
sourceUrnstringOptional source URN to evaluate source-specific configuration.
targetUrnstringOptional target URN to evaluate target-specific configuration.

Campi restituiti

CampoTipoDescrizione
schedulePeriodsobjectAvailable schedule period identifiers and labels.
targetValidationConfigsobjectValidation rules by target type.
configobjectBot defaults and constraints, including AI generated text and consultation frequency.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/bots/info?sourceUrn=urn%3Anonli%3Afeed%3Ahttps%3A%2F%2Fexample.com%2Frss.xml&targetUrn=urn%3Anonli%3Asa%3Afacebook%3APAGE_ID' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

14.Media

2 Endpoint

Search reusable media and metadata used by the publishing composer.

GET/media/tagsBearer richiesto

14.2.Get media tags

Return media tags for autocomplete and asset filtering.

Parametri di query

CampoTipoDescrizione
sizeintMaximum number of results to return.Predefinito: 20 or endpoint-specific
qstringFull-text search query.
qfstringComma-separated fields used by the full-text query.
filtersstringAND filters on item fields. Use semicolons between filters and ".." for ranges. Send the separators ":", ";", "," and ".." as raw characters, which every search endpoint accepts, and percent-encode only characters inside values, such as spaces (%20). Encoded separators behave differently by endpoint: GET /listening/social, GET /posts/leaderboard, and GET /accounts read the raw query string and treat them as literal value characters, so the filter matches nothing or returns 400, while the other GET search endpoints, such as GET /posts, decode the query string first and treat them as separators. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
filtersOrstringOR filters on item fields. Example: "status:200,203". On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
excludesstringExclude results when a field contains a value. On post-backed counter filters, documented counter names are resolved for counters.total.field, counters.graph.counter, counters.histogram.field, and counters.period.field.
existsstringComma-separated fields that must exist in each returned item. Fields must belong to the endpoint entity; invalid fields return 400 Bad Request.

Campi restituiti

CampoTipoDescrizione
totalintTotal number of tags found.
rows.keystringTag value.
rows.doc_countintNumber of media items using the tag.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/media/tags?size=100' \
2 --header 'Authorization: Bearer YOUR_API_TOKEN'

15.Uploads

6 Endpoint

Upload images, videos, and documents before attaching them to posts.

To upload a video:

  1. 1Compute the SHA-256 of the file.
  2. 2Request a token from POST /upload/video/token with sha256. If the response contains video, reuse that video and stop.
  3. 3Otherwise upload the chunks to POST /upload/video/chunk with the returned token and chunkSize, and reuse the URL of the final response. If the last chunk request fails or returns 409, wait a few seconds and re-send it until its response contains the video.
POST/upload/imageBearer richiesto

15.1.Upload an image

Upload a base64 image and receive the Fontan slug used by posts and shortlinks.

Parametri del body

CampoTipoDescrizione
b64Image*stringBase64 encoded image.
keepFormatboolKeep the original format when possible.
faceDetectboolRun face detection/cropping assistance.Predefinito: true

Campi restituiti

CampoTipoDescrizione
slugstringFontan image slug.
urlstringImage URL when available.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/upload/image' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "b64Image": "data:image/png;base64,iVBORw0KGgo...",
6 "keepFormat": true
7 }'
POST/upload/video/tokenBearer richiesto

15.2.Create upload video token

Open a chunked video upload session, or reuse a video the company already stores when you send the SHA-256 of the file. The session response includes chunkSize, which defines the maximum chunk size to send.

  • When sha256 matches content the company already stores, the response is {"video": {...}} with that video and no upload is needed. Otherwise the session response (token, size, chunkSize, chunkIndex) is returned. The declared hash is only a lookup key: an upload is always stored under the SHA-256 the API computes from the received bytes. An invalid sha256 returns 400 Bad Request.
  • Each request opens a new upload session; a Bearer API token never resumes a previous one.
  • The API tokens of a company can hold at most 20 open video upload sessions; the next token request returns 429 Too Many Requests. A session that receives no chunk for 30 minutes stops counting.

Parametri del body

CampoTipoDescrizione
name*stringFile name.
type*stringVideo MIME type.
size*intFile size in bytes.
sha256stringLower or upper case hexadecimal SHA-256 of the complete file. Reuses the video the company already stores for that content instead of uploading it again.
durationintVideo duration in seconds.
widthintVideo width in pixels.
heightintVideo height in pixels.
titlestringVideo title.
descriptionstringVideo description.
customTagstring[]Video custom tags.

Campi restituiti

CampoTipoDescrizione
videoobjectVideo the company already stores for the sent sha256. When present, no upload session is opened and no other field is returned.
tokenstringUpload token.
sizeintFile size in bytes.
chunkSizeintChunk size in bytes.
chunkIndexintNext chunk index.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/upload/video/token' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "sha256": "3a5f1c0e9b7d4f2a6c8e1b3d5f7a9c0e2b4d6f8a1c3e5b7d9f0a2c4e6b8d1f3a",
6 "name": "35427-407130886_small.mp4",
7 "size": 1338788,
8 "type": "video/mp4",
9 "title": "My video title",
10 "description": "My video description",
11 "customTag": ["foo", "bar"]
12 }'

Esempio di risposta

json
1{
2 "video": {
3 "id": "8f14e45fceea167a5a36dedd4bea2543",
4 "url": "https://cdn.example.com/videos/35427-407130886_small.mp4",
5 "title": "My video title",
6 "type": "video/mp4",
7 "size": 1338788
8 }
9}
POST/upload/video/existsBearer richiestoObsoleto

15.3.Check uploaded video existence (legacy)

Legacy compatibility lookup of a video uploaded before the server-owned upload flow with the same client md5sum. New integrations must not use it.

  • This lookup never finds a video uploaded since the server-owned upload flow, which stores every upload under the SHA-256 the API computes. To avoid uploading a video again, send its sha256 to POST /upload/video/token instead.

Parametri del body

CampoTipoDescrizione
md5sum*stringLegacy client video ID, md5 encoded.
namestringIgnored. The video is found whatever its file name.

Campi restituiti

CampoTipoDescrizione
urlstring|nullThe response is {"url": null} when no video is found. Otherwise it is the found video object, whose url is the video URL and id its ID.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/upload/video/exists' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "md5sum": "64bed820ce612754d1e3e957edfc69c7"
6 }'

Esempio di risposta

json
1{
2 "url": null
3}
POST/upload/video/chunkBearer richiesto

15.4.Upload video chunk

Upload one base64 chunk for a video upload session. The response is {size} until the upload is complete, then the final video.

  • The final video is the company's canonical video for that content: the same bytes always return the same URL and are stored and counted once, whatever the file name, extension, or API token. Reuse this URL for every publication destination.
  • Large files can take longer to finalize. If the last chunk request fails with a network error, a timeout, 502, 504, or 409 Conflict (the upload is still being finalized), wait a few seconds and re-send the same last chunk, with the same token and chunk number, until the response contains the video. Re-sending it returns the same video and never stores the content twice. 404 Not Found means the upload session expired: restart the upload with a new token.

Parametri del body

CampoTipoDescrizione
token*stringUpload token.
chunk*intChunk number.
data*stringChunk bytes as base64.

Campi restituiti

CampoTipoDescrizione
sizeintUploaded size after this chunk, returned until the upload is complete.
idstringVideo ID, returned when the last chunk has been uploaded.
urlstringCanonical video URL, returned when the last chunk has been uploaded.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/upload/video/chunk' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "token": "e5bab8115967331ce0db284e03622cf0",
6 "chunk": 1,
7 "data": "AAAAIGZ0eXBtcDQyAAAAAG1wNDJtcDQxaXNvbWF2YzEAABF9b..."
8 }'
POST/upload/document/tokenBearer richiesto

15.5.Create upload document token

Create or resume a document upload session. The response includes chunkSize for chunked upload.

Parametri del body

CampoTipoDescrizione
name*stringFile name.
type*stringDocument MIME type, commonly application/pdf.
size*intFile size in bytes.
md5sum*stringUnique document ID, md5 encoded.
titlestringDocument title.
descriptionstringDocument description.
pageCountintNumber of pages in the document.

Campi restituiti

CampoTipoDescrizione
tokenstringUpload token.
sizeintFile size in bytes.
chunkSizeintChunk size in bytes.
chunkIndexintNext chunk index.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/upload/document/token' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "md5sum": "64bed820ce612754d1e3e957edfc69c7",
6 "name": "press-release.pdf",
7 "size": 1024000,
8 "type": "application/pdf",
9 "title": "Press release",
10 "pageCount": 3
11 }'
POST/upload/document/chunkBearer richiesto

15.6.Upload document chunk

Upload one base64 chunk for a document upload session. The final response includes the document id and URL.

Parametri del body

CampoTipoDescrizione
token*stringUpload token.
chunk*intChunk number.
data*stringChunk bytes as base64.

Campi restituiti

CampoTipoDescrizione
sizeintUploaded size after this chunk.
idstringDocument ID, returned when the last chunk has been uploaded.
urlstringDocument URL, returned when the last chunk has been uploaded.

Esempio

bash
1curl --location 'https://api.nonli.com/v2.1/upload/document/chunk' \
2 --header 'Content-Type: application/json' \
3 --header 'Authorization: Bearer YOUR_API_TOKEN' \
4 --data '{
5 "token": "e5bab8115967331ce0db284e03622cf0",
6 "chunk": 1,
7 "data": "JVBERi0xLjQKJc..."
8 }'