Skip to main content
Manage your App Store customer reviews end-to-end from a single API. Reviews are kept in sync with App Store Connect on a daily cadence and on demand, so you can search and filter them by rating, country, response state, or full-text query without paginating Apple’s API on every request.
Requires connected ASC account. Connect your App Store Connect API key and Vendor Number in appeeky.com → Settings → Integrations, or pass X-ASC-* headers per request. Reviews are synced daily and right after you connect.
Pro feature — requires Indie plan or higher.

Overview

EndpointDescription
GET /v1/connect/reviewsSearch reviews with filters (rating, territory, response state, full-text)
GET /v1/connect/reviews/summaryAggregated stats: avg rating, distribution, response rate, by-territory
POST /v1/connect/reviews/{reviewId}/responseCreate or update your reply to a review
DELETE /v1/connect/reviews/{reviewId}/responseDelete your reply to a review
POST /v1/connect/reviews/{reviewId}/refreshForce-refresh one review from Apple (e.g. right after replying)
POST /v1/connect/reviews/syncTrigger an immediate background sync of all reviews

How freshness works

  • Reviews refresh automatically every day.
  • Posting or deleting a reply writes to Apple synchronously and updates your local copy in the same request, so the next list call reflects the change immediately.
  • Use POST /v1/connect/reviews/{reviewId}/refresh to pull the latest state of a single review from Apple on demand (useful right after replying to confirm the response was published).
  • Use POST /v1/connect/reviews/sync to trigger a full background refresh of every review.

Search Reviews

GET /v1/connect/reviews
Returns reviews sorted by newest first. All filter parameters are optional and combinable — for example, “all unanswered 1-2 star reviews from the US that mention ‘crash’” is a single request.

Query parameters

NameTypeDefaultDescription
appIdstringFilter by App Store Connect app ID. Omit to search across all your connected apps.
ratingnumberFilter by exact star rating (1–5).
territorystringISO 3-letter country code (e.g. USA, GBR, DEU).
hasResponsebooleantrue for answered reviews only, false for unanswered only. Omit for both.
qstringCase-insensitive substring match in review title or body.
limitnumber50Max results (1–200).
beforestringISO timestamp cursor — returns reviews created strictly before this date (use the oldest review_created_at from the previous page).
# Triage: unanswered 1-star reviews mentioning "crash"
curl "https://api.appeeky.com/v1/connect/reviews?rating=1&hasResponse=false&q=crash" \
  -H "X-API-Key: apk_your_key_here"
Response (200 OK):
{
  "data": {
    "reviews": [
      {
        "id": "00000000-0000-0000-0000-000000000abc",
        "app_apple_id": "6759740679",
        "rating": 1,
        "title": "Crashes on launch",
        "body": "Latest update crashes immediately when I open the app...",
        "reviewer_nickname": "MarcusJ",
        "territory": "USA",
        "app_version": "2.4.1",
        "review_created_at": "2026-04-22T14:18:00Z",
        "response_id": null,
        "response_body": null,
        "response_state": null,
        "response_last_modified": null
      }
    ],
    "count": 1
  }
}
FieldDescription
idApple review ID — pass this to the response and refresh endpoints.
rating1–5 stars.
title / bodyReview text.
reviewer_nicknameAnonymous nickname chosen by the reviewer.
territoryISO 3-letter country code.
app_versionApp version the user reviewed.
review_created_atISO timestamp the user submitted the review.
response_idnull if you have not replied yet.
response_bodyYour last reply text (or null).
response_statePUBLISHED, PENDING_PUBLISH, or null.
response_last_modifiedISO timestamp of your last reply update.

Pagination

There is no next cursor in the response — pagination uses the before parameter. To get the next page:
const lastReviewDate = data.reviews[data.reviews.length - 1].review_created_at;
const next = await fetch(
  `https://api.appeeky.com/v1/connect/reviews?limit=50&before=${encodeURIComponent(lastReviewDate)}`,
  { headers: { "X-API-Key": process.env.APEEKY_KEY } }
);

Reviews Summary

GET /v1/connect/reviews/summary
Aggregated statistics for an app (or all your apps). Useful for dashboards and quick health snapshots.

Query parameters

NameTypeDefaultDescription
appIdstringFilter by app. Omit to roll up across all your apps.
fromstringISO date — only consider reviews created on/after this.
tostringISO date — only consider reviews created on/before this.
curl "https://api.appeeky.com/v1/connect/reviews/summary?appId=6759740679" \
  -H "X-API-Key: apk_your_key_here"
Response (200 OK):
{
  "data": {
    "appId": "6759740679",
    "from": null,
    "to": null,
    "total": 482,
    "avg_rating": 4.32,
    "unanswered": 67,
    "response_rate": 0.8610,
    "distribution": {
      "1": 22,
      "2": 14,
      "3": 38,
      "4": 110,
      "5": 298
    },
    "territories": [
      { "territory": "USA", "count": 219, "avg_rating": 4.41 },
      { "territory": "GBR", "count": 58,  "avg_rating": 4.22 },
      { "territory": "DEU", "count": 41,  "avg_rating": 4.05 }
    ]
  }
}
FieldDescription
totalTotal review count in the window.
avg_ratingAverage star rating (1–5).
unansweredReviews with no developer response.
response_rate(total − unanswered) / total, rounded to 4 decimals.
distributionCount per star rating.
territoriesUp to 50 territories sorted by review volume, each with its own avg rating.

Respond to a Review

POST /v1/connect/reviews/{reviewId}/response
Create or update your developer response. Apple does not allow editing a response in place — under the hood, this endpoint deletes the existing response and creates a new one when needed. You always make a single API call.

Path parameters

