게시물

모든 Posts 엔드포인트와 각 엔드포인트의 cURL, Python 및 Node 예제: 공지, 이벤트 및 혜택, 예약, 사업장별 캠페인과 일괄 캠페인, 상태 폴링 및 삭제.

게시물은 사업장의 Google Business Profile 또는 Facebook 페이지에 게시하는 짧은 소식으로, 공지, 날짜가 지정된 이벤트 또는 쿠폰이 포함된 혜택입니다. 하나의 캠페인으로 여러 사업장과 두 사이트를 동시에 대상으로 지정할 수 있습니다. 게시는 비동기 방식으로 처리되므로 모든 생성 호출은 즉시 반환되며, 결과를 확인하려면 폴링합니다. GET 엔드포인트 3개는 Read(읽기) 키로 사용할 수 있으며, 생성 및 삭제에는 Write(쓰기) 키가 필요합니다.

엔드포인트 한눈에 보기

메서드경로기능참조
POST/posts버튼과 이미지를 선택적으로 포함할 수 있는 일반 메시지 형태의 공지를 게시합니다.공지 게시물 생성
POST/posts제목과 시작 및 종료 기간이 있는 이벤트를 게시합니다.이벤트 게시물 생성
POST/posts쿠폰 코드, 할인 및 이용 조건이 포함된 혜택을 게시합니다.혜택 게시물 생성
GET/posts/{postId}사이트별 게시 상태와 참여 횟수가 포함된 캠페인 1개입니다.게시물 조회
GET/locations/{locationId}/posts특정 사업장을 대상으로 하는 캠페인의 한 페이지입니다.사업장의 게시물 조회
DELETE/posts/{postId}캠페인을 영구적으로 삭제하고 모든 곳에서 게시를 취소합니다.게시물 삭제
POST/bulk-posts여러 사업장에 배포되는 캠페인 1개입니다.일괄 게시물 생성
GET/bulk-posts/{postId}사업장별 분석 내역이 포함된 일괄 캠페인 1개입니다.ID로 일괄 게시물 조회
GET/locations/{locationId}/bulk-posts특정 사업장을 포함하는 일괄 캠페인을 필터링 및 정렬하여 조회합니다.사업장의 일괄 게시물 조회

모든 곳에서 사용하는 값

아래의 열거형 4개는 모든 생성 엔드포인트에서 공통으로 사용합니다.

필드허용되는 값
postTypeANNOUNCEMENT, EVENT, OFFER, COVID19, PRODUCT
postSites 및 모든 siteGOOGLE, FACEBOOK
postCta[].typeBOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, GET_OFFER
postMediaUrl[].typeIMAGE, VIDEO

공지 게시

POST /posts

공지에는 메시지가 필요하며, 사이트마다 버튼 1개와 이미지 1개를 선택적으로 포함할 수 있습니다. postSites에 나열된 사이트마다 postMessage 항목을 1개씩 제공합니다. 게시는 백그라운드에서 실행되므로 응답은 status가 INPROGRESS로 설정된 새 캠페인을 반환합니다.

매개변수

이름전달 위치필수 여부참고
input.postNamebody예내부 캠페인 이름으로, 고객에게는 표시되지 않습니다.
input.locationIdsbody예base64 사업장 ID의 배열입니다.
input.postTypebody예여기서는 ANNOUNCEMENT입니다.
input.postSitesbody예GOOGLE 및 FACEBOOK의 배열입니다.
input.postMessagebody아니요site 및 message가 포함된 객체의 배열입니다.
input.postCtabody아니요site, type, url이 포함된 객체의 배열입니다.
input.postMediaUrlbody아니요site, url, type이 포함된 객체의 배열입니다.
input.clientMutationIdbody아니요요청과 응답을 연결할 수 있도록 변경 없이 반환됩니다.
Announcement
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "Grand Opening Announcement",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="],    "postType": "ANNOUNCEMENT",    "postSites": ["GOOGLE"],    "postMessage": [      { "site": "GOOGLE", "message": "We are now open! Visit us this week for our grand opening." }    ],    "postCta": [      { "site": "GOOGLE", "type": "LEARN_MORE", "url": "https://jennyhome.example/opening" }    ],    "postMediaUrl": [      { "site": "GOOGLE", "url": "https://cdn.example.com/opening.jpg", "type": "IMAGE" }    ]  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "errors": [],      "socialPost": {        "id": "U29jaWFsUG9zdDo0NDEyMg==",        "name": "Grand Opening Announcement",        "status": "INPROGRESS",        "type": "ANNOUNCEMENT",        "sites": ["GOOGLE"],        "publishDetails": [          { "site": "GOOGLE", "status": "PUBLISHING", "createdDate": "2026-06-28", "submissionError": null }        ],        "postLinks": [],        "locationIds": [1800289]      }    }  }}

