API développeurs v2.1

Intégrez Nonli avec une référence API claire et prête pour les IA.

Utilisez les tokens API, recherchez les posts et les données de listening, créez des shortlinks, publiez des posts et uploadez des vidéos depuis une documentation unique.

URL de base

https://api.nonli.com/v2.1
71endpoints
11ressources

Démarrage rapide

Trois réflexes suffisent pour authentifier vos appels, limiter les payloads et interroger les données de production proprement.

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

Créez un token API

Les administrateurs de société créent et renouvellent les tokens API depuis le back office Nonli.

2

Envoyez le header Bearer

Envoyez le token sur chaque appel authentifié avec Authorization: Bearer YOUR_API_TOKEN. Pour valider un token, appelez l’endpoint brands affiché ici.

3

Demandez les champs utiles

Utilisez fields, filters, filtersOr, excludes, size, page et sort pour garder des réponses compactes, pour les apps comme pour les agents IA.

Notes pour intégration IA

OpenAPI JSON: /api-docs/openapi.json

llms.txt: /llms.txt

  • Préférez /api-docs/openapi.json pour générer des outils et extraire les schémas.
  • Utilisez /llms.txt comme point d’entrée compact et crawlable.
  • Les tokens API sont des identifiants de rôle société, pas des sessions utilisateur web : n’utilisez pas /me avec des tokens Bearer.
  • Ajoutez fields dès que possible pour réduire l’usage de tokens.
  • Respectez les rate limits et parcourez les gros jeux de données avec size/page.

Explorateur d’endpoints

71 endpoints

GETSearch brands/brandsOuvrirGETGet a brand/brands/:brandIdOuvrirGETSearch social accounts/social-accountsOuvrirGETGet a social account/social-accounts/:socialAccountIdOuvrirGETGet publishing quota/social-accounts/publishing-quotaOuvrirGETGet YouTube playlists/social-accounts/youtube/playlistsOuvrirPOSTCreate a YouTube playlist/social-accounts/youtube/playlistsOuvrirPUTUpdate a YouTube playlist/social-accounts/youtube/playlists/:playlistIdOuvrirDELETEDelete a YouTube playlist/social-accounts/youtube/playlists/:playlistIdOuvrirGETList the videos of a YouTube playlist/social-accounts/youtube/playlists/:playlistId/itemsOuvrirPUTMove a video in a YouTube playlist/social-accounts/youtube/playlists/:playlistId/items/:itemIdOuvrirGETGet Pinterest boards/social-accounts/pinterest/boardsOuvrirGETSearch posts/postsOuvrirGETGet a post/posts/:postIdOuvrirGETGet post categories/posts/categoriesOuvrirGETGet post brands/posts/brandsOuvrirGETGet post users/posts/usersOuvrirGETGet post languages/posts/languagesOuvrirGETGet post histogram/posts/histogramOuvrirGETGet post aggregation/posts/aggregationOuvrirGETGet post stats/posts/statsOuvrirPOSTCheck post content/posts/checkOuvrirGETGet post tags/posts/tagsOuvrirGETGet post topics/posts/topicsOuvrirPOSTCreate posts in bulk/posts/bulkOuvrirPUTUpdate posts in bulk/posts/bulkOuvrirDELETEDelete posts in bulk/posts/bulkOuvrirGETSearch shortlinks/shortlinksOuvrirGETGet a shortlink/shortlinks/:shortlinkIdOuvrirGETGet shortlink stats/shortlinks/statsOuvrirGETGet shortlink authors/shortlinks/authorsOuvrirGETGet shortlink brands/shortlinks/brandsOuvrirPOSTCreate a shortlink/shortlinksOuvrirPOSTCreate shortlinks in bulk/shortlinks/bulkOuvrirPUTUpdate a shortlink/shortlinks/:shortlinkIdOuvrirPOSTGenerate a message for a shortlink/shortlinks/:shortlinkId/generate-messageOuvrirGETSearch themes/themesOuvrirGETGet a theme/themes/:themeIdOuvrirPOSTCreate a theme/themesOuvrirPUTUpdate a theme/themes/:themeIdOuvrirDELETEDelete a theme/themes/:themeIdOuvrirGETGet theme roles/themes/rolesOuvrirGETGet social listening/listening/socialOuvrirGETGet organic listening/listening/organicOuvrirPUTUpsert an organic with videos/listening/organicOuvrirGETGet organic content/listening/organic/:organicIdOuvrirGETGet organic stats/listening/organic/statsOuvrirGETGet organic aggregation/listening/organic/aggregationOuvrirGETSuggest organic filter values/listening/organic/suggest/:fieldOuvrirGETGet the leaderboard/posts/leaderboardOuvrirPOSTSearch the leaderboard/posts/leaderboard/_searchOuvrirGETGet leaderboard categories/posts/leaderboard/categoriesOuvrirGETSearch accounts/accountsOuvrirPOSTAdd an account/accountsOuvrirGETGet account categories/accounts/categoriesOuvrirGETSearch account locations/accounts/locationsOuvrirGETGet account stats/accounts/statsOuvrirGETSearch bots/botsOuvrirGETGet a bot/bots/:botIdOuvrirPOSTCreate bots in bulk/bots/bulkOuvrirPUTUpdate bots in bulk/bots/bulkOuvrirDELETEDelete a bot/bots/:botIdOuvrirGETGet bot configuration info/bots/infoOuvrirGETSearch media/mediaOuvrirGETGet media tags/media/tagsOuvrirPOSTUpload an image/upload/imageOuvrirPOSTCreate upload video token/upload/video/tokenOuvrirPOSTCheck uploaded video existence (legacy)/upload/video/existsOuvrirPOSTUpload video chunk/upload/video/chunkOuvrirPOSTCreate upload document token/upload/document/tokenOuvrirPOSTUpload document chunk/upload/document/chunkOuvrir

