Social

Every Social endpoint: brands, hosted connection links, single and bulk publishing, scheduling, insights, and the connection cap that governs all of it.

The Social API publishes to Facebook, Instagram, X, LinkedIn and Pinterest. Two objects carry the whole model. A brand is the identity you publish as: a name, a timezone, and the social accounts attached to it. A connection is one authorized social account on one brand on one platform. Brands are unlimited on an API plan and cost nothing. Connections are what the plan meters, so the connection cap is the constraint to design around.

Connecting an account cannot be done from your server. The end user has to authorize it at the platform, so you request a hosted link and redirect them to it. Publishing is asynchronous: every create call returns as soon as the post is accepted, and you poll for the per-channel result.

Endpoints at a glance

MethodPathWhat it doesReference
POST/social/brandsCreate a brand.Create a brand
GET/social/brandsA page of the account's brands.List brands
GET/social/brands/{brandId}One brand with its full detail.Get a brand
POST/social/brands/updateChange fields on an existing brand.Update a brand
POST/social/brands/archiveArchive a brand and its connections.Archive a brand
POST/social/brands/{brandId}/connections/linkA hosted URL that connects one platform.Get a connection link
GET/social/brands/{brandId}/connectionsThe accounts connected to a brand.List a brand's connections
POST/social/connections/disconnectRemove one connection.Disconnect a connection
POST/social/postsDraft, schedule or publish one post.Create a social post
GET/social/posts/{postId}One post with per-channel publish state.Get a social post
GET/social/brands/{brandId}/postsA page of a brand's posts.List a brand's posts
POST/social/posts/cancelCancel a post that has not published yet.Cancel a scheduled post
GET/social/posts/{postId}/insightsEngagement counts and comments for one channel.Get post insights and comments
POST/social/bulk-postsUp to 500 posts in one request.Create posts in bulk
GET/social/bulk-posts/{bulkJobId}A bulk job's status and per-row results.Get a bulk post job
GET/social/limitsThe connection cap and what is spent against it.Get Social plan limits and usage

Values you will use everywhere

FieldAccepted values
platforms[] and channelFACEBOOK, INSTAGRAM, TWITTER (X), LINKEDIN, PINTEREST
actionDRAFT, SCHEDULE, PUBLISH

Platform values are GraphQL enum names, so they are uppercase. Sending "instagram" is rejected. TWITTER is X; the enum name did not change when the product did.

Create a brand

POST /social/brands

profileName and timezone are required. The timezone is an object, and tzCode is the field that matters: it is the IANA identifier used to interpret every scheduleDate and scheduleTime on this brand's posts. The other three keys are display labels.

Shell
curl -X POST https://listingsapi.com/api/v4/social/brands \  -H "Authorization: API $LISTINGSAPI_KEY" \  -H "Content-Type: application/json" \  -d '{    "input": {      "profileName": "Jenny Home",      "timezone": {        "label": "(GMT-05:00) Eastern Time",        "tzCode": "America/New_York",        "name": "Eastern Standard Time",        "utc": "-05:00"      },      "categories": ["Furniture Store"],      "siteUrl": "https://jennyhome.example"    }  }'

Keep the returned brand id. It is the brandId path token on every other brand endpoint, and the socialProfileId body field when you publish.

List and read brands

GET /social/brands returns a page of brands. GET /social/brands/{brandId} returns one with its full detail.

Shell
curl -H "Authorization: API $LISTINGSAPI_KEY" \  "https://listingsapi.com/api/v4/social/brands?page=1&size=25"

An id that returns SY95043 is either not on this account or already archived.

Update a brand

POST /social/brands/update

The brand id goes in the body as input.profileId, not in the path. Send only the fields you are changing; anything you omit keeps its current value.

Shell
curl -X POST https://listingsapi.com/api/v4/social/brands/update \  -H "Authorization: API $LISTINGSAPI_KEY" \  -H "Content-Type: application/json" \  -d '{"input": {"profileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902", "bio": "Handmade furniture for real homes."}}'

Archive a brand

POST /social/brands/archive

A brand has to have existed for 90 days before it can be archived. Below that the call is refused with SY95051 and nothing changes. Once the brand is old enough, archiving takes effect immediately and archives every connection on it at the same time.

Those connections stop publishing at once, but their slots stay counted until the end of the billing period, exactly as a manual disconnect does. Archiving is not a way to free capacity today.

