API para desarrolladores v2.1

Integra Nonli con una referencia de API clara y lista para la IA.

Usa tokens de API, busca posts y datos de listening, crea shortlinks, publica posts y sube vídeos desde una única documentación.

URL base

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

Inicio rápido

Tres hábitos bastan para autenticar tus llamadas, limitar los payloads y consultar los datos de producción con limpieza.

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

Crea un token de API

Los administradores de la empresa crean y renuevan los tokens de API desde el back office de Nonli.

2

Envía el header Bearer

Envía el token en cada llamada autenticada con Authorization: Bearer YOUR_API_TOKEN. Para validar un token, llama al endpoint brands que se muestra aquí.

3

Pide solo los campos útiles

Usa fields, filters, filtersOr, excludes, size, page y sort para mantener las respuestas compactas, tanto en apps como en agentes de IA.

Notas para la integración con IA

OpenAPI JSON: /api-docs/openapi.json

llms.txt: /llms.txt

  • Prefiere /api-docs/openapi.json para generar herramientas y extraer los esquemas.
  • Usa /llms.txt como punto de entrada compacto y rastreable.
  • Los tokens de API son credenciales de rol de empresa, no sesiones de usuario web: no uses /me con tokens Bearer.
  • Añade fields siempre que puedas para reducir el uso de tokens.
  • Respeta los rate limits y recorre los conjuntos de datos grandes con size/page.

Explorador de endpoints

71 endpoints

GETSearch brands/brandsAbrirGETGet a brand/brands/:brandIdAbrirGETSearch social accounts/social-accountsAbrirGETGet a social account/social-accounts/:socialAccountIdAbrirGETGet publishing quota/social-accounts/publishing-quotaAbrirGETGet YouTube playlists/social-accounts/youtube/playlistsAbrirPOSTCreate a YouTube playlist/social-accounts/youtube/playlistsAbrirPUTUpdate a YouTube playlist/social-accounts/youtube/playlists/:playlistIdAbrirDELETEDelete a YouTube playlist/social-accounts/youtube/playlists/:playlistIdAbrirGETList the videos of a YouTube playlist/social-accounts/youtube/playlists/:playlistId/itemsAbrirPUTMove a video in a YouTube playlist/social-accounts/youtube/playlists/:playlistId/items/:itemIdAbrirGETGet Pinterest boards/social-accounts/pinterest/boardsAbrirGETSearch posts/postsAbrirGETGet a post/posts/:postIdAbrirGETGet post categories/posts/categoriesAbrirGETGet post brands/posts/brandsAbrirGETGet post users/posts/usersAbrirGETGet post languages/posts/languagesAbrirGETGet post histogram/posts/histogramAbrirGETGet post aggregation/posts/aggregationAbrirGETGet post stats/posts/statsAbrirPOSTCheck post content/posts/checkAbrirGETGet post tags/posts/tagsAbrirGETGet post topics/posts/topicsAbrirPOSTCreate posts in bulk/posts/bulkAbrirPUTUpdate posts in bulk/posts/bulkAbrirDELETEDelete posts in bulk/posts/bulkAbrirGETSearch shortlinks/shortlinksAbrirGETGet a shortlink/shortlinks/:shortlinkIdAbrirGETGet shortlink stats/shortlinks/statsAbrirGETGet shortlink authors/shortlinks/authorsAbrirGETGet shortlink brands/shortlinks/brandsAbrirPOSTCreate a shortlink/shortlinksAbrirPOSTCreate shortlinks in bulk/shortlinks/bulkAbrirPUTUpdate a shortlink/shortlinks/:shortlinkIdAbrirPOSTGenerate a message for a shortlink/shortlinks/:shortlinkId/generate-messageAbrirGETSearch themes/themesAbrirGETGet a theme/themes/:themeIdAbrirPOSTCreate a theme/themesAbrirPUTUpdate a theme/themes/:themeIdAbrirDELETEDelete a theme/themes/:themeIdAbrirGETGet theme roles/themes/rolesAbrirGETGet social listening/listening/socialAbrirGETGet organic listening/listening/organicAbrirPUTUpsert an organic with videos/listening/organicAbrirGETGet organic content/listening/organic/:organicIdAbrirGETGet organic stats/listening/organic/statsAbrirGETGet organic aggregation/listening/organic/aggregationAbrirGETSuggest organic filter values/listening/organic/suggest/:fieldAbrirGETGet the leaderboard/posts/leaderboardAbrirPOSTSearch the leaderboard/posts/leaderboard/_searchAbrirGETGet leaderboard categories/posts/leaderboard/categoriesAbrirGETSearch accounts/accountsAbrirPOSTAdd an account/accountsAbrirGETGet account categories/accounts/categoriesAbrirGETSearch account locations/accounts/locationsAbrirGETGet account stats/accounts/statsAbrirGETSearch bots/botsAbrirGETGet a bot/bots/:botIdAbrirPOSTCreate bots in bulk/bots/bulkAbrirPUTUpdate bots in bulk/bots/bulkAbrirDELETEDelete a bot/bots/:botIdAbrirGETGet bot configuration info/bots/infoAbrirGETSearch media/mediaAbrirGETGet media tags/media/tagsAbrirPOSTUpload an image/upload/imageAbrirPOSTCreate upload video token/upload/video/tokenAbrirPOSTCheck uploaded video existence (legacy)/upload/video/existsAbrirPOSTUpload video chunk/upload/video/chunkAbrirPOSTCreate upload document token/upload/document/tokenAbrirPOSTUpload document chunk/upload/document/chunkAbrir