socialPost.id를 보관합니다. 이 값은 여기에 있는 다른 모든 엔드포인트에 필요한 base64 게시물 ID입니다. 두 SDK의 헬퍼 모두 단일 message 문자열을 받아 사이트별 항목으로 확장하므로, cURL에서만 postMessage 배열을 직접 구성하면 됩니다.

이벤트 게시

POST /posts

이벤트는 날짜가 표시된 카드로 렌더링됩니다. 이벤트 자체의 일정은 postContextInfo에 있습니다. 별도의 postScheduledDates 객체는 게시물 자체의 표시 시점과 만료 시점을 제어하며, 일반적으로 이 기간은 이벤트가 시작되기 전까지입니다. Google은 제목이 없는 이벤트를 거부하며, 두 SDK 모두 title이 누락되면 전송 전에 유효성 검사 오류를 발생시킵니다.

매개변수

이름전달 위치필수 여부참고
input.postTypebody예여기서는 EVENT입니다.
input.postContextInfo.titlebody실제로는 필수이벤트 제목입니다. Google은 제목이 없는 이벤트를 거부합니다.
input.postContextInfo.startDay, endDaybody아니요날짜이며, 형식은 YYYY-MM-DD입니다.
input.postContextInfo.startTime, endTimebody아니요10:00am과 같은 표시용 문자열입니다.
input.postScheduledDatesbody아니요startDatetime, endDatetime, removalSites가 포함된 객체입니다.

공지 표의 모든 항목도 적용됩니다.

Event
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "Summer Sale Kickoff Event",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="],    "postType": "EVENT",    "postSites": ["GOOGLE"],    "postContextInfo": {      "title": "Summer Sale Kickoff",      "startDay": "2026-07-15",      "startTime": "10:00am",      "endDay": "2026-07-15",      "endTime": "06:00pm"    },    "postMessage": [      { "site": "GOOGLE", "message": "Join us for the Summer Sale kickoff, refreshments and up to 40 percent off in store." }    ]  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "socialPost": {        "id": "U29jaWFsUG9zdDo0NDEyMw==",        "status": "INPROGRESS",        "type": "EVENT",        "scheduledStartDate": "2026-07-15",        "scheduledEndDate": "2026-07-15",        "contextInfo": {          "title": "Summer Sale Kickoff",          "startDay": "2026-07-15",          "startTime": "10:00am",          "endDay": "2026-07-15",          "endTime": "06:00pm"        }      }    }  }}

반환된 contextInfo는 이벤트 기간입니다. scheduledStartDate 및 scheduledEndDate는 게시물 자체의 일정을 나타내며, 여기서는 postScheduledDates를 전송하지 않았으므로 이벤트 날짜와 일치합니다.

혜택 게시

POST /posts

혜택은 쿠폰 코드, 할인, 사용 링크 및 약관을 담은 프로모션 카드입니다. 이 모든 내용은 postContextInfo에 들어가며, startDay와 endDay는 게시물이 아닌 혜택의 유효 기간을 정합니다.

매개변수

이름전달 위치필수 여부참고
input.postTypebody예여기서는 OFFER입니다.
input.postContextInfo.titlebody실제로는 필수혜택의 이름을 지정합니다. 두 SDK 모두 이 값을 필수로 요구합니다.
input.postContextInfo.couponCodebody아니요고객이 혜택을 사용할 때 입력하는 코드입니다.
input.postContextInfo.discountbody아니요25%와 같은 자유 형식 텍스트입니다.
input.postContextInfo.redeemUrlbody아니요혜택을 사용할 수 있는 곳입니다.
input.postContextInfo.termsConditionsbody아니요세부 조건입니다.
input.postContextInfo.startDay, endDaybody아니요유효 기간이며, YYYY-MM-DD입니다.
Offer
curl -X POST https://listingsapi.com/api/v4/posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "Festive 25 Percent Off",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ=="],    "postType": "OFFER",    "postSites": ["GOOGLE"],    "postContextInfo": {      "title": "25% off storewide",      "couponCode": "FESTIVE25",      "discount": "25%",      "redeemUrl": "https://jennyhome.example/festive",      "termsConditions": "Valid in-store only through July 6.",      "startDay": "2026-06-22",      "endDay": "2026-07-06"    },    "postMessage": [      { "site": "GOOGLE", "message": "Celebrate with us, 25 percent off everything in store this week." }    ],    "postCta": [      { "site": "GOOGLE", "type": "GET_OFFER", "url": "https://jennyhome.example/festive" }    ]  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "socialPost": {        "id": "U29jaWFsUG9zdDo0NDEyNA==",        "status": "INPROGRESS",        "type": "OFFER",        "contextInfo": {          "title": "25% off storewide",          "couponCode": "FESTIVE25",          "discount": "25%",          "startDay": "2026-06-22",          "endDay": "2026-07-06"        }      }    }  }}