Shell
curl -X POST https://listingsapi.com/api/v4/social/brands/archive \  -H "Authorization: API $LISTINGSAPI_KEY" \  -H "Content-Type: application/json" \  -d '{"input": {"socialProfileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902"}}'

POST /social/brands/{brandId}/connections/link

This is the only way to add a connection. The response carries a hosted url valid for expiresIn seconds. Redirect the end user to it, they authorize at the platform, and they are returned to your redirectUrl with the outcome in the query string. Pass errorUrl to separate cancellations from successes, and state to carry your own correlation value through the round trip.

Remember this body is flat, with no input wrapper.

Shell
curl -X POST https://listingsapi.com/api/v4/social/brands/6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902/connections/link \  -H "Authorization: API $LISTINGSAPI_KEY" \  -H "Content-Type: application/json" \  -d '{    "channel": "INSTAGRAM",    "redirectUrl": "https://app.example.com/social/connected",    "errorUrl": "https://app.example.com/social/failed"  }'
JSON
{  "data": {    "connectSocialAccount": {      "status": "success",      "url": "https://social.listingsapi.com/connect/start?token=bfac8e06-8d72-4810-9c50-ae7ee12a7b52",      "expiresIn": 900    }  }}

Check status before redirecting. A failed response returns no url and puts the reason in title, most often the connection cap (SY95042) or a platform the account does not have (SY95044).

List a brand's connections

GET /social/brands/{brandId}/connections

Returns the accounts attached to the brand, each with the id you need to publish to a specific connection or to disconnect it.

Shell
curl -H "Authorization: API $LISTINGSAPI_KEY" \  https://listingsapi.com/api/v4/social/brands/6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902/connections

Disconnect a connection

POST /social/connections/disconnect

Takes the connection id from the list above. The account stops publishing straight away, but the slot it held is not returned to the pool until the billing period ends. Reconnecting the same account to the same brand within the period reuses that slot and costs nothing; attaching it to a different brand takes a second slot.

Shell
curl -X POST https://listingsapi.com/api/v4/social/connections/disconnect \  -H "Authorization: API $LISTINGSAPI_KEY" \  -H "Content-Type: application/json" \  -d '{"input": {"socialProfileConnectedChannelId": "b81d5b3c-4a90-4f6a-9c11-71e0dd2fa4a7"}}'

Publish a post

POST /social/posts

One call publishes the same content to every platform in platforms. Each of those platforms must already have a connection on the brand, or the call returns SY95045. Instagram and Pinterest require at least one entry in mediaUrls.

Schedule a post in one of two ways, never both. scheduledAt is an ISO-8601 timestamp with an offset. scheduleDate and scheduleTime are sent together and are interpreted in the brand's timezone. Sending both, or a schedule in the past, returns SY95046.

Shell
curl -X POST https://listingsapi.com/api/v4/social/posts \  -H "Authorization: API $LISTINGSAPI_KEY" \  -H "Content-Type: application/json" \  -d '{    "input": {      "socialProfileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902",      "name": "Autumn collection launch",      "platforms": ["INSTAGRAM", "FACEBOOK"],      "content": "Our autumn collection lands today.",      "mediaUrls": ["https://cdn.example.com/social/autumn-collection.jpg"],      "action": "SCHEDULE",      "scheduleDate": "2026-10-02",      "scheduleTime": "09:30",      "clientReference": "campaign-4471-slot-01"    }  }'

Set clientReference to your own identifier. It is stored and returned, which lets you reconcile a post against your records without keeping the UUID.

Read one post

GET /social/posts/{postId}

This is how you find out whether publishing worked. The post's own status is a summary; the real answer is per channel in connectedChannels, where each entry carries its own status, a liveLink once the platform accepts it, and reason or error when it does not.

JSON
{  "connectedChannels": [    {      "platform": "INSTAGRAM",      "status": "SUCCESS",      "liveLink": "https://www.instagram.com/p/CxYz1234abc/",      "error": null    }  ]}

Poll this after a PUBLISH. One channel can fail while the others succeed, so check every entry rather than the top-level status alone.

List a brand's posts

GET /social/brands/{brandId}/posts

A page of the brand's posts, with filters for narrowing by state. Use it to reconcile in bulk instead of polling ids one at a time.

Shell
curl -H "Authorization: API $LISTINGSAPI_KEY" \  "https://listingsapi.com/api/v4/social/brands/6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902/posts?page=1&size=25"