Recorridos de integración habituales

5 endpoints
En esta página

1.Convenciones

Reglas comunes a todos los endpoints.

Autenticación

Envía un token de API en el header Authorization en cada llamada autenticada: Authorization: Bearer YOUR_API_TOKEN. Los tokens de API representan un rol de empresa, no una sesión de usuario web, por lo que /me no está disponible con tokens Bearer. Guárdalos de forma segura, renuévalos desde el back office y no los incluyas nunca en las query strings.

Paginación

Los endpoints de lista devuelven total y un array rows. Recorre los resultados con size y un page con índice cero hasta recuperar total elementos. Usa fields para mantener cada elemento compacto. En GET /posts, size acepta de 0 a 100 y (page + 1) × size no puede superar 10 000: más allá, la API responde 400 Bad Request. Ajusta entonces los filtros, por ejemplo con un rango publishedAt, para llegar a los posts más antiguos.

Fechas y husos horarios

Las fechas están en UTC con formato ISO-8601, por ejemplo 2026-05-27T23:59:59Z. Los parámetros de fecha y las fechas enviadas en el cuerpo de las peticiones, como createdAt, publishedAt o schedule.startDate, aceptan el formato YYYY-MM-DDTHH:MM:SSZ con o sin fracciones de segundo, por ejemplo 2026-05-27T23:59:59.123Z: una fecha leída en una respuesta se puede reenviar tal cual. La fracción se ignora, y un desfase horario como +02:00 devuelve 400 Bad Request. Los filtros de rango usan ".." entre dos valores, por ejemplo publishedAt:-30 days..now; en los endpoints de búsqueda de posts y de listening, el límite superior queda excluido.

Rate limits

Los límites se cuentan por empresa y por endpoint, y los comparten todos los tokens de API de la empresa, en ventanas fijas que se abren con la primera llamada. La referencia de endpoints indica cada límite; un endpoint que no muestra ninguno no tiene límite de llamadas por endpoint. Cada respuesta de un endpoint limitado incluye los headers RateLimit-Limit, RateLimit-Remaining y RateLimit-Reset; RateLimit-Reset indica los segundos que faltan para que termine la ventana. Una vez alcanzado el límite, cada llamada devuelve 429 hasta que termina la ventana, con un header Retry-After con el mismo número de segundos y un cuerpo JSON. GET /listening/social llamado con un token de API Bearer se descuenta del pack de Social Listening de la empresa, sin ventana por endpoint ni headers RateLimit; una sesión de usuario de API heredada está limitada a 720 llamadas cada 12 horas por usuario y recibe los headers RateLimit. Una sesión de usuario de API heredada es un usuario conectado con el rol API, no un token Bearer.

Estados de los posts

Sin un valor de estado en filters (filtersOr no cuenta), GET /posts, /posts/aggregation y /posts/histogram devuelven o cuentan todos los estados salvo los posts eliminados (404) y obsoletos (410), incluidos los borradores, los posts programados, los pendientes de validación y las publicaciones con error. GET /posts/stats no aplica ningún filtro de estado por defecto. Añade filtersOr=status:200,203,204, el grupo «Publicado» del back office, para obtener cifras coherentes con los contenidos publicados. La referencia de GET /posts enumera todos los valores de estado.