Parcours d’intégration courants

5 endpoints
Sur cette page

1.Conventions

Règles communes à tous les endpoints.

Authentification

Envoyez un token API dans le header Authorization à chaque appel authentifié : Authorization: Bearer YOUR_API_TOKEN. Les tokens API représentent un rôle société, pas une session utilisateur web, donc /me n’est pas disponible avec les tokens Bearer. Conservez-les de façon sécurisée, renouvelez-les depuis le back office et ne les mettez jamais dans les query strings.

Pagination

Les endpoints de liste renvoient total et un tableau rows. Parcourez les résultats avec size et un page indexé à zéro jusqu’à récupérer total éléments. Utilisez fields pour garder chaque élément compact. Sur GET /posts, size accepte de 0 à 100 et (page + 1) × size ne peut pas dépasser 10 000 : au-delà, l’API répond 400 Bad Request. Resserrez alors les filtres, par exemple avec une plage publishedAt, pour atteindre les posts plus anciens.

Dates et fuseaux horaires

Les dates sont en UTC au format ISO-8601, par exemple 2026-05-27T23:59:59Z. Les paramètres de date et les dates envoyées dans le corps des requêtes, comme createdAt, publishedAt ou schedule.startDate, acceptent le format YYYY-MM-DDTHH:MM:SSZ avec ou sans fractions de seconde, par exemple 2026-05-27T23:59:59.123Z : une date lue dans une réponse peut être renvoyée telle quelle. La fraction est ignorée, et un décalage horaire comme +02:00 renvoie 400 Bad Request. Les filtres de plage utilisent ".." entre deux valeurs, par exemple publishedAt:-30 days..now ; sur les endpoints de recherche de posts et de listening, la borne haute est exclue.

Rate limits

Les limites sont comptées par société et par endpoint, partagées par tous les tokens API de la société, sur des fenêtres fixes qui s’ouvrent au premier appel. La référence des endpoints indique chaque limite ; un endpoint qui n’en affiche pas n’a pas de limite d’appels par endpoint. Chaque réponse d’un endpoint limité porte les headers RateLimit-Limit, RateLimit-Remaining et RateLimit-Reset ; RateLimit-Reset donne le nombre de secondes avant la fin de la fenêtre. Une fois la limite atteinte, chaque appel renvoie 429 jusqu’à la fin de la fenêtre, avec un header Retry-After du même nombre de secondes et un corps JSON. GET /listening/social appelé avec un token API Bearer est décompté du pack Social Listening de la société, sans fenêtre par endpoint ni headers RateLimit ; une session d’utilisateur API historique est limitée à 720 appels toutes les 12 heures par utilisateur et reçoit les headers RateLimit. Une session d’utilisateur API historique est un utilisateur connecté avec le rôle API, pas un token Bearer.

