/api/v4/social/bulk-postsCreate posts in bulk
Validates and queues up to 500 social posts in one request, returning a job to poll for per-row results.
Parameters
Body
A nested field marked required applies only when you send its parent. If the parent is optional and you omit it, its required children do not apply.
| Name | Type | Required | Description |
|---|---|---|---|
| input | object | required | Wrapper object. The entire request body must be nested under `input`. |
| input.posts | array of objects | required | The rows to create, 1 to 500 of them. Each row is the same object POST /social/posts takes as its `input`; the per-row fields are listed below. |
| input.name | string | optional | Optional label for the job itself, to identify it in your own logs. Not shown to any audience. |
| input.posts[].socialProfileId | string | required | UUID of the brand publishing this row. Rows may target different brands in one request. |
| input.posts[].name | string | required | Internal name for the post. |
| input.posts[].platforms | array of strings | required | Platforms for this row: `FACEBOOK`, `INSTAGRAM`, `TWITTER` (X), `LINKEDIN`, `PINTEREST`. Each must already have a connection on that row's brand. |
| input.posts[].content | string | required | The post body. |
| input.posts[].action | string | required | `DRAFT`, `SCHEDULE` (requires a schedule) or `PUBLISH`. |
| input.posts[].mediaUrls | array of strings | optional | Public https image URLs. Required when the row targets Instagram or Pinterest. |
| input.posts[].videoUrls | array of strings | optional | Public https video URLs. |
| input.posts[].link | string | optional | Link to attach where the platform supports one. |
| input.posts[].scheduledAt | string | optional | ISO-8601 publish instant with an offset. Alternative to `scheduleDate` + `scheduleTime`, never both. |
| input.posts[].scheduleDate | string | optional | `YYYY-MM-DD`, interpreted in that row's brand timezone. Send with `scheduleTime`. |
| input.posts[].scheduleTime | string | optional | `HH:MM`, interpreted in that row's brand timezone. Send with `scheduleDate`. |
| input.posts[].connectedChannelIds | array of strings | optional | Restrict this row to specific connections by ID. |
| input.posts[].clientReference | string | optional | Your own identifier for this row, echoed back on the matching job result. The most reliable way to reconcile a batch against your own records. |
Sample request
Ready-to-paste body. Replace placeholder IDs and values with yours.
{ "input": { "name": "Autumn campaign load", "posts": [ { "socialProfileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902", "name": "Autumn week 1", "platforms": ["INSTAGRAM", "FACEBOOK"], "content": "Week one of the autumn collection: the Fairfield oak sideboard, finished by hand in the workshop.", "mediaUrls": ["https://cdn.example.com/social/autumn-w1.jpg"], "action": "SCHEDULE", "scheduleDate": "2026-10-02", "scheduleTime": "09:30", "clientReference": "campaign-4471-slot-01" }, { "socialProfileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902", "name": "Autumn week 2", "platforms": ["INSTAGRAM", "FACEBOOK", "LINKEDIN"], "content": "Week two: the Halcyon armchair. Sixty hours of work, one chair.", "mediaUrls": ["https://cdn.example.com/social/autumn-w2.jpg"], "action": "SCHEDULE", "scheduleDate": "2026-10-09", "scheduleTime": "09:30", "clientReference": "campaign-4471-slot-02" }, { "socialProfileId": "a90f7c22-5db1-4e83-9f40-2b7c6a1e5d33", "name": "Eastside autumn teaser", "platforms": ["INSTAGRAM"], "content": "Something solid is arriving next week. Stay tuned.", "mediaUrls": ["https://cdn.example.com/social/eastside-teaser.jpg"], "action": "SCHEDULE", "scheduledAt": "2026-10-01T08:00:00-04:00", "clientReference": "campaign-4471-slot-03" } ] }}Responses
200The whole batch validated. A job was queued, poll GET /social/bulk-posts/{bulkJobId} for per-row results.
{ "data": { "createBulkSocialMediaPosts": { "success": true, "error": [], "job": { "id": "9b3f18d0-7a26-4c51-8e94-1f0d5c6b7a82", "status": "QUEUED", "totalCount": 3, "processedCount": 0, "successCount": 0, "failureCount": 0, "createdAt": "2026-09-28T14:04:52Z", "updatedAt": "2026-09-28T14:04:52Z", "results": [] } } }}200At least one row failed validation, so **nothing was created**. `success` is `false`, no job is returned, and each rejected row is reported with its 0-based `index`.
{ "data": { "createBulkSocialMediaPosts": { "success": false, "error": [ { "message": "SY95050: Bulk validation failed, no posts were created", "code": "SY95050", "contextInfo": null } ], "job": { "id": null, "status": "FAILED", "totalCount": 3, "processedCount": 0, "successCount": 0, "failureCount": 2, "createdAt": "2026-09-28T14:04:52Z", "updatedAt": "2026-09-28T14:04:52Z", "results": [ { "index": 1, "clientReference": "campaign-4471-slot-02", "socialProfileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902", "platforms": ["INSTAGRAM", "FACEBOOK", "LINKEDIN"], "status": "INVALID", "socialPostId": null, "postStatus": null, "errors": [ { "message": "SY95045: No connected channel for LINKEDIN on this brand", "code": "SY95045", "contextInfo": [{ "key": "platform", "value": "LINKEDIN" }] } ] }, { "index": 2, "clientReference": "campaign-4471-slot-03", "socialProfileId": "a90f7c22-5db1-4e83-9f40-2b7c6a1e5d33", "platforms": ["INSTAGRAM"], "status": "INVALID", "socialPostId": null, "postStatus": null, "errors": [ { "message": "SY95046: scheduledAt must carry a UTC offset", "code": "SY95046", "contextInfo": null } ] } ] } } }}401Unauthenticated, missing or invalid API key.
Creates many social posts in one request. The rows travel in the request
payload — there is no file upload and no CSV, just an array of the same objects
POST /social/posts takes one at a time. Up to
500 rows per request.
Use case: loading a quarter of content in one call. Your planning tool has the
client's whole content calendar. Build one row per slot, tag each with your own
clientReference, and send them together. The batch either succeeds as a unit or
tells you precisely which rows to fix. Store the returned job.id and poll
GET /social/bulk-posts/{bulkJobId} until
status is terminal, then reconcile each result by its clientReference.
The whole batch is validated up front
Validation runs across every row before anything is created, and the batch is atomic at that stage:
- If any row is invalid, nothing is created.
successisfalse,SY95050comes back inerror, and each bad row appears injob.results[]with its 0-basedindex, itsclientReference, and the reason inerrors[]. Fix those rows and resend the whole batch. - If every row is valid, a job is queued,
successistrue, and you get ajob.id. Individual rows can still fail later, when the platform rejects them at publish time; that shows up on the job, not here.
Reconcile by clientReference where you can and by index otherwise — the rows
themselves are not echoed back, so those two fields are the only link to your own
data.
Notes.
- The request body must be wrapped in an
inputobject, with the rows ininput.posts. - Check
success, not the HTTP status. A batch-wide rejection is anHTTP 200withsuccess: falseandSY95050inerror. - Maximum 500 rows per request. A larger array is rejected outright with
SY95048and nothing is created; split it and send several batches. - The brand on each row is
socialProfileId.brandIdis only ever a URL path token, and this route has no path token at all. - Rows may target different brands. Each row's
platformsare checked against that row's own brand, so a platform connected on one brand and not another fails only the rows that need it. - Each row follows the same schedule rules as a single post:
scheduledAtorscheduleDate+scheduleTime, never both, andscheduleDate/scheduleTimeare read in that row's brand timezone. Mixing the two forms across different rows in one batch is fine. - Rate limits apply to the request, not to the rows: a 500-row batch costs one request against your budget. See Rate limits.
Write operation: not executed against the live account. The request and response shown are built from the API schema; this publishes to real social accounts when run.
curl -X POST 'https://listingsapi.com/api/v4/social/bulk-posts' \ -H "Authorization: API $LISTINGSAPI_KEY" \ -H 'Content-Type: application/json' \ -d '{ "input": { "name": "Autumn campaign load", "posts": [ { "socialProfileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902", "name": "Autumn week 1", "platforms": [ "INSTAGRAM", "FACEBOOK" ], "content": "Week one of the autumn collection: the Fairfield oak sideboard, finished by hand in the workshop.", "mediaUrls": [ "https://cdn.example.com/social/autumn-w1.jpg" ], "action": "SCHEDULE", "scheduleDate": "2026-10-02", "scheduleTime": "09:30", "clientReference": "campaign-4471-slot-01" }, { "socialProfileId": "6f2c9a41-7b0e-4d55-9a2f-3c8e1d47b902", "name": "Autumn week 2", "platforms": [ "INSTAGRAM", "FACEBOOK", "LINKEDIN" ], "content": "Week two: the Halcyon armchair. Sixty hours of work, one chair.", "mediaUrls": [ "https://cdn.example.com/social/autumn-w2.jpg" ], "action": "SCHEDULE", "scheduleDate": "2026-10-09", "scheduleTime": "09:30", "clientReference": "campaign-4471-slot-02" }, { "socialProfileId": "a90f7c22-5db1-4e83-9f40-2b7c6a1e5d33", "name": "Eastside autumn teaser", "platforms": [ "INSTAGRAM" ], "content": "Something solid is arriving next week. Stay tuned.", "mediaUrls": [ "https://cdn.example.com/social/eastside-teaser.jpg" ], "action": "SCHEDULE", "scheduledAt": "2026-10-01T08:00:00-04:00", "clientReference": "campaign-4471-slot-03" } ] } }'