Identificadores

Los posts, shortlinks y contenidos orgánicos usan UUID (string). Las brands, las cuentas sociales y los temas usan ID enteros. Transmite los identificadores tal como los devuelve la API.

Nombres de contadores

En los endpoints basados en posts, usa los nombres de contadores documentados en sort, filters, filtersOr, excludes, los triggers de bots y las métricas de post-rate. Los endpoints de Organic Listening y Shortlink exponen sus propios contadores y rechazan los contadores de posts con un error 400 Bad Request.

2.Parámetros de búsqueda

La mayoría de los endpoints de lista comparten la misma gramática de consulta.

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 20 or endpoint-specific
pageintZero-based page number.Por defecto: 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.Contadores disponibles

Usa esta lista como superficie pública de contadores por familia de endpoints. Los nombres de contadores antiguos que se han sustituido se omiten a propósito.

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

Contadores

clickclick_uniquectrengagementfacebook_engagement_per_minuteweighted_facebook_engagementweighted_facebook_engagement_per_minutefacebook_commentfacebook_total_reactionfacebook_sharereachimpressionvideoViewsvideo_views_3secfacebook_post_video_avg_time_watchedvideo_completion_rateoverperformingrepostsquotesmonetizationsnapchat_screenshotssubscriberfacebook_link_clicksfacebook_other_clicksfacebook_photo_viewfacebook_video_playfacebook_post_video_views_60s_excludes_shorterinstagram_savedinstagram_total_interactionsinstagram_clips_replays_countlinkedin_private_click_countyoutube_likesyoutube_dislikesyoutube_estimated_minutes_watched

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

Organic Listening

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

Contadores

clicknew_leadquantityamountpaywall_viewpaywall_unlockedpaywall_clickpaywall_subscribe_clickpaywall_login_clicksubscription_completecomment_countvideo_viewsview_in_stockview_out_of_stockpriceprice_variationlow_pricehigh_priceratingreview_countstock

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

Shortlinks

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

Contadores

click

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

Accounts

/accounts/accounts/stats

Contadores

follower_count

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

4.Errores

Basa tu gestión en el código HTTP y no en el texto del mensaje. Los códigos conservan su significado HTTP estándar.

EstadoSignificado
200OKLa petición se ha completado correctamente.
400Petición no válidaEl body o los parámetros están mal formados o no superan la validación.
401No autenticadoEl token Bearer del header Authorization falta o no es válido. Comprueba el valor del token o renuévalo desde el back office antes de repetir la llamada.
403ProhibidoEl token de API es válido, pero no tiene permisos sobre este recurso.
404No encontradoEl recurso no existe o tu empresa no puede verlo.
409ConflictoUna petición PUT /posts/bulk sobre un solo post envía un schedule, un publishedAt, un shortId o un status que no se puede aplicar, o un post de la petición cambia un publishedAt, un postType o un shortlink que ya no se puede cambiar. El mensaje indica el motivo y no se modifica nada.
423BloqueadoUn post de una petición PUT /posts/bulk se ha modificado hace menos de 3 segundos, o se está modificando o publicando. Si el header Retry-After está presente, reintenta después de ese número de segundos; si no, reintenta unos segundos más tarde.
429Demasiadas peticionesSe ha alcanzado un límite de tasa. Si el header Retry-After está presente, espera ese número de segundos antes de reintentar.
500Error del servidorError inesperado del servidor. Reintenta las lecturas idempotentes con un backoff.

Las respuestas de error devuelven un body JSON que describe el problema, pero lo que cuenta es el código HTTP: construye tu gestión de errores en torno al código, no al texto del mensaje.

Buenas prácticas de gestión

  • Trata cualquier estado que no sea 2xx como un fallo y lee el body JSON para obtener contexto.
  • Ante un 401, comprueba el valor del token de API, renuévalo si es necesario y repite la petición.
  • Ante un 409, lee el motivo en el mensaje, corrige la petición y reenvíala enseguida: una petición que no ha modificado ningún post no deja ningún bloqueo.
  • Ante un 423, espera el número de segundos del header Retry-After, 3 como máximo tras una actualización terminada, y reenvía la misma petición. Un 423 sin Retry-After significa que el post se está publicando: reintenta unos segundos más tarde.
  • Ante un 429 del rate limiter, espera el número de segundos del header Retry-After, que también se envía en RateLimit-Reset, antes de reintentar. Un 429 causado por la cuota de YouTube en los endpoints de playlists no tiene Retry-After: reintenta más tarde.
  • Ante un 5xx, reintenta las lecturas idempotentes con un backoff exponencial.