NameDescription
reviewIdApple review ID (id from the search response).

Request body

{
  "responseBody": "Thanks for the report — this is fixed in 2.4.2 (rolling out today). Please email support@example.com if you still see the issue."
}
FieldTypeRequiredDescription
responseBodystringYesYour reply text. Non-empty, max 5,970 characters (Apple’s hard limit).
curl -X POST "https://api.appeeky.com/v1/connect/reviews/00000000-0000-0000-0000-000000000abc/response" \
  -H "X-API-Key: apk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"responseBody":"Thanks for the report — fixed in 2.4.2."}'
Response (200 OK):
{
  "data": {
    "reviewId": "00000000-0000-0000-0000-000000000abc",
    "response": {
      "response_id": "11111111-1111-1111-1111-111111111def",
      "response_body": "Thanks for the report — fixed in 2.4.2.",
      "response_state": "PENDING_PUBLISH",
      "response_last_modified": "2026-04-25T22:01:09Z"
    }
  }
}
Apple typically returns PENDING_PUBLISH initially and transitions to PUBLISHED once moderation completes (usually within minutes). Call the refresh endpoint below to see the published state without waiting for the next daily sync.

Delete a Response

DELETE /v1/connect/reviews/{reviewId}/response
Removes your developer response. The review itself remains; only your reply is deleted. Idempotent — calling it on a review with no response returns 200 with response: null.
curl -X DELETE "https://api.appeeky.com/v1/connect/reviews/00000000-0000-0000-0000-000000000abc/response" \
  -H "X-API-Key: apk_your_key_here"
Response (200 OK):
{
  "data": {
    "reviewId": "00000000-0000-0000-0000-000000000abc",
    "response": null
  }
}

Refresh One Review

POST /v1/connect/reviews/{reviewId}/refresh
Pulls the latest state of a single review directly from Apple. Use this right after posting a response to confirm the PUBLISHED transition, or any time you suspect a review is stale.
curl -X POST "https://api.appeeky.com/v1/connect/reviews/00000000-0000-0000-0000-000000000abc/refresh" \
  -H "X-API-Key: apk_your_key_here"
Response (200 OK): the same review shape returned by GET /v1/connect/reviews.

Trigger Background Sync

POST /v1/connect/reviews/sync
Enqueues a background job that pulls every new review since the last sync (or all reviews if mode=full). Useful when you want fresh data on demand without waiting for the daily refresh. Returns a syncRunId you can use to track the job.

Query parameters

NameTypeDefaultDescription
modestringincrementalincremental for new reviews only, full for a complete backfill.
curl -X POST "https://api.appeeky.com/v1/connect/reviews/sync?mode=incremental" \
  -H "X-API-Key: apk_your_key_here"
Response (200 OK):
{
  "data": {
    "syncRunId": "run_abc123",
    "mode": "incremental"
  }
}
Sync requests are deduplicated — clicking sync twice within 30 minutes returns the same syncRunId.

Common Workflow: Triage Low-Rated Reviews

JavaScript
// 1. Find unanswered low-rated reviews
const params = new URLSearchParams({
  rating: "1",
  hasResponse: "false",
  limit: "50",
});
const res = await fetch(
  `https://api.appeeky.com/v1/connect/reviews?${params}`,
  { headers: { "X-API-Key": process.env.APEEKY_KEY } }
);
const { data } = await res.json();

// 2. Reply to each one
for (const review of data.reviews) {
  await fetch(
    `https://api.appeeky.com/v1/connect/reviews/${review.id}/response`,
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.APEEKY_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        responseBody:
          "We're sorry to hear that. Please email support@example.com so we can help.",
      }),
    }
  );
}

// 3. Confirm by checking the summary
const summary = await fetch(
  "https://api.appeeky.com/v1/connect/reviews/summary",
  { headers: { "X-API-Key": process.env.APEEKY_KEY } }
).then((r) => r.json());

console.log(`${summary.data.unanswered} unanswered reviews remain`);

MCP Tools

These endpoints are also exposed as MCP tools so AI assistants can triage reviews on your behalf:
ToolDescription
asc_search_reviewsSearch reviews with filters (rating, territory, hasResponse, full-text)
asc_reviews_summaryAggregated stats and response rate
asc_respond_to_reviewPost or update a developer response
asc_delete_review_responseDelete a developer response
asc_refresh_reviewRefresh a single review from Apple
Example prompt:
Find every unanswered 1- or 2-star review for app 6759740679 from the last
two weeks that mentions "crash" or "bug", and reply with an empathetic
message that points to support@example.com.

Error Codes

StatusCodeWhen
400INVALID_REVIEW_IDMissing reviewId path parameter
400INVALID_RESPONSE_BODYEmpty responseBody
400RESPONSE_BODY_TOO_LONGReply exceeds Apple’s 5,970-character limit
400ASC_CREDENTIALS_REQUIREDNo App Store Connect credentials connected for your account
401INVALID_API_KEYInvalid or inactive API key
403PRO_FEATUREFree plan — upgrade to Indie or higher
404REVIEW_NOT_FOUNDReview does not exist or is not owned by your account
500REVIEWS_FETCH_FAILEDDatabase error
500REVIEWS_SYNC_ENQUEUE_FAILEDCould not enqueue sync job

Legacy Endpoints

The original live-passthrough endpoints remain available for backwards compatibility:
MethodPathNotes
GET/v1/connect/apps/{appId}/customer-reviewsLive paginated read from Apple. No filters beyond pagination.
POST/v1/connect/customer-reviews/responseBody: { reviewId, responseBody }. Does not handle response edits.
For all new integrations, prefer the endpoints above — they are faster, support filtering and aggregation, transparently handle response edits, and keep your local view in sync with Apple.