Statuts des posts

Sans valeur de statut dans filters (filtersOr ne compte pas), GET /posts, /posts/aggregation et /posts/histogram renvoient ou comptent tous les statuts sauf les posts supprimés (404) et obsolètes (410), y compris les brouillons, les posts programmés, ceux en attente de validation et les publications en erreur. GET /posts/stats n’applique aucun filtre de statut par défaut. Ajoutez filtersOr=status:200,203,204, le groupe « Publié » du back office, pour obtenir des chiffres cohérents avec les contenus publiés. La référence de GET /posts liste toutes les valeurs de statut.

Identifiants

Les posts, shortlinks et contenus organic utilisent des UUID (string). Les brands, comptes sociaux et thèmes utilisent des ID entiers. Transmettez les identifiants tels que l’API les renvoie.

Noms de compteurs

Pour les endpoints basés sur des posts, utilisez les noms de compteurs documentés dans sort, filters, filtersOr, excludes, les triggers de bots et les métriques de post-rate. Les endpoints Organic Listening et Shortlink exposent leurs propres compteurs et rejettent les compteurs de posts avec une erreur 400 Bad Request.

2.Paramètres de recherche

La plupart des endpoints de liste partagent la même grammaire de requête.

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 20 or endpoint-specific
pageintZero-based page number.Défaut: 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.Compteurs disponibles

Utilisez cette liste comme surface publique des compteurs par famille d’endpoints. Les anciens noms de compteurs remplacés sont volontairement absents.

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

Compteurs

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

Note: 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

Compteurs

clicknew_leadquantityamountpaywall_viewpaywall_unlockedpaywall_clickpaywall_subscribe_clickpaywall_login_clicksubscription_completecomment_countvideo_viewsview_in_stockview_out_of_stockpriceprice_variationlow_pricehigh_priceratingreview_countstock

Note: 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

Compteurs

click

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

Accounts

/accounts/accounts/stats

Compteurs

follower_count

Note: 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.Erreurs

Basez votre gestion sur le code HTTP plutôt que sur le texte du message. Les codes gardent leur sens HTTP standard.

StatutSignification
200OKLa requête a réussi.
400Requête invalideLe body ou les paramètres sont mal formés ou échouent à la validation.
401Non authentifiéLe token Bearer du header Authorization est absent ou invalide. Vérifiez la valeur du token ou renouvelez-le depuis le back office avant de rejouer l’appel.
403InterditLe token API est valide mais n’a pas les droits sur cette ressource.
404IntrouvableLa ressource n’existe pas ou n’est pas visible par votre société.
409ConflitUne requête PUT /posts/bulk sur un seul post envoie un schedule, un publishedAt, un shortId ou un status qui ne peut pas être appliqué, ou un post de la requête change un publishedAt, un postType ou un shortlink qui ne peut plus changer. Le message donne la raison et rien n’est modifié.
423VerrouilléUn post d’une requête PUT /posts/bulk a été modifié il y a moins de 3 secondes, ou est en cours de modification ou de publication. Si le header Retry-After est présent, réessayez après ce nombre de secondes ; sinon réessayez quelques secondes plus tard.
429Trop de requêtesUne limite de débit a été atteinte. Si le header Retry-After est présent, attendez ce nombre de secondes avant de réessayer.
500Erreur serveurErreur serveur inattendue. Réessayez les lectures idempotentes avec un backoff.

Les réponses d’erreur renvoient un body JSON décrivant le problème, mais c’est le code HTTP qui fait foi : construisez votre gestion d’erreurs autour du code, pas du texte du message.

