The App Store Connect Reviews API: A Complete 2026 Reference
Read reviews and post replies programmatically: JWT ES256 auth, the customerReviews and customerReviewResponses endpoints, the 7-day window, and the gotchas.
The Argus Team
Reply Argus
The App Store Connect Reviews API lets you read your App Store reviews and post developer replies with two REST endpoints — `GET /v1/apps/{id}/customerReviews` to pull the reviews and `POST /v1/customerReviewResponses` to answer them — authenticated with a short-lived ES256 JWT you sign yourself. There's no OAuth dance, no webhook for new reviews, and one limitation that surprises everyone: the reviews endpoint only returns roughly the last seven days.
This is the App Store side of the story. Google Play does the same job with a completely different mechanism (a service-account OAuth token against the `reviews` resource), and we compare the two review APIs head to head in [App Store vs Google Play review replies](/blog/app-store-vs-google-play-review-replies). Here we go deep on Apple's: how the auth actually works, the exact JSON:API envelope you'll parse, the request bodies you'll send, and the four gotchas that cost people an afternoon each.
How do you authenticate to the App Store Connect API?
Apple doesn't hand you a long-lived API key. Instead you generate an API key in App Store Connect (Users and Access → Integrations → App Store Connect API), download the private key as a `.p8` file exactly once — Apple never shows it again — and use it to sign a fresh JSON Web Token for each burst of requests. Three values come out of that screen and you'll need all three: the Key ID (`kid`), the Issuer ID (`iss`), and the `.p8` private key itself.
The token must be signed with ES256 (ECDSA using P-256 and SHA-256) — Apple rejects anything else — and it has a hard ceiling of 20 minutes. Set `exp` to more than 1200 seconds past `iat` and every call comes back 401. The audience is always the literal string `appstoreconnect-v1`. Here's the anatomy:
// JWT header
{
"alg": "ES256",
"kid": "2X9R4HXF34", // your Key ID
"typ": "JWT"
}
// JWT payload
{
"iss": "57246542-96fe-1a63-e053-0824d011072a", // Issuer ID
"iat": 1751961600,
"exp": 1751962800, // iat + 1200s — 20 min is the hard max
"aud": "appstoreconnect-v1"
}In practice you never assemble that by hand. Any JWT library that supports ES256 does it in a few lines — here it is in Node with a signed token you can drop straight into an `Authorization: Bearer` header:
import jwt from "jsonwebtoken";
import { readFileSync } from "node:fs";
const privateKey = readFileSync("./AuthKey_2X9R4HXF34.p8");
const token = jwt.sign({}, privateKey, {
algorithm: "ES256",
keyid: "2X9R4HXF34", // Key ID
issuer: "57246542-96fe-1a63-e053-0824d011072a", // Issuer ID
audience: "appstoreconnect-v1",
expiresIn: "20m", // never longer
});
// Reuse this token for ~20 minutes, then mint a new one.Key roles matter for posting replies
A key scoped to a limited role can read reviews but 403 on `POST /v1/customerReviewResponses`. Apple requires the Account Holder, Admin, or Customer Support role to publish a response, and developers regularly report that Team-level keys refuse to create responses at all — you may need an individually-scoped key. If reading works but replying returns 403 Forbidden, the key's role is almost always why.
How do you read reviews with the customerReviews endpoint?
Reviews hang off the app resource. You need the app's numeric ID (the Apple ID from App Store Connect, e.g. `1234567890`), then you hit its `customerReviews` relationship. The API speaks JSON:API, so responses arrive as a `data` array of typed resources, each with an `id`, an `attributes` object, and `relationships` — plus a top-level `links` block for cursor pagination.
A typical read, sorted newest-first, capped at the max page size of 200, with the developer response side-loaded:
GET /v1/apps/1234567890/customerReviews
?sort=-createdDate
&limit=200
&include=response
&filter[rating]=1,2 HTTP/1.1
Host: api.appstoreconnect.apple.com
Authorization: Bearer <your-signed-jwt>What comes back is the JSON:API envelope. Each review carries the fields you'd expect, and the pagination cursor lives in `links.next` — follow it until the key disappears:
{
"data": [
{
"type": "customerReviews",
"id": "00000000-1111-2222-3333-444444444444",
"attributes": {
"rating": 2,
"title": "Keeps logging me out",
"body": "Since 4.2 I get signed out every single launch.",
"reviewerNickname": "gridrunner",
"createdDate": "2026-07-03T09:14:00-07:00",
"territory": "USA"
},
"relationships": {
"response": { "links": { "related": "…/customerReviews/0000…/response" } }
}
}
],
"links": {
"next": "https://api.appstoreconnect.apple.com/v1/apps/1234567890/customerReviews?cursor=BQ.oefX…"
}
}- `rating` — the integer 1–5. Filter on it server-side with `filter[rating]` so you're not paging through 5-star praise to find the fires.
- `title` / `body` — the review headline and text. A rating with *no written text* never appears here, so counts from this endpoint won't match your public star average.
- `reviewerNickname` — the display name. Apple gives you no stable user identifier, no email, nothing to contact the person outside the store.
- `createdDate` / `territory` — when and where it was posted. `territory` is a three-letter code (`USA`, `JPN`, `DEU`); one call spans every storefront, and you read the territory off each review rather than querying country by country.
Why is the App Store Connect reviews API only returning recent reviews?
Because it's designed to. The `customerReviews` endpoint returns roughly the last seven days of reviews — reviews older than about a week simply aren't in the response, no matter how you sort or paginate. This isn't documented as a hard rule by Apple, but it's the consistent experience across the developer community, and it's the single biggest thing that trips teams building against this API. If you want a durable history, you have to poll on a schedule and store every review as you see it. Miss a week and that week is gone.
That polling requirement is also why there's no push notification for new App Store reviews. The API gives you no webhook, so "tell me when a 1-star lands" is something you build on top yourself: pull on a cron, diff against what you stored last run, and fire your own alert. There's no shortcut in the API for it.
The gotchas, collected
Four things to design around before you write a line: (1) the ~7-day window — poll and persist or lose history; (2) ratings-only reviews with no text don't come through the API at all, so your counts undercount; (3) there's no endpoint that returns your current star average — compute it yourself from what you store; (4) tokens die at 20 minutes, so long batch jobs must re-sign mid-run.
How do you post a reply with customerReviewResponses?
Replying is a single `POST /v1/customerReviewResponses`. The body is a JSON:API resource with one attribute — `responseBody`, the text of your reply — and a `review` relationship pointing at the review you're answering. There's no separate create-vs-update: posting again to the same review replaces your existing response.
POST /v1/customerReviewResponses
Authorization: Bearer <your-signed-jwt>
Content-Type: application/json
{
"data": {
"type": "customerReviewResponses",
"attributes": {
"responseBody": "Getting logged out every launch is not okay…"
},
"relationships": {
"review": {
"data": {
"type": "customerReviews",
"id": "00000000-1111-2222-3333-444444444444"
}
}
}
}
}The response you get back has a `state` attribute that starts at `PENDING_PUBLISH` and flips to `PUBLISHED` once Apple pushes it live, usually within a day (Apple doesn't publish a guaranteed timeframe). Don't treat a 201 as "the reviewer can see it now." Poll the response resource, or just accept the lag, but tell your users the reply is queued, not posted.
One length note worth building in: Apple publishes no official character limit for `responseBody`, and community testing suggests you can fit a few thousand characters. That's very different from Google Play, which caps developer replies at a hard 350 characters (per Google's Play Console documentation). The [reply rules differ enough per store](/blog/reply-rules-for-every-app-store) that it's worth codifying them once: size the App Store draft generously, but hold the Play draft to 350 or the write fails.
The API is the easy part. The reply itself is the work — and a good one, grounded in the actual bug and offering a real next step, is what moves the needle. Google's own I/O 2019 data showed apps that respond to reviews gain about +0.7 stars on average. Here's the kind of reply that earns it:
Since 4.2 I get signed out every single launch. Re-entered my password six times today. Unusable.
Getting logged out every launch is not okay, and I'm sorry 4.2 did that to you. It's a confirmed session bug and the fix is already in review. Until it ships, backgrounding the app instead of force-quitting keeps you signed in — and if you email support@app.com I'll flag your account the moment the patch is live. — Sam
The build-vs-buy line
Everything above is a weekend to prototype and a quarter to harden. The 7-day window means a durable review store and a reliable cron. The 20-minute tokens mean a signing layer with refresh. Then you want the same thing for Google Play's entirely different OAuth-and-service-account flow, then per-territory language handling, then a way to actually write good replies at volume without a human retyping them all day. That last part is the real cost — the API just moves text; it doesn't know your product or your voice.
That's the gap [ReplyArgus](/features) fills. It runs both APIs for you — the ES256 token rotation, the polling, the 7-day persistence, the JSON:API parsing, the 350-versus-unlimited sizing — and drafts each reply grounded in your knowledge base and past approved replies, in the reviewer's own language across 100+ languages. You approve; it posts. Nothing goes live unattended unless you set an explicit rule. If you're weighing that, our take on [auto-publishing review replies](/blog/is-it-safe-to-auto-publish-app-review-replies) is honest about where to draw the line.
And if you already live in an AI assistant, you can skip the client code entirely. ReplyArgus ships an [MCP connector](/agentic-tools), so the whole read-draft-reply loop becomes a sentence to Claude, ChatGPT, or Cursor — "pull my unanswered 1-stars from the last week and draft replies" — instead of a JWT signer and a pagination loop you maintain forever.
Or one MCP call instead of a client
Skip the ES256 signing, the 7-day polling, and the JSON:API parsing. Connect your store to ReplyArgus once and drive reviews from your agent — or the dashboard. Free plan, one app, no card: [start free](/signup).
Frequently asked
- What are the App Store Connect reviews API endpoints?
- Two main ones. `GET /v1/apps/{id}/customerReviews` lists an app's reviews (with a `customerReviews/{id}` variant for a single review), and `POST /v1/customerReviewResponses` creates or replaces your developer reply. You can also `GET` and `DELETE` an existing response by its id.
- How do I authenticate with the App Store Connect API?
- You sign a short-lived JSON Web Token yourself. Generate an API key in App Store Connect, download the `.p8` private key, then sign a JWT with the ES256 algorithm using your Key ID and Issuer ID, with the audience set to `appstoreconnect-v1`. The token can't live longer than 20 minutes, and you send it as a Bearer token.
- Why isn't the customerReviews endpoint returning older reviews?
- The endpoint only returns roughly the last seven days of reviews — older ones aren't included regardless of sorting or pagination. It's a widely-reported limitation, not an official documented rule. To keep history, poll on a schedule and store each review as you see it.
- Is there a character limit on App Store review replies?
- Apple publishes no official character limit for the `responseBody` field, and community testing suggests you can fit a few thousand characters. Google Play is different: its developer replies are capped at a hard 350 characters. If your code targets both, size each draft to its store.
- How long until my API-posted reply appears on the App Store?
- Usually within a day, though Apple doesn't publish a guaranteed timeframe. A newly created response returns with a `state` of `PENDING_PUBLISH` and flips to `PUBLISHED` once Apple pushes it live, so a successful POST doesn't mean the reviewer can see it yet.
- Why do I get a 403 when posting a review response?
- Almost always the API key's role. Creating a response requires the Account Holder, Admin, or Customer Support role, and developers frequently report that Team-scoped keys can't create responses at all — reading works, posting 403s. Use an appropriately-scoped key.
Try it
Let Argus draft your next reply.
Watch it answer a real review in your voice. 10-day trial, no card to begin.
Keep reading
App Store Reviews Not Showing? The Full Fix List for Apple and Google Play
App Store or Google Play reviews not showing? Here's every real cause — moderation delay, wrong country storefront, filtering, rating lag, cache — and the fix.
Read moreAre App Store Reviews Fake? How to Spot Them and What to Do
Yes, some App Store and Google Play reviews are fake — but fewer survive than you think. How to spot them, and what to do when they hit your app.
Read more