혜택 버튼에는 Google의 경우 ORDER를, Facebook의 경우 GET_OFFER를 사용합니다. 프로모션이 일찍 종료되면 캠페인을 수정하지 말고 삭제합니다.

게시물 1개와 해당 상태 조회

GET /posts/{postId}

접수된 캠페인의 게시 결과를 반환합니다. 응답의 data.socialPostView 아래에 캠페인 콘텐츠, 사이트별 publishDetails 항목, 참여 집계 수치가 포함되며, 게시에 성공하면 실제 게시물 링크도 포함됩니다.

매개변수

이름전달 위치필수 여부참고
postIdpath예생성 응답 또는 목록에서 가져온 Base64 게시물 id입니다.
Get a post
curl https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "socialPostView": {      "socialPostId": "U29jaWFsUG9zdDo0NDEyMg==",      "socialPostInfo": {        "status": "SUCCESS",        "type": "ANNOUNCEMENT",        "views": 128,        "clicks": 14,        "publishDetails": [          { "site": "GOOGLE", "status": "PUBLISHED", "publishedDate": "2026-06-28", "submissionError": null }        ],        "postLinks": [          { "site": "GOOGLE", "link": "https://www.google.com/maps/place/?post=44122" }        ]      },      "publishedLocationsCount": 1,      "socialPostAnalyticsBySite": [        { "site": "GOOGLE", "views": 128, "clicks": 14, "reactions": 3, "shares": 0, "comments": 1 }      ]    }  }}

중요한 상태 필드는 2개이며, 각각 서로 다른 질문에 답합니다.

필드값의미
socialPostInfo.statusINPROGRESS접수되었으며, 게시가 아직 진행 중입니다.
SUCCESS게시가 완료되었습니다. 사이트별 결과는 publishDetails에서 확인합니다.
SCHEDULEDpostScheduledDates 기간이 시작되기를 기다리고 있습니다.
EXPIRED예약된 기간이 종료되어 게시물이 더 이상 게시되어 있지 않습니다.
publishDetails[].statusPUBLISHING해당 사이트가 아직 응답하지 않았습니다.
PUBLISHED해당 사이트에 게시되어 있습니다. URL은 postLinks에서 확인합니다.
ERROR해당 사이트가 게시를 거부했습니다. 이유는 submissionError에 나와 있습니다.

캠페인이 SUCCESS로 표시되더라도 그 안의 한 사이트는 ERROR 상태일 수 있으므로, 항상 최상위 상태만 보지 말고 publishDetails도 확인합니다.

사업장의 게시물 목록 조회

GET /locations/{locationId}/posts

특정 사업장을 포함하는 모든 캠페인의 이력을 오프셋 기반 페이지네이션으로 제공하며, data.postsByLocation 아래에 반환합니다. 새 게시물을 예약하기 전에 이를 사용하여 이미 게시된 내용을 점검합니다.

매개변수

이름전달 위치필수 여부참고
locationIdpath예변경 없이 전달하는 Base64 id입니다.
pagequery아니요1부터 시작하는 페이지 번호이며, 기본값은 1입니다.
perPagequery아니요기본값은 10이며, 50을 초과해서는 안 됩니다.
Posts on a location
curl "https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/posts?page=1&perPage=10" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "postsByLocation": {      "records": [        {          "id": "U29jaWFsUG9zdDo0NDEyMg==",          "name": "Summer Hours Update",          "status": "SUCCESS",          "type": "ANNOUNCEMENT",          "createdAt": "2026-06-28",          "views": 128,          "clicks": 14,          "sites": ["GOOGLE"]        }      ],      "pageInfo": { "totalPages": 1, "hasNextPage": false, "totalRecords": 1 }    }  }}