Cancel a scheduled post

POST /social/posts/cancel

Cancels a post that has not published yet. A post already live on a platform cannot be pulled back through this endpoint.

Shell
curl -X POST https://listingsapi.com/api/v4/social/posts/cancel \  -H "Authorization: API $LISTINGSAPI_KEY" \  -H "Content-Type: application/json" \  -d '{"input": {"socialPostId": "a3f81c62-9d4e-4a17-b5c0-2e7f6d8a9b10"}}'

Read insights and comments

GET /social/posts/{postId}/insights

Engagement counts and comments for one post on one channel. The channel is required, because a post published to three platforms has three separate sets of numbers.

Shell
curl -H "Authorization: API $LISTINGSAPI_KEY" \  "https://listingsapi.com/api/v4/social/posts/a3f81c62-9d4e-4a17-b5c0-2e7f6d8a9b10/insights?socialConnectedChannelId=b81d5b3c-4a90-4f6a-9c11-71e0dd2fa4a7"

Create posts in bulk

POST /social/bulk-posts

Takes 1 to 500 rows in input.posts. Each row is the same object POST /social/posts takes as its input, and rows may target different brands in the same request.

The whole batch is validated before anything is created. If any row fails you get SY95050 with per-row errors and nothing is queued, so fix and resend. More than 500 rows returns SY95048, again with nothing created. A valid batch returns a job to poll.

Shell
curl -X POST https://listingsapi.com/api/v4/social/bulk-posts \  -H "Authorization: API $LISTINGSAPI_KEY" \  -H "Content-Type: application/json" \  -d '{    "input": {      "name": "October launch batch",      "posts": [        {          "socialProfileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902",          "name": "Launch day",          "platforms": ["FACEBOOK"],          "content": "Doors open at nine.",          "action": "SCHEDULE",          "scheduleDate": "2026-10-02",          "scheduleTime": "09:00"        }      ]    }  }'

Bulk counts as one request against your rate limit, which is what makes it the efficient path for large batches.

Poll a bulk job

GET /social/bulk-posts/{bulkJobId}

Returns the job's status with per-row results, including the post id created for each row and any row that failed after queueing. Jobs are not retained forever; an id that has aged out returns SY95047.

Shell
curl -H "Authorization: API $LISTINGSAPI_KEY" \  https://listingsapi.com/api/v4/social/bulk-posts/f47ac10b-58cc-4372-a567-0e02b2c3d479

Check limits and usage

GET /social/limits

Read this before creating connections rather than after a SY95042. Compare usage.connections against maxConnections: that is the billable count, connected now plus released earlier in this period. usage.connectionsActive is what is live at this moment, and usage.connectionsReleased is how much capacity returns at renewal.

brands comes back null, which means unlimited. Brands are not metered on an API plan.

Shell
curl -H "Authorization: API $LISTINGSAPI_KEY" \  https://listingsapi.com/api/v4/social/limits

Errors you will see

CodeMeaningWhat to do
SY90001No Social subscription on the account.Add the Social product before calling these endpoints.
SY95042At the connection cap for this billing period.Compare usage.connections with maxConnections. A recent disconnect does not clear this, because the slot is held to period end.
SY95043No such brand on this account, or it is archived.Re-read the id from List brands.
SY95044Platform not enabled for the account.Pick a platform the account has, or contact support.
SY95045The brand has no connection for that platform.Send the user through a connection link, or drop the platform from platforms.
SY95046Invalid schedule or timezone.Send scheduledAt or scheduleDate plus scheduleTime, never both.
SY95047No such bulk job.Re-read job.id from the bulk create response.
SY95048More than 500 rows.Split the batch. Nothing was created.
SY95049Invalid redirect URL.redirectUrl and errorUrl must be absolute https URLs.
SY95050Bulk validation failed.Read the per-row errors, fix those rows, resend the batch.
SY95051The brand is younger than 90 days.Wait until it reaches 90 days, then archive.

The full list is in the error code reference.

Next steps

Read Plans and add-ons for how the connection cap, the 5-connection packs and the 90-day archival rule interact, and React to events with webhooks to receive social_post.* and social_connection.* events instead of polling.

The Python SDK covers these endpoints as client.social.brands, client.social.connections, client.social.posts and client.social.bulk; see the Social resource. The Node SDK does not cover Social yet, so call the REST endpoints directly from Node.