Bonnes pratiques de gestion

  • Traitez tout statut non-2xx comme un échec et lisez le body JSON pour le contexte.
  • Sur 401, vérifiez la valeur du token API, renouvelez-le si nécessaire, puis rejouez la requête.
  • Sur 409, lisez la raison dans le message, corrigez la requête et renvoyez-la aussitôt : une requête qui n’a modifié aucun post ne pose aucun verrou.
  • Sur 423, attendez le nombre de secondes du header Retry-After, 3 au plus après une mise à jour terminée, puis renvoyez la même requête. Un 423 sans Retry-After signifie que le post est en cours de publication : réessayez quelques secondes plus tard.
  • Sur un 429 du rate limiter, attendez le nombre de secondes du header Retry-After, aussi envoyé dans RateLimit-Reset, avant de réessayer. Un 429 dû au quota YouTube sur les endpoints de playlists n’a pas de Retry-After : réessayez plus tard.
  • Sur 5xx, réessayez les lectures idempotentes avec un backoff exponentiel.

Référence

5.Brands

2 endpoints

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

GET/brands/:brandIdBearer requis

5.2.Get a brand

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

Paramètres query

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

Champs retournés

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

Exemple

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 endpoints

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

GET/social-accounts/:socialAccountIdBearer requis

6.2.Get a social account

Return one connected social account with publishing metadata.

Paramètres query

ChampTypeDescription
fieldsstringComma-separated fields to include.

Champs retournés

ChampTypeDescription
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.

Exemple

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 requis

6.3.Get publishing quota

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

Paramètres query

ChampTypeDescription
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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
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.Défaut: false

Champs retournés

ChampTypeDescription
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.

Exemple

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'

Exemple de réponse

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 requis

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

Paramètres body

ChampTypeDescription
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.Défaut: private

Champs retournés

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

Exemple

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 }'

Exemple de réponse

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 requis

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

Paramètres body

ChampTypeDescription
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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 requis

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

Paramètres query

ChampTypeDescription
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.

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
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.Défaut: 50
pageTokenstringToken of the page to read, taken from nextPageToken.

Champs retournés

ChampTypeDescription
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.

Exemple

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'

Exemple de réponse

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 requis

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

Paramètres body

ChampTypeDescription
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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
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.Défaut: false

Champs retournés