이 엔드포인트는 서버 측 필터를 제공하지 않습니다. page와 perPage만 허용하고 나머지는 모두 무시하므로, 각 레코드의 type과 status를 읽고 자체 코드에서 필터링합니다. 서버 측에서 정렬하고 필터링하려면 대신 아래의 일괄 목록 조회 엔드포인트를 사용합니다.

게시물 삭제

DELETE /posts/{postId}

삭제는 영구적이며, 게시물이 게시된 모든 사이트에서 게시를 취소합니다. 실행 취소 기능이나 수정 엔드포인트가 없으므로, 게시 중인 게시물을 바로잡으려면 삭제한 후 대체 게시물을 게시해야 합니다.

매개변수

이름전달 위치필수 여부참고
postIdpath예Base64 게시물 id입니다.
clientMutationIdbody아니요선택 사항이며, 요청과 응답을 연결할 수 있도록 변경 없이 반환됩니다.
Delete
curl -X DELETE https://listingsapi.com/api/v4/posts/U29jaWFsUG9zdDo0NDEyMg== \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "clientMutationId": "delete-44122-req-1" }'
JSON
{  "data": {    "deleteSocialPost": {      "success": true,      "socialPostId": "U29jaWFsUG9zdDo0NDEyMg=="    }  }}

존재하지 않거나 이미 삭제된 게시물을 삭제하면 success: false 대신 data가 null인 오류 응답이 반환됩니다.

여러 사업장에 캠페인 1개 게시

POST /bulk-posts

POST /posts와 동일한 규약을 사용하며, 다수의 사업장을 위해 설계되었습니다. locationIds에 모든 사업장을 나열하면 캠페인 1개로 모두 처리합니다. 응답은 POST /posts와 마찬가지로 createSocialPost 키 아래에 반환됩니다.

매개변수

이름전달 위치필수 여부참고
input.postNamebody예내부용 캠페인 이름입니다.
input.locationIdsbody예게시 대상인 모든 사업장의 base64 id입니다.
input.postTypebody예EVENT, OFFER 및 PRODUCT에는 postContextInfo 또는 products도 필요합니다.
input.postSitesbody예GOOGLE, FACEBOOK 또는 둘 다입니다.
input.postScheduledDatesbody아니요ISO 8601 형식의 startDatetime과 endDatetime입니다. 게시물을 SCHEDULED 상태로 생성합니다.
input.componentIdentifierbody아니요직접 지정하는 참조 값이며, 나중에 필터로 사용하기에 유용합니다.
Bulk publish
curl -X POST https://listingsapi.com/api/v4/bulk-posts \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "postName": "July Reopening Announcement",    "locationIds": ["TG9jYXRpb246MTgwMDI4OQ==", "TG9jYXRpb246MTgwMDI5MA=="],    "postType": "ANNOUNCEMENT",    "postSites": ["GOOGLE", "FACEBOOK"],    "postMessage": [      { "site": "GOOGLE", "message": "We are back. Visit us this week for our summer menu." },      { "site": "FACEBOOK", "message": "We are back. Swing by for the new summer menu." }    ],    "postScheduledDates": {      "startDatetime": "2026-07-10T09:00:00Z",      "endDatetime": "2026-07-24T23:59:00Z"    }  }}'
JSON
{  "data": {    "createSocialPost": {      "success": true,      "errors": null,      "socialPost": {        "id": "U29jaWFsUG9zdDo5OTIwMQ==",        "name": "July Reopening Announcement",        "status": "SCHEDULED",        "type": "ANNOUNCEMENT",        "scheduledStartDate": "2026-07-10",        "scheduledEndDate": "2026-07-24",        "sites": ["GOOGLE", "FACEBOOK"],        "locationIds": [1800289, 1800290]      }    }  }}

두 SDK의 헬퍼 모두 sites의 기본값을 Google과 Facebook으로 설정하고, 사이트별 메시지 맵을 자동으로 확장하며, 숫자형 사업장 id를 인코딩하므로, 어느 SDK에서든 일괄 게시는 대개 가장 짧은 호출로 처리할 수 있습니다.

일괄 캠페인 1개 조회

GET /bulk-posts/{postId}

GET /posts/{postId}의 일괄 처리 버전입니다. 동일한 구조를 반환하며, publishedLocationsCount의 값은 1보다 크고 socialPostAnalyticsByLocationId에는 매장별 분석 내역이 포함됩니다.

매개변수

