Publish the package you actually reviewed
Send reviewed text and images to Google Play. Choose the app, languages and assets, submit them once, then check the upload result and the store’s review status.
Studio supports direct publishing for supported formats on an eligible plan. Connect the intended store account in the App profile first. A Developer API generation key is not a store credential. The API workflow below is Google Play only; use Studio for supported Apple publishing workflows.
1. Set up the publishing permission
Create a separate key in Studio with Allow publishing to connected stores. It needs the publish scope; existing generate-only keys stay unchanged. Connect the app’s Google Play account in Studio and check that your plan includes publishing before starting.
2. Choose the text and assets to upload
Persist a new Idempotency-Key of 20–200 safe identifier characters once before session creation. Choose 1–10 locales, parts from aso, screenshots, feature-graphic, explicit replaceImages, a reviewPolicy and exact total imageCount. If ASO is selected, supply all three Google fields for every locale.
/developer/publishing/google/sessions{
"appId": "665f0a2b9c1e4d0012ab34cd",
"locales": [
"en-GB"
],
"parts": [
"aso"
],
"replaceImages": false,
"reviewPolicy": "submit",
"imageCount": 0,
"metadata": [
{
"locale": "en-GB",
"title": "Your App",
"shortDescription": "A concise, factual benefit statement.",
"fullDescription": "Your exact reviewed description."
}
]
}The response contains sessionId. A replay with the same key/body recovers the same operation; changing the request under that key returns 409. The exact text is snapshotted and cannot drift with later Studio edits.
3. Stage selected images
For each selected locale, stage 2–8 phone screenshots in contiguous positions starting at 0, and exactly one feature graphic at position 0 when that part is selected. Use owned, unexpired Developer artifact IDs from the same App profile. Total images across all locales must match imageCount. For text-only requests skip staging.
/developer/publishing/sessions/{id}/artifacts{
"artifactId": "66a1f0c2b7e4d90012cd5678",
"locale": "en-US",
"target": "phoneScreenshots",
"position": 0
}Use target: "featureGraphic" for the graphic. Staging the same artifact in the same position is retry-safe; a different artifact in an occupied position returns 409. Icon upload uses the separate existing-icon operation below when it is available.
Reuse your existing app icon
Find the current icon in your app repository or brand assets before asking an assistant to create one. Prefer the original artwork over a small launcher bitmap. For Google Play, prepare a 512 × 512 PNG up to 1 MiB; alpha is preserved. Keep the intended background and do not add a rounded store mask. App Store icons come from the app build.
Include icon in session parts and count one icon per selected locale in imageCount. An icon-only session uses parts: ["icon"] with no metadata. Upload the existing file after creating the session:
/developer/publishing/sessions/{id}/iconimport { readFile } from "node:fs/promises";
const png = await readFile("./assets/google-play-icon-512.png");
const response = await fetch(`https://api.applistinglab.com/api/v1/developer/publishing/sessions/${sessionId}/icon`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.APP_LISTING_LAB_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ locale: "en-US", dataUrl: "data:image/png;base64," + png.toString("base64") }),
});
if (!response.ok) throw new Error(`Icon staging HTTP ${response.status}`);
const { staged, sha256 } = await response.json();
// Persist sha256 with the manifest; no store write has occurred yet.Position is always zero. Identical normalized bytes recover the same slot; a different icon conflicts. Uploading an icon replaces that locale’s single icon slot without clearing screenshots or other image types. After the session is confirmed done, compare the returned hash with icon readback:
/developer/publishing/google/apps/{id}/icon?locale=en-USReadback returns available and a sha256 array from Google Play. A confirmed commit with a mismatching hash needs reconciliation, not a new publication. API icon upload reuses existing artwork; it does not expose Icon Studio generation.
4. Start once, then follow the session
/developer/publishing/sessions/{id}/start/developer/publishing/sessions/{id}Start sends an empty JSON object. Poll this publishing session, whose status can be staging, claimed, running, done, partial, error, uncertain or cleaning. The safe result includes operation/run IDs, staged and expected counts, and confirmed field/image counts per locale. An uncertain outcome requires checking the original session and Google Play before trying again. If the response includes failure, follow its error message and recovery action.
5. Read back the exact text
/developer/publishing/google/apps/{id}/listing?locale=en-GBCompare title, short description and full description with the original request. A done result confirms Google Play accepted the changes. Check the review status in Play Console to see when they can appear publicly.
Review policy and safe recovery
manual asks Play to hold eligible changes and can be refused by Google. submit sends changes for review; Google may publish approved changes unless managed publishing is enabled. Existing reviews are not replaced. Do not silently switch policy to make a request succeed.
Keep the same session/key when recovering network failures. Before a deliberately new attempt after a definite refusal, reconcile the original operation and read back the store. Never retry an uncertain operation with a fresh identity. See Errors & usage.