ChampTypeDescription
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`.

Exemple

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 endpoints

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.

Publications de vos comptes connectés ou publications du Social Listening ? Lire le guide du Social Listening

GET/posts/:postIdBearer requis

7.2.Get a post

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

Paramètres query

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

Champs retournés

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

Exemple

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 requis

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

Paramètres query

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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

Paramètres query

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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

Paramètres query

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
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.Défaut: publishedAt
baseIntervalstringBucket interval: minute, hour, day, week, month, or year.Défaut: year
searchForMinPointsCountboolWhen true, the API retries with the next smaller interval while the histogram has fewer than minPointsCount buckets. Set false to keep baseInterval.Défaut: true
minPointsCountintMinimum number of buckets sought when searchForMinPointsCount is true.Défaut: 10
keyAsDateboolReturn bucket keys as dates. Set false to receive millisecond timestamps.Défaut: true
timezonestringTimezone of the buckets, as an ISO 8601 UTC offset (+01:00) or a TZ database name (Europe/Paris).

Champs retournés

ChampTypeDescription
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.

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
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.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 20 or endpoint-specific
pageintZero-based page number.Défaut: 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.

Champs retournés

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

Exemple

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 requis

7.10.Check post content

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

Paramètres body

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

Champs retournés

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

Exemple

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 requis

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

Paramètres query

ChampTypeDescription
sizeintCount of results to return.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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

Paramètres query

ChampTypeDescription
sizeintCount of results to return.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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

Paramètres body

ChampTypeDescription
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.

Champs retournés

ChampTypeDescription
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.

Exemple

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'

Exemple de réponse

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 requis

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

Paramètres body

ChampTypeDescription
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.Défaut: 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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 requis

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

Paramètres body

ChampTypeDescription
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.Défaut: false

Champs retournés

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

Exemple

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 endpoints

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

GET/themes/:themeIdBearer requis

9.2.Get a theme

Return one saved theme by ID.

Paramètres query

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

Champs retournés

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

Exemple

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 requis

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

Paramètres body

ChampTypeDescription
name*stringTheme name.
typestringTheme type: listening or post.Défaut: listening
scopestringVisibility scope allowed by the API token role: team, bot, or public. Private is reserved for user sessions.Défaut: team
langstringTheme language: en or fr.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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

Paramètres body

ChampTypeDescription
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.

Champs retournés

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

Exemple

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 requis

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

Champs retournés

ChampTypeDescription
deletedboolWhether the theme was deleted.

Exemple

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

9.6.Get theme roles

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

Champs retournés

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

Exemple

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

10.Listening

7 endpoints

Search social-network performance and organic website content signals.

Comment le Social Listening collecte les publications et comment les exporter : lire le guide

PUT/listening/organicBearer requis

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

Paramètres body

ChampTypeDescription
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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 requis

10.4.Get organic content

Return one indexed organic content item by ID.

Paramètres query

ChampTypeDescription
fieldsstringComma-separated fields to include.

Champs retournés

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

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 20 or endpoint-specific
pageintZero-based page number.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 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.

Champs retournés

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

Exemple

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 requis

10.7.Suggest organic filter values

Return autocomplete suggestions for an Organic Listening field.

Paramètres query

ChampTypeDescription
qstringSuggestion query.
sizeintCount of suggestions to return.Défaut: 10
filtersstringOptional filters to constrain suggestions.

Champs retournés

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

Exemple

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 endpoints

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

GET/posts/leaderboardBearer requis

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.

Paramètres query

ChampTypeDescription
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.Défaut: 20
pageintZero-based page number.Défaut: 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.

Champs retournés

ChampTypeDescription
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.

Exemple

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'

Exemple de réponse

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 requis

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.

Paramètres query

ChampTypeDescription
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.Défaut: 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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 endpoints

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 requis

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.

Paramètres body

ChampTypeDescription
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.

Champs retournés

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

Exemple

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 requis

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

Paramètres query

ChampTypeDescription
sizeintCount of results to return.Défaut: 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.

Champs retournés

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

Exemple

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 requis

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.

Paramètres query

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

Champs retournés

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

Exemple

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 requis

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.

Paramètres query

ChampTypeDescription
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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 endpoints

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

GET/bots/:botIdBearer requis

13.2.Get a bot

Return one bot by UUID.

Paramètres query

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

Champs retournés

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

Exemple

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 requis

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.

Paramètres body

ChampTypeDescription
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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 }'

Exemple de réponse

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 requis

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.

Paramètres body

ChampTypeDescription
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.

Exemple

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 }'

Exemple de réponse

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 requis

13.5.Delete a bot

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

Exemple

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 requis

13.6.Get bot configuration info

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

Paramètres query

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

Champs retournés

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

Exemple

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 endpoints

Search reusable media and metadata used by the publishing composer.

GET/media/tagsBearer requis

14.2.Get media tags

Return media tags for autocomplete and asset filtering.

Paramètres query

ChampTypeDescription
sizeintMaximum number of results to return.Défaut: 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.

Champs retournés

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

Exemple

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

15.Uploads

6 endpoints

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 requis

15.1.Upload an image

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

Paramètres body

ChampTypeDescription
b64Image*stringBase64 encoded image.
keepFormatboolKeep the original format when possible.
faceDetectboolRun face detection/cropping assistance.Défaut: true

Champs retournés

ChampTypeDescription
slugstringFontan image slug.
urlstringImage URL when available.

Exemple

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 requis

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.

Paramètres body

ChampTypeDescription
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.

Champs retournés

ChampTypeDescription
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.

Exemple

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 }'

Exemple de réponse

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 requisObsolète

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.

Paramètres body

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

Champs retournés

ChampTypeDescription
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.

Exemple

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 }'

Exemple de réponse

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

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.

Paramètres body

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

Champs retournés

ChampTypeDescription
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.

Exemple

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 requis

15.5.Create upload document token

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

Paramètres body

ChampTypeDescription
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.

Champs retournés

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

Exemple

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 requis

15.6.Upload document chunk

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

Paramètres body

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

Champs retournés

ChampTypeDescription
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.

Exemple

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 }'