이름전달 위치필수 여부참고
postIdpath예원시 정수가 아닌 Base64 일괄 게시물 id입니다.
Get a bulk post
curl https://listingsapi.com/api/v4/bulk-posts/U29jaWFsUG9zdDo1MTA4OA== \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "socialPostView": {      "socialPostId": "U29jaWFsUG9zdDo1MTA4OA==",      "socialPostInfo": {        "name": "Festive Offer - All Stores",        "status": "SUCCESS",        "type": "OFFER",        "views": 4210,        "clicks": 318,        "sites": ["GOOGLE", "FACEBOOK"]      },      "socialPostAnalyticsByLocationId": [        {          "locationId": 1800289,          "siteStats": [            { "site": "GOOGLE", "status": "PUBLISHED", "views": 1040, "clicks": 80 },            { "site": "FACEBOOK", "status": "PUBLISHED", "views": 360, "clicks": 26 }          ]        }      ]    }  }}

socialPostAnalyticsBySite는 모든 매장의 캠페인 수치를 합산하고, socialPostAnalyticsByLocationId는 동일한 수치를 매장별로 나누어 보여 줍니다. 매장별 status 값을 살펴보면 사이트에서 실패가 발생한 사업장을 찾을 수 있습니다.

사업장의 일괄 캠페인 목록 조회

GET /locations/{locationId}/bulk-posts

data.rollupSocialPosts 아래에서 오프셋 기반 페이지네이션을 사용합니다. 사업장별 목록과 달리 이 엔드포인트는 필터와 정렬을 지원하므로 캠페인 대시보드에 적합한 엔드포인트입니다.

매개변수

이름전달 위치필수 여부참고
locationIdpath예변경 없이 전달하는 Base64 id입니다.
page, perPagequery아니요오프셋 기반 페이지네이션입니다.
filtersquery아니요JSON으로 인코딩하며, type, status, creationDateRange 또는 siteUrls를 기준으로 합니다.
sortFieldsquery아니요JSON으로 인코딩하며, 예를 들어 필드를 created_at으로, 순서를 Descending으로 지정합니다.
locationIdsquery아니요포함할 추가 사업장 id를 담은 JSON 인코딩 배열입니다.
componentIdentifierquery아니요결과를 해당 연동에서 생성한 캠페인으로 제한합니다.

정렬 가능한 필드는 name, type, status, created_at, schedule_start_date, schedule_end_date, client_name, clicks, views, shares입니다.

Bulk posts on a location
curl -G https://listingsapi.com/api/v4/locations/TG9jYXRpb246MTgwMDI4OQ==/bulk-posts \-H "Authorization: API $LISTINGSAPI_KEY" \--data-urlencode 'page=1' \--data-urlencode 'perPage=10' \--data-urlencode 'sortFields={"field":"created_at","order":"Descending"}'
JSON
{  "data": {    "rollupSocialPosts": {      "records": [        {          "id": "U29jaWFsUG9zdDo1MTA4OA==",          "name": "Festive Offer - All Stores",          "status": "SUCCESS",          "type": "OFFER",          "createdAt": "2026-06-20",          "views": 4210,          "clicks": 318        }      ],      "pageInfo": { "totalPages": 1, "hasNextPage": false, "totalRecords": 1 }    }  }}

두 SDK 모두 filters 및 sortFields 객체를 자동으로 JSON 인코딩하므로, 해당 언어의 기본 딕셔너리나 객체로 전달합니다.

발생할 수 있는 오류

코드 또는 상태의미해결 방법
$input 변수 누락을 알리는 400본문이 input으로 감싸져 있지 않습니다.모든 필드를 하나의 input 키 아래에 중첩합니다.
HTTP 400으로 반환되는 SY90016생성 또는 삭제 작업에 Read 키를 사용했습니다.Write 키를 사용합니다.
401키가 없거나 유효하지 않습니다.Authorization: API <YOUR_API_KEY>를 전송합니다.
Retry-After를 동반한 429호출이 너무 많으며, 게시물 상태를 폴링할 때 자주 발생합니다.안내된 초 수만큼 기다립니다. 요청 빈도 제한을 참조합니다.
success: false를 동반한 200유효성 검사에 실패했습니다.data.createSocialPost에서 errors를 확인합니다.
per_page must not exceed 50사업장 목록에서 perPage가 상한을 초과했습니다.50 이하로 요청합니다.
tag is not a supported parameter for this query사업장별 게시물 엔드포인트에 tag를 전송했습니다.page와 perPage만 전송합니다.

전체 목록은 오류 코드에 있습니다.

다음 단계