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
Values you will use everywhere
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.
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.
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.
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.
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"}}'Get a connection link
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.
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" }'{ "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.
curl -H "Authorization: API $LISTINGSAPI_KEY" \ https://listingsapi.com/api/v4/social/brands/6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902/connectionsDisconnect 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.
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.
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.
{ "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.
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.
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.
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.
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.
curl -H "Authorization: API $LISTINGSAPI_KEY" \ https://listingsapi.com/api/v4/social/bulk-posts/f47ac10b-58cc-4372-a567-0e02b2c3d479Check 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.
curl -H "Authorization: API $LISTINGSAPI_KEY" \ https://listingsapi.com/api/v4/social/limitsErrors you will see
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.