Referencia

5.Brands

2 endpoints

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

GET/brands/:brandIdBearer obligatorio

5.2.Get a brand

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

Parámetros query

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

Campos devueltos

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

Ejemplo

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 obligatorio

6.2.Get a social account

Return one connected social account with publishing metadata.

Parámetros query

CampoTipoDescripción
fieldsstringComma-separated fields to include.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

6.3.Get publishing quota

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

Parámetros query

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
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.Por defecto: false

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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'

Ejemplo de respuesta

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 obligatorio

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

Parámetros body

CampoTipoDescripción
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.Por defecto: private

Campos devueltos

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

Ejemplo

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

Ejemplo de respuesta

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 obligatorio

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

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

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

Parámetros query

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
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.Por defecto: 50
pageTokenstringToken of the page to read, taken from nextPageToken.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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'

Ejemplo de respuesta

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 obligatorio

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

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
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.Por defecto: false

Campos devueltos

CampoTipoDescripción
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`.

Ejemplo

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.

¿Publicaciones de tus cuentas conectadas o del Social Listening? Lee la guía del Social Listening

GET/posts/:postIdBearer obligatorio

7.2.Get a post

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

Parámetros query

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

Campos devueltos

CampoTipoDescripción
idstringPost ID.
createdAtdateCreation date.
publishedAtdatePublication date.
messagestringPost message.
postTypestringPost type.
socialAccountSocialAccountTarget social account.
statusintPost status.

Ejemplo

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 obligatorio

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

Parámetros query

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
totalintTotal number of categories found.
rows.keystringCategory value.
rows.doc_countintNumber of posts in the category.

Ejemplo

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 obligatorio

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

Parámetros query

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 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.

Campos devueltos

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

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 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.

Campos devueltos

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

Ejemplo

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 obligatorio

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

Parámetros query

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
totalintTotal number of languages found.
rows.keystringLanguage code.
rows.doc_countintNumber of posts for the language.

Ejemplo

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 obligatorio

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.

Parámetros query

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

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
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.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
totalintTotal number of aggregation buckets.
rows.keystringBucket key.
rows.valueintNumber of posts in the bucket.

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 20 or endpoint-specific
pageintZero-based page number.Por defecto: 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.

Campos devueltos

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

Ejemplo

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 obligatorio

7.10.Check post content

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

Parámetros body

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

Campos devueltos

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

Ejemplo

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 obligatorio

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

Parámetros query

CampoTipoDescripción
sizeintCount of results to return.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
totalintTotal number of items found.
rows.keystringTag.
rows.doc_countintNumber of published posts where the tag was found.

Ejemplo

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 obligatorio

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

Parámetros query

CampoTipoDescripción
sizeintCount of results to return.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
totalintTotal number of items found.
rows.keystringTopic.
rows.doc_countintNumber of published posts where the topic was found.

Ejemplo

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 obligatorio

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

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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'

Ejemplo de respuesta

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 obligatorio

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

Parámetros body

CampoTipoDescripción
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.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

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

Parámetros body

CampoTipoDescripción
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.Por defecto: false

Campos devueltos

CampoTipoDescripción
Post[]Post[]Deleted posts or per-item errors.

Ejemplo

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 obligatorio

9.2.Get a theme

Return one saved theme by ID.

Parámetros query

CampoTipoDescripción
fieldsstringComma-separated fields to include.
embedstringUse embed=roles to include associated roles.

Campos devueltos

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

Ejemplo

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 obligatorio

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

Parámetros body

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

Campos devueltos

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

Ejemplo

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 obligatorio

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

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

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

Ejemplo

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 obligatorio

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

Campos devueltos

CampoTipoDescripción
deletedboolWhether the theme was deleted.

Ejemplo

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

9.6.Get theme roles

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

Campos devueltos

CampoTipoDescripción
totalintTotal number of roles found.
rowsAclRoles[]Available roles.

Ejemplo

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.

Cómo recopila el Social Listening las publicaciones y cómo exportarlas: lee la guía

PUT/listening/organicBearer obligatorio

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

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

10.4.Get organic content

Return one indexed organic content item by ID.

Parámetros query

CampoTipoDescripción
fieldsstringComma-separated fields to include.

Campos devueltos

CampoTipoDescripción
idstringOrganic content ID.
canonicalUrlstringCanonical URL.
titlestringTitle.
descriptionstringDescription.
publishedAtdatePublication date.
counters.totalCounterTotal[]Aggregated counters.

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 20 or endpoint-specific
pageintZero-based page number.Por defecto: 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.

Campos devueltos

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

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
totalintTotal number of buckets.
rows.keystringBucket key.
rows.doc_countintBucket count.

Ejemplo

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 obligatorio

10.7.Suggest organic filter values

Return autocomplete suggestions for an Organic Listening field.

Parámetros query

CampoTipoDescripción
qstringSuggestion query.
sizeintCount of suggestions to return.Por defecto: 10
filtersstringOptional filters to constrain suggestions.

Campos devueltos

CampoTipoDescripción
rowsobject[]Suggested field values.
rows.keystringSuggested value.

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
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.Por defecto: 20
pageintZero-based page number.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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'

Ejemplo de respuesta

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
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.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

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.

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
AccountAccountCreated or updated account, with the same fields as search results.

Ejemplo

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 obligatorio

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

Parámetros query

CampoTipoDescripción
sizeintCount of results to return.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
totalintTotal number of categories found.
rows.keystringCategory value.
rows.doc_countintNumber of accounts in the category.

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
sizeintCount of results to return.Por defecto: 20
qstringLocation search query.
filters*stringAND filters. The type:<network> filter selecting the social network to search is required.

Campos devueltos

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

Ejemplo

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 obligatorio

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.

Parámetros query

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

13.2.Get a bot

Return one bot by UUID.

Parámetros query

CampoTipoDescripción
fieldsstringComma-separated fields to include.
embedstringCan embed running and spamControl.

Campos devueltos

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

Ejemplo

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 obligatorio

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.

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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

Ejemplo de respuesta

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 obligatorio

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.

Parámetros body

CampoTipoDescripción
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.

Ejemplo

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

Ejemplo de respuesta

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 obligatorio

13.5.Delete a bot

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

Ejemplo

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 obligatorio

13.6.Get bot configuration info

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

Parámetros query

CampoTipoDescripción
sourceUrnstringOptional source URN to evaluate source-specific configuration.
targetUrnstringOptional target URN to evaluate target-specific configuration.

Campos devueltos

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

Ejemplo

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 obligatorio

14.2.Get media tags

Return media tags for autocomplete and asset filtering.

Parámetros query

CampoTipoDescripción
sizeintMaximum number of results to return.Por defecto: 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.

Campos devueltos

CampoTipoDescripción
totalintTotal number of tags found.
rows.keystringTag value.
rows.doc_countintNumber of media items using the tag.

Ejemplo

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 obligatorio

15.1.Upload an image

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

Parámetros body

CampoTipoDescripción
b64Image*stringBase64 encoded image.
keepFormatboolKeep the original format when possible.
faceDetectboolRun face detection/cropping assistance.Por defecto: true

Campos devueltos

CampoTipoDescripción
slugstringFontan image slug.
urlstringImage URL when available.

Ejemplo

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 obligatorio

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.

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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

Ejemplo de respuesta

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 obligatorioObsoleto

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.

Parámetros body

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

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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

Ejemplo de respuesta

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

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.

Parámetros body

CampoTipoDescripción
token*stringUpload token.
chunk*intChunk number.
data*stringChunk bytes as base64.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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 obligatorio

15.5.Create upload document token

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

Parámetros body

CampoTipoDescripción
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.

Campos devueltos

CampoTipoDescripción
tokenstringUpload token.
sizeintFile size in bytes.
chunkSizeintChunk size in bytes.
chunkIndexintNext chunk index.

Ejemplo

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 obligatorio

15.6.Upload document chunk

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

Parámetros body

CampoTipoDescripción
token*stringUpload token.
chunk*intChunk number.
data*stringChunk bytes as base64.

Campos devueltos

CampoTipoDescripción
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.

Ejemplo

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